Feature/Move theme system from core to client-side ThemeProvider (#4108)

**Add approved & assigned issue number here:** 

Resolves #2549

## Description:

Themes are purely for the client's rendering, and the server doesn't
need context on them. This PR moves `Theme.ts` from
`src/core/configuration` to `src/client/theme` and moves affiliation
colors to `render-settings.json`.

This is to support the ability to add additional themes more quickly,
such as colorblind-friendly themes. No visible changes occur from this
refactor.

## Please complete the following:

- [X] I have added screenshots for all UI updates
- [X] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [X] 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:

jetaviz

---------

Co-authored-by: Josh Harris <josh@wickedsick.com>
This commit is contained in:
noahschmal
2026-06-02 09:32:08 +00:00
committed by GitHub
co-authored by Josh Harris
parent d8c127a462
commit 2c8a66625c
23 changed files with 150 additions and 88 deletions
+1 -1
View File
@@ -1,8 +1,8 @@
import { Colord } from "colord";
import { Theme } from "src/core/configuration/Theme";
import { assetUrl } from "../../core/AssetUrls";
import { TrainType, UnitType } from "../../core/game/Game";
import { UnitView } from "../../core/game/GameView";
import { Theme } from "../theme/Theme";
const atomBombSprite = assetUrl("sprites/atombomb.png");
const hydrogenBombSprite = assetUrl("sprites/hydrogenbomb.png");
const mirvSprite = assetUrl("sprites/mirv2.png");
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from "../../../core/game/GameUpdates";
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
import { Controller } from "../../Controller";
import { themeProvider } from "../../theme/ThemeProvider";
import {
GoToPlayerEvent,
GoToPositionEvent,
@@ -166,7 +167,7 @@ export class AttacksDisplay extends LitElement implements Controller {
const cached = this.spriteDataURLCache.get(key);
if (cached) return cached;
try {
const canvas = getColoredSprite(unit, this.game.config().theme());
const canvas = getColoredSprite(unit, themeProvider.current());
const dataURL = canvas.toDataURL();
this.spriteDataURLCache.set(key, dataURL);
return dataURL;
+2 -4
View File
@@ -7,6 +7,7 @@ import { GameMode, Team } from "../../../core/game/Game";
import { GameView } from "../../../core/game/GameView";
import { Controller } from "../../Controller";
import { Platform } from "../../Platform";
import { themeProvider } from "../../theme/ThemeProvider";
import { getTranslatedPlayerTeamLabel, translateText } from "../../Utils";
import { ImmunityBarVisibleEvent } from "./ImmunityTimer";
import { SpawnBarVisibleEvent } from "./SpawnTimer";
@@ -66,10 +67,7 @@ export class GameLeftSidebar extends LitElement implements Controller {
if (!this.playerTeam && this.game.myPlayer()?.team()) {
this.playerTeam = this.game.myPlayer()!.team();
if (this.playerTeam) {
this.playerColor = this.game
.config()
.theme()
.teamColor(this.playerTeam);
this.playerColor = themeProvider.current().teamColor(this.playerTeam);
this.requestUpdate();
}
}
+3 -3
View File
@@ -18,6 +18,7 @@ import {
MouseMoveEvent,
TouchEvent,
} from "../../InputHandler";
import { themeProvider } from "../../theme/ThemeProvider";
import { TransformHandler } from "../../TransformHandler";
import {
getTranslatedPlayerTeamLabel,
@@ -360,9 +361,8 @@ export class PlayerInfoOverlay extends LitElement implements Controller {
>
<span class="text-xs font-normal text-gray-400"
>[<span
style="color: ${this.game
.config()
.theme()
style="color: ${themeProvider
.current()
.teamColor(player.team()!)
.toHex()}"
>${playerTeam}</span
+2 -1
View File
@@ -4,6 +4,7 @@ import { EventBus, GameEvent } from "../../../core/EventBus";
import { GameMode, GameType, Team } from "../../../core/game/Game";
import { GameView } from "../../../core/game/GameView";
import { Controller } from "../../Controller";
import { themeProvider } from "../../theme/ThemeProvider";
import { TransformHandler } from "../../TransformHandler";
export class SpawnBarVisibleEvent implements GameEvent {
@@ -71,7 +72,7 @@ export class SpawnTimer extends LitElement implements Controller {
teamTiles.set(team, tiles + player.numTilesOwned());
}
const theme = this.game.config().theme();
const theme = themeProvider.current();
const total = sumIterator(teamTiles.values());
if (total > 0) {
for (const [team, count] of teamTiles) {