Skip to content

Update & refactor dark mode#4114

Merged
evanpelle merged 1 commit into
mainfrom
dark-mode
Jun 2, 2026
Merged

Update & refactor dark mode#4114
evanpelle merged 1 commit into
mainfrom
dark-mode

Conversation

@evanpelle
Copy link
Copy Markdown
Collaborator

@evanpelle evanpelle commented Jun 2, 2026

Description:

  • The renderer no longer knows what "dark mode" is. RenderSettings.dayNight.mode ("light" | "dark") is gone — passes read neutral values (lighting.ambient: number, lighting.enabled: boolean).
  • render-settings.json holds the light-mode baseline. Dark mode is just another override layer, applied the same way as graphics settings (darkNames, classicIcons, etc.).
  • New src/client/render/gl/RenderOverrides.ts exposes two in-place mutators with matching shapes:
    • applyGraphicsOverrides(settings, overrides) — replaces the old generateRenderSettings
    • applyDarkModeOverride(settings, isDark)
  • ClientGameRunner regenerates the live settings each time the user setting changes via deepAssign(live, createRenderSettings()) + the override chain. No per-slice copy list, no intermediate object — adding a new override that touches a new section just works.
  • Renamed dayNightlighting; collapsed nightAmbient/dayAmbient into single ambient; renamed enableLightCompositingenabled.
  • Bumped dark-mode ambient from 0.15 → 0.35 so terrain stays readable.
Screenshot 2026-06-02 at 11 47 28 AM

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory

Please put your Discord username so you can be contacted if a bug or regression is found:

evan

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

Walkthrough

The PR refactors WebGL render settings from a mode-driven day/night model to a direct lighting control model. The dayNight object is replaced with lighting (ambient and enabled fields); override logic is moved into dedicated helper functions; renderer and render passes update to use the new structure; and ClientGameRunner consolidates regeneration into a single path with unified listeners.

Changes

Lighting Settings Refactor

Layer / File(s) Summary
Lighting Settings Schema Redesign
src/client/render/gl/RenderSettings.ts
RenderSettings interface replaces dayNight with lighting: { ambient: number; enabled: boolean }. Exported generateRenderSettings() function is removed; GraphicsOverrides import is removed since override application moves to dedicated helpers.
Override Helper Functions
src/client/render/gl/RenderOverrides.ts, src/client/render/gl/index.ts
New RenderOverrides.ts exports applyGraphicsOverrides and applyDarkModeOverride to mutate RenderSettings. Barrel exports surface the new helpers and remove generateRenderSettings.
Renderer Compositing Control
src/client/render/gl/Renderer.ts
renderFrame() switches rendering path (scene + lightmap + composite vs. direct base layer) based on isLightCompositingActive(), which reads settings.lighting.enabled instead of checking day/night mode.
Render Passes Reading Lighting Settings
src/client/render/gl/passes/*.ts
FalloutLightPass, LightmapPass, NightCompositePass, and PointLightPass all update settings reads from dayNight to lighting; NightCompositePass no longer conditionally selects day vs. night ambient.
Consolidated Settings Regeneration in ClientGameRunner
src/client/ClientGameRunner.ts
Imports applyDarkModeOverride; establishes territory-pattern visibility wiring; consolidates render-settings updates into a single regenerateRenderSettings function called by global listeners for graphics overrides and dark mode, replacing prior separate handlers.
Debug UI, Config File, and Test Updates
src/client/render/gl/debug/Layout.ts, src/client/render/gl/render-settings.json, tests/GraphicsOverrides.test.ts
Debug Layout replaces "Day / Night" folder with "Lighting" folder; config file replaces dayNight with lighting: { ambient: 1, enabled: false }; tests refactored to use local gen() helper building defaults and applying overrides, with assertions checking lighting instead of dayNight.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • openfrontio/OpenFrontIO#4065: Adds user-settings-driven listener and name-label tuning via generateRenderSettings that feeds graphics overrides into ClientGameRunner; this PR refactors that override pipeline.
  • openfrontio/OpenFrontIO#3966: Modifies ClientGameRunner.ts territory-pattern rendering wiring with view.setShowPatterns and its listener; this PR consolidates that into the render-settings regeneration flow.

Suggested labels

UI/UX

Suggested reviewers

  • Celant

Poem

The day/night divide fades to grey,

Ambient light now holds its sway,

No mode to pick, just soft and on,

The render path flows unified—begun! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title "Update & refactor dark mode" accurately describes the main change: a comprehensive refactoring of the dark mode system, though it doesn't capture that this involves restructuring render settings architecture.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly explains the refactoring: renaming dayNight to lighting, implementing new RenderOverrides helpers, and restructuring how dark mode is applied.

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


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.

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

🧹 Nitpick comments (1)
src/client/render/gl/DarkModeOverride.ts (1)

1-12: ⚡ Quick win

Move DARK_AMBIENT into render-settings.json.

The logic is correct and easy to read. One small thing: 0.35 is a lighting tuning value, so it fits better next to the other lighting knobs in the JSON config rather than as a hardcoded constant here. You could read it from the passed settings or a dedicated json field.

As per coding guidelines: "All per-pass tuning constants (alpha, radii, colors, etc.) should be defined in gl/render-settings.json".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/render/gl/DarkModeOverride.ts` around lines 1 - 12, The hardcoded
DARK_AMBIENT constant should be moved into the render-settings JSON and read
from the RenderSettings object; update the RenderSettings typing (e.g. add
lighting.darkAmbient or lighting.overrideAmbient) and the JSON default in
gl/render-settings.json, then change applyDarkModeOverride to use
settings.lighting.darkAmbient instead of DARK_AMBIENT and keep the existing
logic (only apply when isDark is true and enable lighting). This ensures the
per-pass tuning value is configurable while leaving applyDarkModeOverride,
settings.lighting.ambient and settings.lighting.enabled intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/ClientGameRunner.ts`:
- Around line 479-482: The global listener added in ClientGameRunner using
globalThis.addEventListener(`${USER_SETTINGS_CHANGED_EVENT}:settings.territoryPatterns`,
...) is not bound to graphicsListenerAbort.signal so it can outlive stop() and
hold a stale view; update the addEventListener call that wraps
view.setShowPatterns to pass the same abort signal
(graphicsListenerAbort.signal) as an option, or alternatively remove that
listener inside stop(), ensuring the handler uses the same
graphicsListenerAbort.signal as other graphics listeners to avoid leaking the
view reference.

---

Nitpick comments:
In `@src/client/render/gl/DarkModeOverride.ts`:
- Around line 1-12: The hardcoded DARK_AMBIENT constant should be moved into the
render-settings JSON and read from the RenderSettings object; update the
RenderSettings typing (e.g. add lighting.darkAmbient or
lighting.overrideAmbient) and the JSON default in gl/render-settings.json, then
change applyDarkModeOverride to use settings.lighting.darkAmbient instead of
DARK_AMBIENT and keep the existing logic (only apply when isDark is true and
enable lighting). This ensures the per-pass tuning value is configurable while
leaving applyDarkModeOverride, settings.lighting.ambient and
settings.lighting.enabled intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2aed21bb-85d0-4ff0-ad2e-8f1edc22ece9

📥 Commits

Reviewing files that changed from the base of the PR and between 2386b4b and 0d7ec18.

📒 Files selected for processing (12)
  • src/client/ClientGameRunner.ts
  • src/client/render/gl/DarkModeOverride.ts
  • src/client/render/gl/RenderSettings.ts
  • src/client/render/gl/Renderer.ts
  • src/client/render/gl/debug/Layout.ts
  • src/client/render/gl/index.ts
  • src/client/render/gl/passes/FalloutLightPass.ts
  • src/client/render/gl/passes/LightmapPass.ts
  • src/client/render/gl/passes/NightCompositePass.ts
  • src/client/render/gl/passes/PointLightPass.ts
  • src/client/render/gl/render-settings.json
  • tests/GraphicsOverrides.test.ts

Comment on lines 479 to 482
globalThis.addEventListener(
`${USER_SETTINGS_CHANGED_EVENT}:settings.territoryPatterns`,
(e) => view.setShowPatterns((e as CustomEvent<string>).detail === "true"),
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check where graphicsListenerAbort is created/aborted and whether the
# territoryPatterns listener is removed anywhere.
rg -nP -C3 'graphicsListenerAbort|territoryPatterns|setShowPatterns' src/client/ClientGameRunner.ts

Repository: openfrontio/OpenFrontIO

Length of output: 1702


Fix: clean up the territoryPatterns global listener in ClientGameRunner
src/client/ClientGameRunner.ts lines 479-482 add the globalThis.addEventListener(...:settings.territoryPatterns, ...) handler without graphicsListenerAbort.signal, while stop() only aborts graphicsListenerAbort. So this handler can outlive stop() and keep a stale view reference. Bind it to the same abort signal (or remove the listener in stop()).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/ClientGameRunner.ts` around lines 479 - 482, The global listener
added in ClientGameRunner using
globalThis.addEventListener(`${USER_SETTINGS_CHANGED_EVENT}:settings.territoryPatterns`,
...) is not bound to graphicsListenerAbort.signal so it can outlive stop() and
hold a stale view; update the addEventListener call that wraps
view.setShowPatterns to pass the same abort signal
(graphicsListenerAbort.signal) as an option, or alternatively remove that
listener inside stop(), ensuring the handler uses the same
graphicsListenerAbort.signal as other graphics listeners to avoid leaking the
view reference.

@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management Jun 2, 2026
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.

🧹 Nitpick comments (1)
src/client/render/gl/passes/NightCompositePass.ts (1)

2-6: ⚡ Quick win

Update comments to reflect the lighting model.

The header comments reference "day/night" terminology, but the implementation now uses the lighting model (ambient + enabled). Consider updating to avoid confusion.

📝 Suggested comment updates
 /**
- * NightCompositePass — scene capture + day/night composite.
+ * NightCompositePass — scene capture + lighting composite.
  *
  * Owns the scene capture FBO: terrain + territory render into it when
- * day/night is enabled. Composites the captured scene with a blurred
+ * lighting is enabled. Composites the captured scene with a blurred
  * lightmap: output = scene * min(ambient + lightmap, 1.2).
  *
- * At full daytime (ambient ≈ 1.0) the composite is a visual identity —
+ * At full ambient (≈ 1.0) the composite is a visual identity —
  * multiplication by ~1.0 — so the pass runs continuously with no threshold.
  */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/render/gl/passes/NightCompositePass.ts` around lines 2 - 6, Update
the header comment for NightCompositePass to stop using "day/night" wording and
instead describe the current lighting model; mention that this pass owns the
scene-capture FBO and that the composite mixes the captured scene with the
blurred lightmap using the lighting model (e.g., output = scene * min(ambient +
enabledLighting, 1.2) or similar terminology used in the implementation), so the
comment matches the actual symbols and formula used by NightCompositePass.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/client/render/gl/passes/NightCompositePass.ts`:
- Around line 2-6: Update the header comment for NightCompositePass to stop
using "day/night" wording and instead describe the current lighting model;
mention that this pass owns the scene-capture FBO and that the composite mixes
the captured scene with the blurred lightmap using the lighting model (e.g.,
output = scene * min(ambient + enabledLighting, 1.2) or similar terminology used
in the implementation), so the comment matches the actual symbols and formula
used by NightCompositePass.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b0f164d-edd5-49f5-8d2d-29c57e38cc83

📥 Commits

Reviewing files that changed from the base of the PR and between 0d7ec18 and 0ef58dd.

📒 Files selected for processing (12)
  • src/client/ClientGameRunner.ts
  • src/client/render/gl/RenderOverrides.ts
  • src/client/render/gl/RenderSettings.ts
  • src/client/render/gl/Renderer.ts
  • src/client/render/gl/debug/Layout.ts
  • src/client/render/gl/index.ts
  • src/client/render/gl/passes/FalloutLightPass.ts
  • src/client/render/gl/passes/LightmapPass.ts
  • src/client/render/gl/passes/NightCompositePass.ts
  • src/client/render/gl/passes/PointLightPass.ts
  • src/client/render/gl/render-settings.json
  • tests/GraphicsOverrides.test.ts
✅ Files skipped from review due to trivial changes (2)
  • src/client/render/gl/render-settings.json
  • src/client/render/gl/passes/PointLightPass.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/client/render/gl/passes/FalloutLightPass.ts
  • src/client/render/gl/passes/LightmapPass.ts
  • src/client/render/gl/debug/Layout.ts
  • src/client/ClientGameRunner.ts
  • src/client/render/gl/Renderer.ts

@evanpelle evanpelle added this to the v32 milestone Jun 2, 2026
@evanpelle evanpelle marked this pull request as ready for review June 2, 2026 18:48
@evanpelle evanpelle requested a review from a team as a code owner June 2, 2026 18:48
@evanpelle evanpelle merged commit f1045a2 into main Jun 2, 2026
16 of 26 checks passed
@evanpelle evanpelle deleted the dark-mode branch June 2, 2026 18:48
@github-project-automation github-project-automation Bot moved this from Development to Complete in OpenFront Release Management Jun 2, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jun 3, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

1 participant