Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions resources/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@
"territory_patterns_label": "🏳️ Territory Skins",
"territory_patterns_desc": "Choose whether to display territory skin designs in game",
"performance_overlay_label": "Performance Overlay",
"performance_overlay_desc": "Toggle the performance overlay. When enabled, the performance overlay will be displayed. Press shift-D during game to toggle.",
"performance_overlay_desc": "Toggle the performance overlay. When enabled, the performance overlay will be displayed. Use your configured keybind during gameplay.",
"easter_writing_speed_label": "Writing Speed Multiplier",
"easter_writing_speed_desc": "Adjust how fast you pretend to code (x1–x100)",
"easter_bug_count_label": "Bug Count",
Expand All @@ -521,6 +521,12 @@
"view_options": "View Options",
"toggle_view": "Toggle View",
"toggle_view_desc": "Alternate view (terrain/countries)",
"close_view": "Close Menus",
"close_view_desc": "Close open menus and exit the current view.",
"reset_graphics": "Reset Graphics",
"reset_graphics_desc": "Press this key combination to reset graphics.",
"performance_overlay_keybind": "Toggle Performance Overlay",
"performance_overlay_keybind_desc": "Key combination used to toggle performance stats.",
"build_controls": "Build Controls",
"build_city": "Build City",
"build_city_desc": "Build a City under your cursor.",
Expand All @@ -547,7 +553,15 @@
"build_menu_modifier_desc": "Hold this key while clicking to open the build menu.",
"emoji_menu_modifier": "Emoji Menu Modifier",
"emoji_menu_modifier_desc": "Hold this key while clicking to open the emoji menu.",
"auto_upgrade": "Auto-Upgrade",
"auto_upgrade_desc": "Keybind to auto-upgrade the nearest building.",
"attack_ratio_controls": "Attack Ratio Controls",
"attack_modifier": "Attack Modifier",
"attack_modifier_desc": "Hold this key while clicking to attack when left click opens the menu.",
"attack_ratio_scroll_up": "Attack Ratio Increase (Alternate)",
"attack_ratio_scroll_up_desc": "Additional keybind to increase the attack ratio.",
"attack_ratio_scroll_down": "Attack Ratio Decrease (Alternate)",
"attack_ratio_scroll_down_desc": "Additional keybind to decrease the attack ratio.",
"attack_ratio_up": "Increase Attack Ratio",
"attack_ratio_up_desc": "Increase attack ratio by 10%",
"attack_ratio_down": "Decrease Attack Ratio",
Expand All @@ -564,6 +578,10 @@
"zoom_out_desc": "Zoom out the map",
"zoom_in": "Zoom In",
"zoom_in_desc": "Zoom in the map",
"zoom_out_alt": "Zoom Out (Alternate)",
"zoom_out_alt_desc": "Secondary key to zoom out the map",
"zoom_in_alt": "Zoom In (Alternate)",
"zoom_in_alt_desc": "Secondary key to zoom in the map",
"camera_movement": "Camera Movement",
"center_camera": "Center Camera",
"center_camera_desc": "Center camera on player",
Expand All @@ -575,6 +593,14 @@
"move_down_desc": "Move the camera downward",
"move_right": "Move Camera Right",
"move_right_desc": "Move the camera to the right",
"move_up_alt": "Move Camera Up (Alternate)",
"move_up_alt_desc": "Secondary key to move the camera upward",
"move_left_alt": "Move Camera Left (Alternate)",
"move_left_alt_desc": "Secondary key to move the camera to the left",
"move_down_alt": "Move Camera Down (Alternate)",
"move_down_alt_desc": "Secondary key to move the camera downward",
"move_right_alt": "Move Camera Right (Alternate)",
"move_right_alt_desc": "Secondary key to move the camera to the right",
"reset": "Reset",
"unbind": "Unbind",
"on": "On",
Expand All @@ -584,7 +610,8 @@
"exit_game_info": "Return to main menu",
"background_music_volume": "Background Music Volume",
"sound_effects_volume": "Sound Effects Volume",
"keybind_conflict_error": "The key {key} is already bound to another action."
"keybind_conflict_error": "The key {key} is already bound to another action.",
"keybind_reserved_error": "The key {key} is reserved by the browser and can't be used."
},
"chat": {
"title": "Quick Chat",
Expand Down
122 changes: 37 additions & 85 deletions src/client/HelpModal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { html } from "lit";
import { customElement, state } from "lit/decorators.js";
import { getDefaultKeybinds } from "../client/Keybinds";
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import { translateText } from "../client/Utils";
import { customElement, query, state } from "lit/decorators.js";
import { translateText, TUTORIAL_VIDEO_URL } from "../client/Utils";
Comment on lines +2 to 6
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 | 🔴 Critical

Duplicate imports break the build.

Lines 2–4 duplicate the imports already on lines 5–6. The CI pipeline fails with TS2300: Duplicate identifier for customElement, state, and translateText. Merge both groups into single import statements and keep query and TUTORIAL_VIDEO_URL which lines 2–4 dropped.

🐛 Fix: merge into two clean imports
-import { customElement, state } from "lit/decorators.js";
-import { getDefaultKeybinds } from "../client/Keybinds";
-import { translateText } from "../client/Utils";
-import { customElement, query, state } from "lit/decorators.js";
-import { translateText, TUTORIAL_VIDEO_URL } from "../client/Utils";
+import { customElement, query, state } from "lit/decorators.js";
+import { getDefaultKeybinds } from "../client/Keybinds";
+import { translateText, TUTORIAL_VIDEO_URL } from "../client/Utils";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { customElement, state } from "lit/decorators.js";
import { getDefaultKeybinds } from "../client/Keybinds";
import { translateText } from "../client/Utils";
import { customElement, query, state } from "lit/decorators.js";
import { translateText, TUTORIAL_VIDEO_URL } from "../client/Utils";
import { customElement, query, state } from "lit/decorators.js";
import { getDefaultKeybinds } from "../client/Keybinds";
import { translateText, TUTORIAL_VIDEO_URL } from "../client/Utils";
🧰 Tools
🪛 Biome (2.3.13)

[error] 5-5: Shouldn't redeclare 'customElement'. Consider to delete it or rename it.

'customElement' is defined here:

(lint/suspicious/noRedeclare)


[error] 5-5: Shouldn't redeclare 'state'. Consider to delete it or rename it.

'state' is defined here:

(lint/suspicious/noRedeclare)


[error] 6-6: Shouldn't redeclare 'translateText'. Consider to delete it or rename it.

'translateText' is defined here:

(lint/suspicious/noRedeclare)

🪛 GitHub Actions: 🧪 CI

[error] 2-2: Duplicate identifier 'customElement'.

🪛 GitHub Actions: 🚀 Deploy

[error] 2-6: Duplicate identifier 'customElement'. Duplicate identifier 'state'. Duplicate identifier 'translateText'.


[error] 2-6: TS2300: Duplicate identifier errors detected during TypeScript compilation in HelpModal.ts.

🤖 Prompt for AI Agents
In `@src/client/HelpModal.ts` around lines 2 - 6, There are duplicate imports
causing TS2300; consolidate the duplicated import groups by merging the
lit/decorators.js imports into one statement that exports customElement, query
and state, and merge the ../client/Utils imports into one statement that exports
getDefaultKeybinds, translateText and TUTORIAL_VIDEO_URL (so replace the two
separate import blocks with a single import from "lit/decorators.js" containing
customElement, query, state and a single import from "../client/Utils"
containing getDefaultKeybinds, translateText, TUTORIAL_VIDEO_URL).

import { BaseModal } from "./components/BaseModal";
Expand Down Expand Up @@ -29,39 +32,44 @@ export class HelpModal extends BaseModal {
saved = Object.fromEntries(
Object.entries(parsed)
.map(([k, v]) => {
if (this.isKeybindObject(v)) return [k, v.value];
if (this.isKeybindObject(v)) return [k, (v as any).value];

if (
typeof v === "object" &&
v !== null &&
"value" in (v as any) &&
Array.isArray((v as any).value)
) {
return [k, (v as any).value.join(" ")];
}

if (Array.isArray(v)) return [k, v.join(" ")];
Comment on lines +37 to +46
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

🧩 Analysis chain

🏁 Script executed:

cd src/client && head -100 HelpModal.ts | tail -80

Repository: openfrontio/OpenFrontIO

Length of output: 2247


🏁 Script executed:

# Find the full HelpModal.ts file and check its size
wc -l src/client/HelpModal.ts

Repository: openfrontio/OpenFrontIO

Length of output: 95


🏁 Script executed:

# Look for getKeyLabel function
rg "getKeyLabel" src/client/HelpModal.ts -A 15

Repository: openfrontio/OpenFrontIO

Length of output: 1194


🏁 Script executed:

# Look for getDefaultKeybinds
rg "getDefaultKeybinds\|DEFAULT_KEYBINDS" src/client/ -A 10

Repository: openfrontio/OpenFrontIO

Length of output: 49


🏁 Script executed:

# Check the full getKeybinds method
rg "getKeybinds\(\)" src/client/HelpModal.ts -A 40

Repository: openfrontio/OpenFrontIO

Length of output: 1938


🏁 Script executed:

# Search for getDefaultKeybinds function definition
rg "function getDefaultKeybinds|const getDefaultKeybinds|export.*getDefaultKeybinds" src/client/ -A 20

Repository: openfrontio/OpenFrontIO

Length of output: 222


🏁 Script executed:

# Look in the entire src/client directory for DEFAULT_KEYBINDS or getDefaultKeybinds
rg "getDefaultKeybinds" src/ -B 2 -A 25

Repository: openfrontio/OpenFrontIO

Length of output: 8863


🏁 Script executed:

# Check the Keybinds.ts file to see DEFAULT_KEYBINDS
cat -n src/client/Keybinds.ts | head -60

Repository: openfrontio/OpenFrontIO

Length of output: 1751


Array values joined with space, but getKeyLabel splits on +.

When a stored keybind value is an array (e.g. ["ShiftLeft", "KeyR"]), it gets joined with " " (space). But getKeyLabel splits composite keys on "+" and all default keybinds use "+" as the composite delimiter (e.g. "ShiftLeft+KeyR"). This means array-based composite keybinds will render as an unrecognized label like "ShiftLeft KeyR" instead of "⇧ Shift + R".

Join arrays with "+" to match the default format and parsing logic.

Fix: use "+" as the join delimiter
             Array.isArray((v as any).value)
            ) {
-              return [k, (v as any).value.join(" ")];
+              return [k, (v as any).value.join("+")];
             }

-            if (Array.isArray(v)) return [k, v.join(" ")];
+            if (Array.isArray(v)) return [k, v.join("+")];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
typeof v === "object" &&
v !== null &&
"value" in (v as any) &&
Array.isArray((v as any).value)
) {
return [k, (v as any).value.join(" ")];
}
if (Array.isArray(v)) return [k, v.join(" ")];
if (
typeof v === "object" &&
v !== null &&
"value" in (v as any) &&
Array.isArray((v as any).value)
) {
return [k, (v as any).value.join("+")];
}
if (Array.isArray(v)) return [k, v.join("+")];
🤖 Prompt for AI Agents
In `@src/client/HelpModal.ts` around lines 37 - 46, The code that normalizes
stored keybind arrays in HelpModal (the branch handling typeof v === "object"
with "value" and the Array.isArray(v) branch) joins arrays with a space, which
mismatches getKeyLabel's expectation of "+"-delimited composites; change both
join calls to use "+" as the delimiter so array-based keybinds (e.g.,
["ShiftLeft","KeyR"]) become "ShiftLeft+KeyR" and are parsed correctly by
getKeyLabel.

if (typeof v === "string") return [k, v];
return [k, undefined];
})
.filter(([, v]) => typeof v === "string" && v !== "Null"),
.filter(([, v]) => typeof v === "string"),
) as Record<string, string>;
} catch (e) {
console.warn("Invalid keybinds JSON:", e);
}

const isMac = /Mac/.test(navigator.userAgent);
return {
toggleView: "Space",
centerCamera: "KeyC",
moveUp: "KeyW",
moveDown: "KeyS",
moveLeft: "KeyA",
moveRight: "KeyD",
zoomOut: "KeyQ",
zoomIn: "KeyE",
attackRatioDown: "KeyT",
attackRatioUp: "KeyY",
swapDirection: "KeyU",
shiftKey: "ShiftLeft",
modifierKey: isMac ? "MetaLeft" : "ControlLeft",
altKey: "AltLeft",
resetGfx: "KeyR",
...getDefaultKeybinds(),
...saved,
};
}

private getKeyLabel(code: string): string {
if (!code) return "";
if (code === "Null") return translateText("common.none");

if (code.includes("+")) {
return code
.split("+")
.map((part) => this.getKeyLabel(part.trim()))
.filter(Boolean)
.join(" + ");
}

const specialLabels: Record<string, string> = {
ShiftLeft: "⇧ Shift",
Expand All @@ -72,6 +80,10 @@ export class HelpModal extends BaseModal {
AltRight: "Alt",
MetaLeft: "⌘",
MetaRight: "⌘",
MouseLeft: "Left Click",
MouseMiddle: "Middle Click",
ScrollUp: "Scroll Up",
ScrollDown: "Scroll Down",
Space: "Space",
ArrowUp: "↑",
ArrowDown: "↓",
Expand Down Expand Up @@ -275,62 +287,23 @@ export class HelpModal extends BaseModal {
</tr>
<tr class="hover:bg-white/5 transition-colors">
<td class="py-3 pl-4 border-b border-white/5">
<div class="inline-flex items-center gap-2">
${this.renderKey(keybinds.shiftKey)}
<span class="text-white/40 font-bold">+</span>
<div
class="w-5 h-8 border border-white/40 rounded-full relative"
>
<div
class="absolute top-0 left-0 w-1/2 h-1/2 bg-red-500/80 rounded-tl-full"
></div>
<div
class="w-0.5 h-1.5 bg-white/40 rounded-full absolute top-1.5 left-1/2 -translate-x-1/2"
></div>
</div>
</div>
${this.renderKey(keybinds.attackModifier)}
</td>
<td class="py-3 border-b border-white/5 text-white/70">
${translateText("help_modal.action_attack_altclick")}
</td>
</tr>
<tr class="hover:bg-white/5 transition-colors">
<td class="py-3 pl-4 border-b border-white/5">
<div class="inline-flex items-center gap-2">
${this.renderKey(keybinds.modifierKey)}
<span class="text-white/40 font-bold">+</span>
<div
class="w-5 h-8 border border-white/40 rounded-full relative"
>
<div
class="absolute top-0 left-0 w-1/2 h-1/2 bg-red-500/80 rounded-tl-full"
></div>
<div
class="w-0.5 h-1.5 bg-white/40 rounded-full absolute top-1.5 left-1/2 -translate-x-1/2"
></div>
</div>
</div>
${this.renderKey(keybinds.modifierKey)}
</td>
<td class="py-3 border-b border-white/5 text-white/70">
${translateText("help_modal.action_build")}
</td>
</tr>
<tr class="hover:bg-white/5 transition-colors">
<td class="py-3 pl-4 border-b border-white/5">
<div class="inline-flex items-center gap-2">
${this.renderKey(keybinds.altKey)}
<span class="text-white/40 font-bold">+</span>
<div
class="w-5 h-8 border border-white/40 rounded-full relative"
>
<div
class="absolute top-0 left-0 w-1/2 h-1/2 bg-red-500/80 rounded-tl-full"
></div>
<div
class="w-0.5 h-1.5 bg-white/40 rounded-full absolute top-1.5 left-1/2 -translate-x-1/2"
></div>
</div>
</div>
${this.renderKey(keybinds.altKey)}
</td>
<td class="py-3 border-b border-white/5 text-white/70">
${translateText("help_modal.action_emote")}
Expand Down Expand Up @@ -381,22 +354,9 @@ export class HelpModal extends BaseModal {
</tr>
<tr class="hover:bg-white/5 transition-colors">
<td class="py-3 pl-4 border-b border-white/5">
<div class="inline-flex items-center gap-2">
${this.renderKey(keybinds.shiftKey)}
<span class="text-white/40 font-bold">+</span>
<div class="flex items-center gap-1">
<div
class="w-5 h-8 border border-white/40 rounded-full relative"
>
<div
class="w-0.5 h-2 bg-red-400 rounded-full absolute top-1.5 left-1/2 -translate-x-1/2"
></div>
</div>
<div class="flex flex-col text-[10px] text-white/50">
<span>↑</span>
<span>↓</span>
</div>
</div>
<div class="flex flex-wrap gap-2">
${this.renderKey(keybinds.attackRatioScrollDown)}
${this.renderKey(keybinds.attackRatioScrollUp)}
</div>
</td>
<td class="py-3 border-b border-white/5 text-white/70">
Expand All @@ -406,8 +366,6 @@ export class HelpModal extends BaseModal {
<tr class="hover:bg-white/5 transition-colors">
<td class="py-3 pl-4 border-b border-white/5">
<div class="inline-flex items-center gap-2">
${this.renderKey(keybinds.altKey)}
<span class="text-white/40 font-bold">+</span>
${this.renderKey(keybinds.resetGfx)}
</div>
</td>
Expand All @@ -417,13 +375,7 @@ export class HelpModal extends BaseModal {
</tr>
<tr class="hover:bg-white/5 transition-colors">
<td class="py-3 pl-4 border-b border-white/5">
<div
class="w-5 h-8 border border-white/40 rounded-full relative"
>
<div
class="w-0.5 h-2 bg-red-400 rounded-full absolute top-1.5 left-1/2 -translate-x-1/2"
></div>
</div>
${this.renderKey(keybinds.autoUpgrade)}
</td>
<td class="py-3 border-b border-white/5 text-white/70">
${translateText("help_modal.action_auto_upgrade")}
Expand Down
Loading
Loading