-
Notifications
You must be signed in to change notification settings - Fork 1k
Feature/custom colors and leaderboard #3950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
dea0461
afe2928
491b5dd
67b4fcb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,108 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| import { LitElement, html } from "lit"; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { customElement, state } from "lit/decorators.js"; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { UserSettings } from "../core/game/UserSettings"; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export const COLOR_MODE_CHANGED_EVENT = "color-mode-changed"; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * A small button that sits next to the Skin/Pattern button on the main menu. | ||||||||||||||||||||||||||||||||||||||||||||||
| * Clicking it cycles through color modes: Random → Custom → Team. | ||||||||||||||||||||||||||||||||||||||||||||||
| * In "custom" mode a native color picker is also shown. | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| @customElement("color-input") | ||||||||||||||||||||||||||||||||||||||||||||||
| export class ColorInput extends LitElement { | ||||||||||||||||||||||||||||||||||||||||||||||
| @state() private mode: "random" | "custom" | "team" = "random"; | ||||||||||||||||||||||||||||||||||||||||||||||
| @state() private primaryColor = "#2196f3"; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private userSettings = new UserSettings(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| createRenderRoot() { | ||||||||||||||||||||||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| connectedCallback() { | ||||||||||||||||||||||||||||||||||||||||||||||
| super.connectedCallback(); | ||||||||||||||||||||||||||||||||||||||||||||||
| this.mode = this.userSettings.colorMode(); | ||||||||||||||||||||||||||||||||||||||||||||||
| this.primaryColor = this.userSettings.customPrimaryColor(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private cycleMode() { | ||||||||||||||||||||||||||||||||||||||||||||||
| const next: Record<string, "random" | "custom" | "team"> = { | ||||||||||||||||||||||||||||||||||||||||||||||
| random: "custom", | ||||||||||||||||||||||||||||||||||||||||||||||
| custom: "team", | ||||||||||||||||||||||||||||||||||||||||||||||
| team: "random", | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| const newMode = next[this.mode]; | ||||||||||||||||||||||||||||||||||||||||||||||
| this.mode = newMode; | ||||||||||||||||||||||||||||||||||||||||||||||
| this.userSettings.setColorMode(newMode); | ||||||||||||||||||||||||||||||||||||||||||||||
| window.dispatchEvent(new CustomEvent(COLOR_MODE_CHANGED_EVENT)); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private onColorChange(e: Event) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const color = (e.target as HTMLInputElement).value; | ||||||||||||||||||||||||||||||||||||||||||||||
| this.primaryColor = color; | ||||||||||||||||||||||||||||||||||||||||||||||
| this.userSettings.setCustomPrimaryColor(color); | ||||||||||||||||||||||||||||||||||||||||||||||
| // Use a slightly darker shade as secondary color | ||||||||||||||||||||||||||||||||||||||||||||||
| this.userSettings.setCustomSecondaryColor(color); | ||||||||||||||||||||||||||||||||||||||||||||||
| window.dispatchEvent(new CustomEvent(COLOR_MODE_CHANGED_EVENT)); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private modeLabel(): string { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (this.mode === "custom") return "Custom"; | ||||||||||||||||||||||||||||||||||||||||||||||
| if (this.mode === "team") return "Team"; | ||||||||||||||||||||||||||||||||||||||||||||||
| return "Random"; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| private modeIcon(): string { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (this.mode === "custom") return "🎨"; | ||||||||||||||||||||||||||||||||||||||||||||||
| if (this.mode === "team") return "🤝"; | ||||||||||||||||||||||||||||||||||||||||||||||
| return "🎲"; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| render() { | ||||||||||||||||||||||||||||||||||||||||||||||
| return html` | ||||||||||||||||||||||||||||||||||||||||||||||
| <div class="relative flex flex-col items-center gap-1 h-full"> | ||||||||||||||||||||||||||||||||||||||||||||||
| <!-- Main button — same style as pattern-input --> | ||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||
| id="color-input-btn" | ||||||||||||||||||||||||||||||||||||||||||||||
| title="Color Mode: ${this.modeLabel()} (click to change)" | ||||||||||||||||||||||||||||||||||||||||||||||
| class="pattern-btn m-0 p-0 w-full h-full flex cursor-pointer flex-col justify-center items-center focus:outline-none focus:ring-0 transition-all duration-200 hover:scale-105 bg-surface hover:brightness-[1.08] active:brightness-[0.95] hover:shadow-[var(--shadow-action-card-hover)] rounded-lg overflow-hidden gap-1" | ||||||||||||||||||||||||||||||||||||||||||||||
| @click=${this.cycleMode} | ||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||
| ${this.mode === "custom" | ||||||||||||||||||||||||||||||||||||||||||||||
| ? html` | ||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||
| class="w-7 h-7 rounded-md border-2 border-white/30" | ||||||||||||||||||||||||||||||||||||||||||||||
| style="background:${this.primaryColor}" | ||||||||||||||||||||||||||||||||||||||||||||||
| ></div> | ||||||||||||||||||||||||||||||||||||||||||||||
| <span class="text-[7px] leading-tight font-black text-white uppercase">Custom</span> | ||||||||||||||||||||||||||||||||||||||||||||||
| ` | ||||||||||||||||||||||||||||||||||||||||||||||
| : this.mode === "team" | ||||||||||||||||||||||||||||||||||||||||||||||
| ? html` | ||||||||||||||||||||||||||||||||||||||||||||||
| <span class="text-xl leading-none">🤝</span> | ||||||||||||||||||||||||||||||||||||||||||||||
| <span class="text-[7px] leading-tight font-black text-white uppercase">Team</span> | ||||||||||||||||||||||||||||||||||||||||||||||
| ` | ||||||||||||||||||||||||||||||||||||||||||||||
| : html` | ||||||||||||||||||||||||||||||||||||||||||||||
| <span class="text-xl leading-none">🎲</span> | ||||||||||||||||||||||||||||||||||||||||||||||
| <span class="text-[7px] leading-tight font-black text-white uppercase">Random</span> | ||||||||||||||||||||||||||||||||||||||||||||||
| `} | ||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| <!-- Hidden native color picker — only shown in custom mode --> | ||||||||||||||||||||||||||||||||||||||||||||||
| ${this.mode === "custom" | ||||||||||||||||||||||||||||||||||||||||||||||
| ? html` | ||||||||||||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||||||||||||
| type="color" | ||||||||||||||||||||||||||||||||||||||||||||||
| id="color-input-picker" | ||||||||||||||||||||||||||||||||||||||||||||||
| .value=${this.primaryColor} | ||||||||||||||||||||||||||||||||||||||||||||||
| @input=${this.onColorChange} | ||||||||||||||||||||||||||||||||||||||||||||||
| title="Pick your territory color" | ||||||||||||||||||||||||||||||||||||||||||||||
| class="absolute bottom-0 left-0 w-full h-1 opacity-0 cursor-pointer" | ||||||||||||||||||||||||||||||||||||||||||||||
| style="height:100%;top:0;" | ||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+91
to
+102
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's read the ColorInput.ts file to see the full context
head -n 120 src/client/ColorInput.ts | tail -n +70Repository: openfrontio/OpenFrontIO Length of output: 1459 🏁 Script executed: # Check if the file uses translateText() function
rg "translateText" src/client/ColorInput.ts -A 2 -B 2Repository: openfrontio/OpenFrontIO Length of output: 49 🏁 Script executed: # Look for all user-visible strings in the file
rg -A 3 -B 3 'title=|placeholder=|textContent' src/client/ColorInput.tsRepository: openfrontio/OpenFrontIO Length of output: 938 🏁 Script executed: # Check the resources/lang/en.json for existing translations
head -n 50 resources/lang/en.jsonRepository: openfrontio/OpenFrontIO Length of output: 1452 🏁 Script executed: # Look for how mode cycling button is implemented and attached
rg -A 10 "onModeChange|modeButton|mode-cycle" src/client/ColorInput.tsRepository: openfrontio/OpenFrontIO Length of output: 49 🏁 Script executed: # Check the modeLabel() method to see if it returns localized text
rg -A 5 "modeLabel" src/client/ColorInput.tsRepository: openfrontio/OpenFrontIO Length of output: 715 🏁 Script executed: # Check how the file is structured - parent container for button and input
rg -B 15 "color-input-picker" src/client/ColorInput.ts | head -n 30Repository: openfrontio/OpenFrontIO Length of output: 694 🏁 Script executed: # Verify the exact parent container structure
ast-grep --pattern 'render() {
$$$
<button
id="color-input-btn"
$$$
>
$$$
</button>
$$$
}'Repository: openfrontio/OpenFrontIO Length of output: 49 Color picker overlay blocks mode-cycle button and text lacks translations. In custom mode, the transparent color input ( Suggested fix for overlay- class="absolute bottom-0 left-0 w-full h-1 opacity-0 cursor-pointer"
- style="height:100%;top:0;"
+ class="absolute inset-x-0 top-1 mx-auto w-7 h-7 opacity-0 cursor-pointer"Also refactor 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| ` | ||||||||||||||||||||||||||||||||||||||||||||||
| : ""} | ||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||
| `; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import { LitElement, html } from "lit"; | ||
| import { customElement, property } from "lit/decorators.js"; | ||
|
|
||
| @customElement("setting-color") | ||
| export class SettingColor extends LitElement { | ||
| @property() label = "Color"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Default label should be localized. The default visible label As per coding guidelines: 🤖 Prompt for AI Agents |
||
| @property() description = ""; | ||
| @property() value = "#000000"; | ||
|
|
||
| createRenderRoot() { | ||
| return this; | ||
| } | ||
|
|
||
| private handleChange(e: Event) { | ||
| const input = e.target as HTMLInputElement; | ||
| this.value = input.value; | ||
|
|
||
| // Dispatch an event so the parent component can listen to it | ||
| this.dispatchEvent(new CustomEvent("change", { | ||
| detail: { value: this.value }, | ||
| bubbles: true, | ||
| composed: true | ||
| })); | ||
| } | ||
|
|
||
| render() { | ||
| return html` | ||
| <label | ||
| class="flex flex-row items-center justify-between w-full p-4 bg-white/5 border border-white/10 rounded-xl hover:bg-white/10 transition-all gap-4 cursor-pointer" | ||
| > | ||
| <div class="flex flex-col flex-1 min-w-0 mr-4"> | ||
| <div class="text-white font-bold text-base block mb-1"> | ||
| ${this.label} | ||
| </div> | ||
| <div class="text-white/50 text-sm leading-snug"> | ||
| ${this.description} | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="relative shrink-0 flex items-center gap-2"> | ||
| <div class="text-xs font-mono text-white/50">${this.value}</div> | ||
| <input | ||
| type="color" | ||
| class="h-8 w-8 cursor-pointer rounded-md bg-transparent border-0 p-0" | ||
| .value=${this.value} | ||
| @input=${this.handleChange} | ||
| @change=${this.handleChange} | ||
| /> | ||
| </div> | ||
| </label> | ||
| `; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Localize new UI strings via
translateText().These user-facing labels/titles are hardcoded and bypass localization.
As per coding guidelines:
src/client/**/*.{ts,tsx}: All user-visible text must go throughtranslateText()function with corresponding entries inresources/lang/en.json.Also applies to: 68-69, 78-78, 83-83, 87-87, 99-99
🤖 Prompt for AI Agents