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..80e54eda4 100644 --- a/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx +++ b/packages/website/src/components/Armor/components/ArmorPlateDisplay.tsx @@ -1,7 +1,6 @@ import { ReloadIcon } from "@radix-ui/react-icons"; import { Card, - Code, Flex, IconButton, Text, @@ -14,6 +13,7 @@ import { resolveArmorIndex } from "../../../core/blitzkit/resolveArmorIndex"; 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"; @@ -60,7 +60,15 @@ export function ArmorPlateDisplay() { {developerMode && ( <> - DEV: {highlightArmor.name} + DEV:{" "} + + {highlightArmor.name} + )} diff --git a/packages/website/src/components/CopyableCode.tsx b/packages/website/src/components/CopyableCode.tsx new file mode 100644 index 000000000..52b33648a --- /dev/null +++ b/packages/website/src/components/CopyableCode.tsx @@ -0,0 +1,40 @@ +import { Code, Tooltip, type CodeProps } from "@radix-ui/themes"; +import { useState } from "react"; +import { useLocale } from "../hooks/useLocale"; + +export interface CopyableCodeProps extends CodeProps { + copy?: string; +} + +export function CopyableCode({ + copy, + onClick, + onPointerLeave, + style, + children, + ...props +}: CopyableCodeProps) { + const [copied, setCopied] = useState(false); + const { strings } = useLocale(); + + return ( + + { + onClick?.(event); + if (!copy) return; + navigator.clipboard.writeText(copy); + setCopied(true); + }} + 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 f8d3fe135..03e15e7bc 100644 --- a/packages/website/src/components/Tankopedia/MetaSection/index.tsx +++ b/packages/website/src/components/Tankopedia/MetaSection/index.tsx @@ -4,12 +4,13 @@ import { TankType, TIER_ROMAN_NUMERALS, } from '@blitzkit/core'; -import { Box, Code, Flex } from '@radix-ui/themes'; +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 = { @@ -101,7 +102,28 @@ export function MetaSection() { - {protagonist.id} + + {protagonist.id} + + + )} + {developerMode && ( + + + {protagonist.dev_name} + )} {protagonist.type === TankType.PREMIUM && (