From e6fcfdb51488d75fb007c44f890f9ec4729b538f Mon Sep 17 00:00:00 2001 From: GonnaHetzMe Date: Wed, 25 Mar 2026 02:09:07 +0100 Subject: [PATCH 1/5] Feat: dev name --- .../core/src/protos/tank_definitions.proto | 1 + packages/core/src/protos/tank_definitions.ts | 18 +++++++ packages/i18n/strings/en.json | 1 + .../Armor/components/ArmorPlateDisplay.tsx | 26 +++++++++- .../Tankopedia/MetaSection/index.tsx | 52 ++++++++++++++++++- 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/packages/core/src/protos/tank_definitions.proto b/packages/core/src/protos/tank_definitions.proto index a827ef158..cd36878cf 100644 --- a/packages/core/src/protos/tank_definitions.proto +++ b/packages/core/src/protos/tank_definitions.proto @@ -39,6 +39,7 @@ message TankDefinition { required float camouflage_onFire = 29; required string equipment_preset = 30; required uint32 weight = 31; + optional string dev_name = 32; } message ResearchCost { diff --git a/packages/core/src/protos/tank_definitions.ts b/packages/core/src/protos/tank_definitions.ts index 08431fa77..f8358e187 100644 --- a/packages/core/src/protos/tank_definitions.ts +++ b/packages/core/src/protos/tank_definitions.ts @@ -316,6 +316,7 @@ export interface TankDefinition { camouflage_onFire: number; equipment_preset: string; weight: number; + dev_name: string; } export interface TankDefinition_RolesEntry { @@ -693,6 +694,7 @@ function createBaseTankDefinition(): TankDefinition { camouflage_onFire: 0, equipment_preset: "", weight: 0, + dev_name: "", }; } @@ -803,6 +805,9 @@ export const TankDefinition: MessageFns = { if (message.weight !== 0) { writer.uint32(248).uint32(message.weight); } + if (message.dev_name !== "") { + writer.uint32(258).string(message.dev_name); + } return writer; }, @@ -1094,6 +1099,14 @@ export const TankDefinition: MessageFns = { message.weight = reader.uint32(); continue; } + case 32: { + if (tag !== 258) { + break; + } + + message.dev_name = reader.string(); + continue; + } } if ((tag & 7) === 4 || tag === 0) { break; @@ -1181,6 +1194,7 @@ export const TankDefinition: MessageFns = { ? globalThis.String(object.equipment_preset) : "", weight: isSet(object.weight) ? globalThis.Number(object.weight) : 0, + dev_name: isSet(object.dev_name) ? globalThis.String(object.dev_name) : "", }; }, @@ -1282,6 +1296,9 @@ export const TankDefinition: MessageFns = { if (message.weight !== 0) { obj.weight = Math.round(message.weight); } + if (message.dev_name !== "") { + obj.dev_name = message.dev_name; + } return obj; }, @@ -1343,6 +1360,7 @@ export const TankDefinition: MessageFns = { message.camouflage_onFire = object.camouflage_onFire ?? 0; message.equipment_preset = object.equipment_preset ?? ""; message.weight = object.weight ?? 0; + message.dev_name = object.dev_name ?? ""; return message; }, }; diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 8b0dba23f..c5bc8b85f 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -1083,6 +1083,7 @@ "tier": "Tier", "type": "Type", "dev_id": "DEV: ID", + "dev_name": "DEV: NAME", "purchase": "Purchase price", "restoration": "Restoration price", "sale": "Sale price", diff --git a/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx b/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx index 9bbc2e524..87148680c 100644 --- a/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx +++ b/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx @@ -6,11 +6,13 @@ import { IconButton, Text, TextField, + Tooltip, } from "@radix-ui/themes"; import { Html } from "@react-three/drei"; -import { useRef } from "react"; +import { useRef, useState } from "react"; import { radToDeg } from "three/src/math/MathUtils.js"; import { resolveArmorIndex } from "../../../core/blitzkit/resolveArmorIndex"; +import { useLocale } from "../../../hooks/useLocale"; import { App } from "../../../stores/app"; import { Duel } from "../../../stores/duel"; import { Tankopedia } from "../../../stores/tankopedia"; @@ -21,6 +23,8 @@ export function ArmorPlateDisplay() { const highlightArmor = Tankopedia.use((state) => state.highlightArmor); const developerMode = App.useDeferred((state) => state.developerMode, false); const input = useRef(null); + const { strings } = useLocale(); + const [copied, setCopied] = useState(false); if (highlightArmor === undefined) return null; @@ -60,7 +64,25 @@ export function ArmorPlateDisplay() { {developerMode && ( <> - DEV: {highlightArmor.name} + DEV:{" "} + + { + navigator.clipboard.writeText(highlightArmor.name); + setCopied(true); + }} + onPointerLeave={() => setCopied(false)} + > + {highlightArmor.name} + + )} diff --git a/packages/website/src/components/Tankopedia/MetaSection/index.tsx b/packages/website/src/components/Tankopedia/MetaSection/index.tsx index f8d3fe135..787a0b069 100644 --- a/packages/website/src/components/Tankopedia/MetaSection/index.tsx +++ b/packages/website/src/components/Tankopedia/MetaSection/index.tsx @@ -4,7 +4,8 @@ import { TankType, TIER_ROMAN_NUMERALS, } from '@blitzkit/core'; -import { Box, Code, Flex } from '@radix-ui/themes'; +import { Box, Code, Flex, Tooltip } from '@radix-ui/themes'; +import { useState } from 'react'; import { Var } from '../../../core/radix/var'; import { useLocale } from '../../../hooks/useLocale'; import { App } from '../../../stores/app'; @@ -23,6 +24,7 @@ export function MetaSection() { const protagonist = Duel.use((state) => state.protagonist.tank); const ClassIcon = classIcons[protagonist.class]; const { locale, strings } = useLocale(); + const [copied, setCopied] = useState(null); return ( @@ -101,7 +103,53 @@ export function MetaSection() { - {protagonist.id} + + { + navigator.clipboard.writeText(`${protagonist.id}`); + setCopied('id'); + }} + onPointerLeave={() => { + if (copied === 'id') setCopied(null); + }} + > + {protagonist.id} + + + + )} + {developerMode && ( + + + { + if (!protagonist.dev_name) return; + navigator.clipboard.writeText(protagonist.dev_name); + setCopied('name'); + }} + onPointerLeave={() => { + if (copied === 'name') setCopied(null); + }} + > + {protagonist.dev_name ?? '-'} + + )} {protagonist.type === TankType.PREMIUM && ( From 4cdb71201366aeb76f73585715c4320098129964 Mon Sep 17 00:00:00 2001 From: GonnaHetzMe Date: Wed, 25 Mar 2026 11:18:53 +0100 Subject: [PATCH 2/5] feat: add CopyableCode component --- .../Armor/components/ArmorPlateDisplay.tsx | 29 ++--------- .../website/src/components/CopyableCode.tsx | 32 ++++++++++++ .../Tankopedia/MetaSection/index.tsx | 52 +++---------------- 3 files changed, 45 insertions(+), 68 deletions(-) create mode 100644 packages/website/src/components/CopyableCode.tsx diff --git a/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx b/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx index 87148680c..fb3fd4b6e 100644 --- a/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx +++ b/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx @@ -1,21 +1,19 @@ import { ReloadIcon } from "@radix-ui/react-icons"; import { Card, - Code, Flex, IconButton, Text, TextField, - Tooltip, } from "@radix-ui/themes"; import { Html } from "@react-three/drei"; -import { useRef, useState } from "react"; +import { useRef } from "react"; import { radToDeg } from "three/src/math/MathUtils.js"; import { resolveArmorIndex } from "../../../core/blitzkit/resolveArmorIndex"; -import { useLocale } from "../../../hooks/useLocale"; import { App } from "../../../stores/app"; import { Duel } from "../../../stores/duel"; import { Tankopedia } from "../../../stores/tankopedia"; +import { CopyableCode } from "../../CopyableCode"; import { layerTypeNames } from "./ShotDisplayCard"; import { ArmorType } from "./SpacedArmorScene"; @@ -23,8 +21,6 @@ export function ArmorPlateDisplay() { const highlightArmor = Tankopedia.use((state) => state.highlightArmor); const developerMode = App.useDeferred((state) => state.developerMode, false); const input = useRef(null); - const { strings } = useLocale(); - const [copied, setCopied] = useState(false); if (highlightArmor === undefined) return null; @@ -65,24 +61,9 @@ export function ArmorPlateDisplay() { <> DEV:{" "} - - { - navigator.clipboard.writeText(highlightArmor.name); - setCopied(true); - }} - onPointerLeave={() => setCopied(false)} - > - {highlightArmor.name} - - + + {highlightArmor.name} + )} diff --git a/packages/website/src/components/CopyableCode.tsx b/packages/website/src/components/CopyableCode.tsx new file mode 100644 index 000000000..8877b4b07 --- /dev/null +++ b/packages/website/src/components/CopyableCode.tsx @@ -0,0 +1,32 @@ +import { Code, Tooltip } from "@radix-ui/themes"; +import { useState, type ReactNode } from "react"; +import { useLocale } from "../hooks/useLocale"; + +interface CopyableCodeProps { + copy?: string; + children: ReactNode; +} + +export function CopyableCode({ copy, children }: CopyableCodeProps) { + const [copied, setCopied] = useState(false); + const { strings } = useLocale(); + + return ( + + { + if (!copy) return; + navigator.clipboard.writeText(copy); + setCopied(true); + }} + onPointerLeave={() => setCopied(false)} + > + {children} + + + ); +} diff --git a/packages/website/src/components/Tankopedia/MetaSection/index.tsx b/packages/website/src/components/Tankopedia/MetaSection/index.tsx index 787a0b069..f3276136a 100644 --- a/packages/website/src/components/Tankopedia/MetaSection/index.tsx +++ b/packages/website/src/components/Tankopedia/MetaSection/index.tsx @@ -4,13 +4,13 @@ import { TankType, TIER_ROMAN_NUMERALS, } from '@blitzkit/core'; -import { Box, Code, Flex, Tooltip } from '@radix-ui/themes'; -import { useState } from 'react'; +import { Box, Flex } from '@radix-ui/themes'; import { Var } from '../../../core/radix/var'; import { useLocale } from '../../../hooks/useLocale'; import { App } from '../../../stores/app'; import { Duel } from '../../../stores/duel'; import { classIcons } from '../../ClassIcon'; +import { CopyableCode } from '../../CopyableCode'; import { Listing } from './components/Listing'; const NATIONAL_BANNER_POSITION_OVERRIDES: Record = { @@ -24,7 +24,6 @@ export function MetaSection() { const protagonist = Duel.use((state) => state.protagonist.tank); const ClassIcon = classIcons[protagonist.class]; const { locale, strings } = useLocale(); - const [copied, setCopied] = useState(null); return ( @@ -103,53 +102,18 @@ export function MetaSection() { - - { - navigator.clipboard.writeText(`${protagonist.id}`); - setCopied('id'); - }} - onPointerLeave={() => { - if (copied === 'id') setCopied(null); - }} - > - {protagonist.id} - - + + {protagonist.id} + )} {developerMode && ( - - { - if (!protagonist.dev_name) return; - navigator.clipboard.writeText(protagonist.dev_name); - setCopied('name'); - }} - onPointerLeave={() => { - if (copied === 'name') setCopied(null); - }} - > - {protagonist.dev_name ?? '-'} - - + + {protagonist.dev_name ?? '-'} + )} {protagonist.type === TankType.PREMIUM && ( From 5b9ff7acb37d689408d42d08b0b2b935b8a7030b Mon Sep 17 00:00:00 2001 From: GonnaHetzMe Date: Wed, 25 Mar 2026 17:25:52 +0100 Subject: [PATCH 3/5] feat: CopyableCode styles and remove DEV name fallback --- .../Armor/components/ArmorPlateDisplay.tsx | 7 ++++- .../website/src/components/CopyableCode.tsx | 30 ++++++++++++------- .../Tankopedia/MetaSection/index.tsx | 19 +++++++++--- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx b/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx index fb3fd4b6e..80e54eda4 100644 --- a/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx +++ b/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx @@ -61,7 +61,12 @@ export function ArmorPlateDisplay() { <> DEV:{" "} - + {highlightArmor.name} diff --git a/packages/website/src/components/CopyableCode.tsx b/packages/website/src/components/CopyableCode.tsx index 8877b4b07..030d52963 100644 --- a/packages/website/src/components/CopyableCode.tsx +++ b/packages/website/src/components/CopyableCode.tsx @@ -1,29 +1,37 @@ -import { Code, Tooltip } from "@radix-ui/themes"; -import { useState, type ReactNode } from "react"; +import { Code, Tooltip, type CodeProps } from "@radix-ui/themes"; +import { useState } from "react"; import { useLocale } from "../hooks/useLocale"; -interface CopyableCodeProps { +export interface CopyableCodeProps extends CodeProps { copy?: string; - children: ReactNode; } -export function CopyableCode({ copy, children }: CopyableCodeProps) { +export function CopyableCode({ + copy, + onClick, + onPointerLeave, + style, + children, + ...props +}: CopyableCodeProps) { const [copied, setCopied] = useState(false); const { strings } = useLocale(); return ( { + {...props} + style={{ ...(copy ? { cursor: "copy" } : {}), ...style }} + onClick={(event) => { + onClick?.(event); if (!copy) return; navigator.clipboard.writeText(copy); setCopied(true); }} - onPointerLeave={() => setCopied(false)} + onPointerLeave={(event) => { + onPointerLeave?.(event); + setCopied(false); + }} > {children} diff --git a/packages/website/src/components/Tankopedia/MetaSection/index.tsx b/packages/website/src/components/Tankopedia/MetaSection/index.tsx index f3276136a..62f560f5b 100644 --- a/packages/website/src/components/Tankopedia/MetaSection/index.tsx +++ b/packages/website/src/components/Tankopedia/MetaSection/index.tsx @@ -24,6 +24,7 @@ export function MetaSection() { const protagonist = Duel.use((state) => state.protagonist.tank); const ClassIcon = classIcons[protagonist.class]; const { locale, strings } = useLocale(); + const devName = (protagonist as { dev_name?: string }).dev_name; return ( @@ -102,17 +103,27 @@ export function MetaSection() { - + {protagonist.id} )} - {developerMode && ( + {developerMode && devName && ( - - {protagonist.dev_name ?? '-'} + + {devName} )} From 2ab576a37c65747ec0f346415a092f30d4a8c86f Mon Sep 17 00:00:00 2001 From: GonnaHetzMe Date: Thu, 26 Mar 2026 09:29:35 +0100 Subject: [PATCH 4/5] fix: Set pointer cursor and simplify devName access --- packages/website/src/components/CopyableCode.tsx | 2 +- .../website/src/components/Tankopedia/MetaSection/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/src/components/CopyableCode.tsx b/packages/website/src/components/CopyableCode.tsx index 030d52963..52b33648a 100644 --- a/packages/website/src/components/CopyableCode.tsx +++ b/packages/website/src/components/CopyableCode.tsx @@ -21,7 +21,7 @@ export function CopyableCode({ { onClick?.(event); if (!copy) return; diff --git a/packages/website/src/components/Tankopedia/MetaSection/index.tsx b/packages/website/src/components/Tankopedia/MetaSection/index.tsx index 62f560f5b..83849a0b9 100644 --- a/packages/website/src/components/Tankopedia/MetaSection/index.tsx +++ b/packages/website/src/components/Tankopedia/MetaSection/index.tsx @@ -24,7 +24,7 @@ export function MetaSection() { const protagonist = Duel.use((state) => state.protagonist.tank); const ClassIcon = classIcons[protagonist.class]; const { locale, strings } = useLocale(); - const devName = (protagonist as { dev_name?: string }).dev_name; + const devName = protagonist.dev_name; return ( From 8345f99603f4f8921d035f3ce4092dea71ccd64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Thu, 26 Mar 2026 08:06:43 -0500 Subject: [PATCH 5/5] fix: use dev name directly --- .../website/src/components/Tankopedia/MetaSection/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/website/src/components/Tankopedia/MetaSection/index.tsx b/packages/website/src/components/Tankopedia/MetaSection/index.tsx index 83849a0b9..03e15e7bc 100644 --- a/packages/website/src/components/Tankopedia/MetaSection/index.tsx +++ b/packages/website/src/components/Tankopedia/MetaSection/index.tsx @@ -24,7 +24,6 @@ export function MetaSection() { const protagonist = Duel.use((state) => state.protagonist.tank); const ClassIcon = classIcons[protagonist.class]; const { locale, strings } = useLocale(); - const devName = protagonist.dev_name; return ( @@ -113,7 +112,7 @@ export function MetaSection() { )} - {developerMode && devName && ( + {developerMode && ( @@ -123,7 +122,7 @@ export function MetaSection() { highContrast color="gray" > - {devName} + {protagonist.dev_name} )}