feat: migrate all

This commit is contained in:
TrèsAbhi committed 2025-10-02 10:15:38 -05:00
1 parent 5594c0ec3b
commit 320a764d45
38 files changed
+632 -600

No files matched your search

@@ -53,32 +53,38 @@ function LayerEntry({
<td>
{layer.type === null && (
<Text size="2" color={shotStatusColor}>
{literals(strings.common.units.mm, [
Math.round(layer.distance * 1000).toLocaleString(locale),
])}
{literals(strings.common.units.mm, {
value: Math.round(layer.distance * 1000).toLocaleString(locale),
})}
</Text>
)}
{layer.type === ArmorType.External && (
<Text size="2" color={shotStatusColor}>
{literals(strings.common.units.mm, [
Math.round(layer.thickness).toLocaleString(locale),
])}
{literals(strings.common.units.mm, {
value: Math.round(layer.thickness).toLocaleString(locale),
})}
</Text>
)}
{(layer.type === ArmorType.Primary ||
layer.type === ArmorType.Spaced) && (
<Text size="2" color={shotStatusColor}>
{literals(
angle === undefined
? strings.common.units.mm
: strings.website.tools.tankopedia.sandbox.dynamic.shot_card
{angle === undefined
? literals(strings.common.units.mm, {
value: Math.round(layer.thicknessAngled).toLocaleString(
locale
),
})
: literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.stats.thickness_and_angle,
[
Math.round(layer.thicknessAngled).toLocaleString(locale),
radToDeg(angle ?? 0).toFixed(0),
]
)}
{
thickness: Math.round(layer.thicknessAngled).toLocaleString(
locale
),
angle: radToDeg(angle ?? 0).toFixed(0),
}
)}
</Text>
)}
</td>
@@ -89,7 +95,7 @@ function LayerEntry({
{literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats
.ricochet_loss,
[Math.max(-100, -50 * layer.distance).toFixed(0)]
{ loss: Math.max(-100, -50 * layer.distance).toFixed(0) }
)}
</Text>
)}
@@ -100,7 +106,7 @@ function LayerEntry({
{literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats
.nominal,
[Math.round(layer.thickness).toLocaleString(locale)]
{ thickness: Math.round(layer.thickness).toLocaleString(locale) }
)}
</Text>
)}
@@ -132,7 +138,7 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) {
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.status[
shot.in.status
],
[Math.round(shot.damage).toLocaleString(locale)]
{ damage: Math.round(shot.damage).toLocaleString(locale) }
)}
</Text>
@@ -202,7 +208,7 @@ export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) {
{literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.status[shot.out.status],
[Math.round(shot.damage).toLocaleString(locale)]
{ damage: Math.round(shot.damage).toLocaleString(locale) }
)}
</Text>
@@ -155,7 +155,7 @@ export function GenerateURL({ embed }: GenerateURLProps) {
{literals(
strings.website.tools.embed.configuration.export.generate
.success.info,
[`${copyUser?.nickname}`]
{ name: copyUser?.nickname }
)}
</Text>
</Flex>
@@ -50,9 +50,9 @@ export function GalleryList({ skeleton }: MaybeSkeletonComponentProps) {
{skeleton ? (
<InlineSkeleton width="7rem" />
) : (
literals(strings.website.tools.gallery.search.results, [
filtered.length.toLocaleString(locale),
])
literals(strings.website.tools.gallery.search.results, {
count: filtered.length.toLocaleString(locale),
})
)}
</Text>
+4 -4
View File
@@ -139,10 +139,10 @@ export function Guesser() {
<Flex justify="center" style={{ userSelect: "none" }}>
<Text>
{literals(strings.website.tools.guess.stats, [
`${correctGuesses}`,
`${totalGuesses}`,
])}
{literals(strings.website.tools.guess.stats, {
correct: correctGuesses,
total: totalGuesses,
})}
</Text>
</Flex>
+18 -16
View File
@@ -1,8 +1,8 @@
---
import { Code, Flex, Heading, Text } from '@radix-ui/themes';
import { resolveBranchName } from '../core/blitzkit/branches';
import { getStrings } from '../core/i18n/getStrings';
import { literals } from '@blitzkit/i18n/src/literals';
import { Code, Flex, Heading, Text } from "@radix-ui/themes";
import { resolveBranchName } from "../core/blitzkit/branches";
import { getStrings } from "../core/i18n/getStrings";
import { literals } from "@blitzkit/i18n/src/literals";
const { locale } = Astro.params;
const branchName = resolveBranchName(locale);
@@ -45,7 +45,7 @@ const strings = getStrings(locale);
);
}
#home-hero[data-variant='pride'] {
#home-hero[data-variant="pride"] {
--g-1-dark: var(--crimson-3);
--g-1-light: var(--crimson-7);
--g-2-dark: var(--orange-3);
@@ -58,7 +58,7 @@ const strings = getStrings(locale);
--g-7-light: var(--purple-7);
}
#home-hero[data-variant='spooky'] {
#home-hero[data-variant="spooky"] {
--g-1-dark: var(--orange-3);
--g-1-light: var(--orange-7);
--g-2-dark: var(--tomato-3);
@@ -73,23 +73,23 @@ const strings = getStrings(locale);
</style>
<script>
import { clamp, times } from 'lodash-es';
import { clamp, times } from "lodash-es";
const n = 5;
const homeHero = document.getElementById('home-hero');
const homeHero = document.getElementById("home-hero");
if (homeHero) {
switch (new Date().getMonth()) {
case 5:
homeHero.dataset.variant = 'pride';
homeHero.dataset.variant = "pride";
break;
case 9:
homeHero.dataset.variant = 'spooky';
homeHero.dataset.variant = "spooky";
break;
}
window.addEventListener('pointermove', (event) => {
window.addEventListener("pointermove", (event) => {
const x = event.clientX / window.innerWidth;
const c = clamp(
(1 -
@@ -97,7 +97,7 @@ const strings = getStrings(locale);
window.innerHeight) **
2,
0,
1,
1
);
// https://www.desmos.com/calculator/ndz2poxk32
@@ -128,13 +128,13 @@ const strings = getStrings(locale);
width="100%"
>
<Flex direction="column" align="center" gap="1">
<Heading size={{ initial: '8', xs: '9' }} weight="bold" align="center">
<Flex align="center" gap={{ initial: '2', xs: '4' }}>
<Heading size={{ initial: "8", xs: "9" }} weight="bold" align="center">
<Flex align="center" gap={{ initial: "2", xs: "4" }}>
BlitzKit
{
isBranchNamed && (
<Code
size={{ initial: '7', xs: '8' }}
size={{ initial: "7", xs: "8" }}
color="gray"
variant="outline"
>
@@ -146,7 +146,9 @@ const strings = getStrings(locale);
</Heading>
<Text color="gray" align="center" size="4"
>{literals(strings.website.home.hero.credit, ['TrèsAbhi'])}</Text
>{
literals(strings.website.home.hero.credit, { name: "TrèsAbhi" })
}</Text
>
</Flex>
</Flex>
@@ -32,7 +32,7 @@ function Content() {
{strings.website.home.hot_tanks.title}
</Heading>
<Text color="gray" align="center" mb="5" size="2">
{literals(strings.website.home.hot_tanks.subtitle, ["7"])}
{literals(strings.website.home.hot_tanks.subtitle, { days: 7 })}
</Text>
<Flex justify="center" gap="4" wrap="wrap">
@@ -1,12 +1,12 @@
import { formatCompact } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n/src/literals';
import { InfoCircledIcon } from '@radix-ui/react-icons';
import { Callout, Flex } from '@radix-ui/themes';
import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions';
import { awaitableDiscoveredIdsDefinitions } from '../../core/awaitables/discoveredIdsDefinitions';
import { useLocale } from '../../hooks/useLocale';
import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps';
import { InlineSkeleton } from '../InlineSkeleton';
import { formatCompact } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n/src/literals";
import { InfoCircledIcon } from "@radix-ui/react-icons";
import { Callout, Flex } from "@radix-ui/themes";
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
import { awaitableDiscoveredIdsDefinitions } from "../../core/awaitables/discoveredIdsDefinitions";
import { useLocale } from "../../hooks/useLocale";
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
import { InlineSkeleton } from "../InlineSkeleton";
const [discoveredIdsDefinitions, averageDefinitions] = await Promise.all([
awaitableDiscoveredIdsDefinitions,
@@ -17,22 +17,22 @@ export function PerformanceInfo({ skeleton }: MaybeSkeletonComponentProps) {
const { strings, locale } = useLocale();
const samples = formatCompact(
locale,
Math.round(discoveredIdsDefinitions.count),
Math.round(discoveredIdsDefinitions.count)
);
const minutesAgo = Math.floor(
(Date.now() - averageDefinitions.time) / (1000 * 60),
(Date.now() - averageDefinitions.time) / (1000 * 60)
);
const hoursAgo = Math.floor(minutesAgo / 60);
const timeAgo = literals(
strings.website.tools.performance.estimation[
hoursAgo === 0 ? 'minutes' : 'hours'
hoursAgo === 0 ? "minutes" : "hours"
],
[`${hoursAgo === 0 ? minutesAgo : hoursAgo}`],
{ count: hoursAgo === 0 ? minutesAgo : hoursAgo }
);
return (
<Flex justify="center">
<Callout.Root style={{ width: 'fit-content' }}>
<Callout.Root style={{ width: "fit-content" }}>
<Callout.Icon>
<InfoCircledIcon />
</Callout.Icon>
@@ -40,10 +40,10 @@ export function PerformanceInfo({ skeleton }: MaybeSkeletonComponentProps) {
{skeleton && <InlineSkeleton width="9rem" />}
{!skeleton &&
literals(strings.website.tools.performance.estimation.body, [
samples,
timeAgo,
])}
literals(strings.website.tools.performance.estimation.body, {
count: samples,
age: timeAgo,
})}
</Callout.Text>
</Callout.Root>
</Flex>
@@ -431,9 +431,9 @@ export const TankSearch = memo<TankSearchProps>(
<Text color="gray">
{tanksFiltered.length === 1
? strings.website.common.tank_search.count_singular
: literals(strings.website.common.tank_search.count_plural, [
`${tanksFiltered.length.toLocaleString(locale)}`,
])}
: literals(strings.website.common.tank_search.count_plural, {
count: tanksFiltered.length.toLocaleString(locale),
})}
</Text>
{onSelectAll && (
@@ -460,9 +460,11 @@ export const TankSearch = memo<TankSearchProps>(
{tankopediaSort.by !== "meta.none" && (
<Text color="gray">
{literals(strings.website.common.tank_search.sorting_by, [
strings.website.common.tank_search.sort[tankopediaSort.by],
])}
{literals(strings.website.common.tank_search.sorting_by, {
name: strings.website.common.tank_search.sort[
tankopediaSort.by
],
})}
{SORT_UNITS[tankopediaSort.by] === undefined
? ""
: ` (${SORT_UNITS[tankopediaSort.by]})`}
@@ -1,16 +1,16 @@
import type { AssaultRanges } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Box, Flex, Text } from '@radix-ui/themes';
import { clamp } from 'three/src/math/MathUtils.js';
import type { AssaultRanges } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Box, Flex, Text } from "@radix-ui/themes";
import { clamp } from "three/src/math/MathUtils.js";
import {
fakeLogSquishyHigh,
fakeLogSquishyLow,
} from '../../../../../../../core/blitzkit/fakeLog';
import type { TankCharacteristics } from '../../../../../../../core/blitzkit/tankCharacteristics';
import { Var } from '../../../../../../../core/radix/var';
import { useLocale } from '../../../../../../../hooks/useLocale';
import { VisualizerCard } from '../VisualizerCard';
import { AssaultCursor } from './components/Cursor';
} from "../../../../../../../core/blitzkit/fakeLog";
import type { TankCharacteristics } from "../../../../../../../core/blitzkit/tankCharacteristics";
import { Var } from "../../../../../../../core/radix/var";
import { useLocale } from "../../../../../../../hooks/useLocale";
import { VisualizerCard } from "../VisualizerCard";
import { AssaultCursor } from "./components/Cursor";
interface Props {
ranges: AssaultRanges;
@@ -48,27 +48,29 @@ export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) {
p="3"
pt="8"
pr="5"
style={{ backgroundColor: Var('gray-1') }}
style={{ backgroundColor: Var("gray-1") }}
flexGrow="1"
>
<Flex flexGrow="1">
<Box position="relative" width={Var('space-8')}>
<Box position="relative" width={Var("space-8")}>
{ranges.map((range, index) => (
<Text
key={index}
style={{
position: 'absolute',
position: "absolute",
left: 0,
bottom: `${fakeLogSquishyLow(range.factor / maxFactor) * 100}%`,
transform: 'translateY(50%)',
transform: "translateY(50%)",
}}
color="gray"
size="1"
wrap="nowrap"
>
{literals(strings.common.units.hp, [
`${Math.round(stats.damageWithoutAssault * range.factor)}`,
])}
{literals(strings.common.units.hp, {
value: Math.round(
stats.damageWithoutAssault * range.factor
),
})}
</Text>
))}
</Box>
@@ -77,8 +79,8 @@ export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) {
{ranges.map((range, index) => {
const mix =
clamp(2 * (range.factor / maxFactor - 1) + 1, 0, 1) * 100;
const color0 = `color-mix(in hsl, ${Var('tomato-9')}, ${Var('jade-9')} ${mix}%)`;
const color1 = `color-mix(in hsl, ${Var('tomato-7')}, ${Var('jade-7')} ${mix}%)`;
const color0 = `color-mix(in hsl, ${Var("tomato-9")}, ${Var("jade-9")} ${mix}%)`;
const color1 = `color-mix(in hsl, ${Var("tomato-7")}, ${Var("jade-7")} ${mix}%)`;
const last = ranges[index - 1];
const lastFakeDistance = last?.fakeDistance ?? 0;
const fakeWidth = range.fakeDistance - lastFakeDistance;
@@ -105,8 +107,8 @@ export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) {
width="100%"
height="1pt"
style={{
transform: 'translateY(50%)',
backgroundColor: Var('gray-11'),
transform: "translateY(50%)",
backgroundColor: Var("gray-11"),
}}
/>
))}
@@ -118,7 +120,7 @@ export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) {
</Flex>
</Flex>
<Flex ml="8" height={Var('space-7')}>
<Flex ml="8" height={Var("space-7")}>
{ranges.map((range, index) => {
const last = ranges[index - 1];
const lastFakeDistance = last?.fakeDistance ?? 0;
@@ -137,15 +139,15 @@ export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) {
color="gray"
mt="2"
style={{
position: 'absolute',
position: "absolute",
right: 0,
transform: 'translateX(50%)',
writingMode: 'vertical-rl',
transform: "translateX(50%)",
writingMode: "vertical-rl",
}}
>
{literals(strings.common.units.m, [
`${Math.round(range.distance)}`,
])}
{literals(strings.common.units.m, {
value: Math.round(range.distance),
})}
</Text>
</Box>
);
@@ -38,16 +38,17 @@ export function CompareOptions(props: FlexProps) {
<SegmentedControl.Item value={`${TankopediaRelativeAgainst.Class}`}>
{literals(
strings.website.tools.tankopedia.characteristics.compare.class,
[
TIER_ROMAN_NUMERALS[tank.tier],
strings.common.tank_class_short_plural_lowercase[tank.class],
]
{
tier: TIER_ROMAN_NUMERALS[tank.tier],
class:
strings.common.tank_class_short_plural_lowercase[tank.class],
}
)}
</SegmentedControl.Item>
<SegmentedControl.Item value={`${TankopediaRelativeAgainst.Tier}`}>
{literals(
strings.website.tools.tankopedia.characteristics.compare.tier,
[TIER_ROMAN_NUMERALS[tank.tier]]
{ tier: TIER_ROMAN_NUMERALS[tank.tier] }
)}
</SegmentedControl.Item>
<SegmentedControl.Item value={`${TankopediaRelativeAgainst.All}`}>
@@ -1,15 +1,15 @@
import { CREW_MEMBER_NAMES, CrewType } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { AccessibilityIcon, InfoCircledIcon } from '@radix-ui/react-icons';
import { Flex, Heading, IconButton, Popover, Text } from '@radix-ui/themes';
import { Fragment } from 'react/jsx-runtime';
import { awaitableProvisionDefinitions } from '../../../../../../core/awaitables/provisionDefinitions';
import { useEquipment } from '../../../../../../hooks/useEquipment';
import { useLocale } from '../../../../../../hooks/useLocale';
import { Duel } from '../../../../../../stores/duel';
import { InfoWithDelta } from './InfoWithDelta';
import { StatsTableWrapper } from './StatsTableWrapper';
import type { StatsAcceptorProps } from './TraverseVisualizer';
import { CREW_MEMBER_NAMES, CrewType } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { AccessibilityIcon, InfoCircledIcon } from "@radix-ui/react-icons";
import { Flex, Heading, IconButton, Popover, Text } from "@radix-ui/themes";
import { Fragment } from "react/jsx-runtime";
import { awaitableProvisionDefinitions } from "../../../../../../core/awaitables/provisionDefinitions";
import { useEquipment } from "../../../../../../hooks/useEquipment";
import { useLocale } from "../../../../../../hooks/useLocale";
import { Duel } from "../../../../../../stores/duel";
import { InfoWithDelta } from "./InfoWithDelta";
import { StatsTableWrapper } from "./StatsTableWrapper";
import type { StatsAcceptorProps } from "./TraverseVisualizer";
const provisionDefinitions = await awaitableProvisionDefinitions;
@@ -24,7 +24,7 @@ export function Crew({ stats }: StatsAcceptorProps) {
provisionDefinitions.provisions[provision].crew
? total + provisionDefinitions.provisions[provision].crew! / 100
: total,
0,
0
) + (hasImprovedVentilation ? 0.08 : 0);
const commanderMastery = 1 + provisionCrewBonus;
@@ -65,8 +65,8 @@ export function Crew({ stats }: StatsAcceptorProps) {
]
}${
member.count > 1
? ` ${literals(strings.common.units.x, [`${member.count}`])}`
: ''
? ` ${literals(strings.common.units.x, { value: member.count })}`
: ""
}`}
decimals={0}
noRanking
@@ -93,9 +93,9 @@ export function Crew({ stats }: StatsAcceptorProps) {
? strings.website.tools.tankopedia.crew[
CREW_MEMBER_NAMES[sub]
]
: sub,
: sub
)
.join(', ')}
.join(", ")}
</Flex>
</>
}
@@ -119,7 +119,7 @@ export function Firepower({
name={literals(
strings.website.tools.tankopedia.characteristics.values
.shell_index,
[`${index + 1}`]
{ index: index + 1 }
)}
decimals={2}
deltaType="lowerIsBetter"
@@ -191,7 +191,7 @@ export function Firepower({
name={literals(
strings.website.tools.tankopedia.characteristics.values
.dispersion_at_distance,
["100"]
{ distance: 100 }
)}
/>
<InfoWithDelta
@@ -359,31 +359,33 @@ export function GunFlexibilityVisualizer({
<VisualizerCornerStat
label={strings.website.tools.tankopedia.visualizers.flexibility.max}
value={literals(strings.common.units.deg, [
radToDeg(maxPitch).toFixed(0),
])}
value={literals(strings.common.units.deg, {
value: radToDeg(maxPitch).toFixed(0),
})}
side="bottom-left"
/>
<VisualizerCornerStat
label={strings.website.tools.tankopedia.visualizers.flexibility.min}
value={literals(strings.common.units.deg, [
radToDeg(minPitch).toFixed(0),
])}
value={literals(strings.common.units.deg, {
value: radToDeg(minPitch).toFixed(0),
})}
side="bottom-right"
/>
<VisualizerCornerStat
label={strings.website.tools.tankopedia.visualizers.flexibility.pitch}
value={literals(strings.common.units.deg, [
radToDeg(pitch).toFixed(0),
])}
value={literals(strings.common.units.deg, {
value: radToDeg(pitch).toFixed(0),
})}
side="top-left"
/>
<VisualizerCornerStat
label={strings.website.tools.tankopedia.visualizers.flexibility.yaw}
value={literals(strings.common.units.deg, [radToDeg(yaw).toFixed(0)])}
value={literals(strings.common.units.deg, {
value: radToDeg(yaw).toFixed(0),
})}
side="top-right"
/>
</VisualizerCard>
@@ -1,15 +1,15 @@
import { asset, isExplosive } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Box, Flex, Text } from '@radix-ui/themes';
import { useRef, useState } from 'react';
import { clamp, degToRad, radToDeg } from 'three/src/math/MathUtils.js';
import { Var } from '../../../../../../../core/radix/var';
import { useLocale } from '../../../../../../../hooks/useLocale';
import { Duel } from '../../../../../../../stores/duel';
import type { StatsAcceptorProps } from '../TraverseVisualizer';
import { VisualizerCard } from '../VisualizerCard';
import { VisualizerCornerStat } from '../VisualizerCornerStat';
import { ShellSwitcher } from './components/ShellSwitcher';
import { asset, isExplosive } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Box, Flex, Text } from "@radix-ui/themes";
import { useRef, useState } from "react";
import { clamp, degToRad, radToDeg } from "three/src/math/MathUtils.js";
import { Var } from "../../../../../../../core/radix/var";
import { useLocale } from "../../../../../../../hooks/useLocale";
import { Duel } from "../../../../../../../stores/duel";
import type { StatsAcceptorProps } from "../TraverseVisualizer";
import { VisualizerCard } from "../VisualizerCard";
import { VisualizerCornerStat } from "../VisualizerCornerStat";
import { ShellSwitcher } from "./components/ShellSwitcher";
export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
const shell = Duel.use((state) => state.protagonist.shell);
@@ -30,7 +30,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
<Flex direction="column" mb="6" align="center">
<VisualizerCard
mb="0"
style={{ width: '100%' }}
style={{ width: "100%" }}
ref={container}
onPointerMove={(event) => {
if (!container.current || explodes) return;
@@ -50,7 +50,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
top="0"
left="0"
style={{
background: `linear-gradient(to right, transparent, ${Var('gray-a4')})`,
background: `linear-gradient(to right, transparent, ${Var("gray-a4")})`,
}}
>
<Box
@@ -73,20 +73,20 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
background: `conic-gradient(
at 0 50%,
${Var('ruby-a2')},
${Var('ruby-a1')} ${Math.PI / 2 - ricochet}rad,
${Var("ruby-a2")},
${Var("ruby-a1")} ${Math.PI / 2 - ricochet}rad,
${Var('jade-a2')} ${Math.PI / 2 - ricochet}rad,
${Var('jade-a4')} ${Math.PI / 2 - normalization}rad,
${Var("jade-a2")} ${Math.PI / 2 - ricochet}rad,
${Var("jade-a4")} ${Math.PI / 2 - normalization}rad,
${Var('cyan-a5')} ${Math.PI / 2 - normalization}rad,
${Var('cyan-a3')} ${Math.PI / 2 + normalization}rad,
${Var("cyan-a5")} ${Math.PI / 2 - normalization}rad,
${Var("cyan-a3")} ${Math.PI / 2 + normalization}rad,
${Var('jade-a2')} ${Math.PI / 2 + normalization}rad,
${Var('jade-a4')} ${Math.PI / 2 + ricochet}rad,
${Var("jade-a2")} ${Math.PI / 2 + normalization}rad,
${Var("jade-a4")} ${Math.PI / 2 + ricochet}rad,
${Var('ruby-a1')} ${Math.PI / 2 + ricochet}rad,
${Var('ruby-a2')} 180deg
${Var("ruby-a1")} ${Math.PI / 2 + ricochet}rad,
${Var("ruby-a2")} 180deg
`,
}}
/>
@@ -97,7 +97,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
top="50%"
height="18rem"
style={{
transformOrigin: 'top center',
transformOrigin: "top center",
transform: `translateX(-50%) rotate(${-Math.PI / 2 - angle}rad)`,
}}
>
@@ -107,8 +107,8 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
height="100%"
bottom="100%"
style={{
transform: 'translateX(-50%)',
backgroundColor: Var('gray-a8'),
transform: "translateX(-50%)",
backgroundColor: Var("gray-a8"),
}}
/>
<Box
@@ -116,22 +116,22 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
width="3px"
height="100%"
style={{
transform: 'translateX(-50%)',
background: `linear-gradient(${Var('gray-a11')}, ${Var('gray-a4')})`,
transform: "translateX(-50%)",
background: `linear-gradient(${Var("gray-a11")}, ${Var("gray-a4")})`,
}}
/>
<img
src={asset(`icons/shells/${shell.icon}.webp`)}
style={{
objectFit: 'contain',
transform: 'translate(-50%, -50%) rotate(-45deg)',
position: 'absolute',
width: '2rem',
height: '2rem',
top: '6rem',
objectFit: "contain",
transform: "translate(-50%, -50%) rotate(-45deg)",
position: "absolute",
width: "2rem",
height: "2rem",
top: "6rem",
left: 0,
filter: 'drop-shadow(0px 0px 0.25rem black)',
filter: "drop-shadow(0px 0px 0.25rem black)",
}}
/>
</Box>
@@ -142,7 +142,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
top="50%"
height="16rem"
style={{
transformOrigin: 'top center',
transformOrigin: "top center",
transform: `translateX(-50%) rotate(${-Math.PI / 2 - effectiveAngle}rad)`,
}}
>
@@ -152,17 +152,17 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
height="100%"
top="-100%"
style={{
transform: 'translateX(-50%)',
background: `linear-gradient(${Var('gray-a4')}, ${Var('gray-a11')})`,
transform: "translateX(-50%)",
background: `linear-gradient(${Var("gray-a4")}, ${Var("gray-a11")})`,
}}
/>
</Box>
<VisualizerCornerStat
label={strings.website.tools.tankopedia.visualizers.ricochet.angle}
value={literals(strings.common.units.deg, [
radToDeg(Math.abs(angle)).toFixed(0),
])}
value={literals(strings.common.units.deg, {
value: radToDeg(Math.abs(angle)).toFixed(0),
})}
side="top-left"
/>
<VisualizerCornerStat
@@ -171,10 +171,12 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
}
value={
doesRicochet
? '-'
: literals(strings.common.units.deg, [
radToDeg(Math.min(normalization, Math.abs(angle))).toFixed(0),
])
? "-"
: literals(strings.common.units.deg, {
value: radToDeg(
Math.min(normalization, Math.abs(angle))
).toFixed(0),
})
}
side="top-right"
/>
@@ -185,10 +187,10 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
}
value={
doesRicochet
? '-'
: literals(strings.common.units.deg, [
radToDeg(Math.abs(effectiveAngle)).toFixed(0),
])
? "-"
: literals(strings.common.units.deg, {
value: radToDeg(Math.abs(effectiveAngle)).toFixed(0),
})
}
side="bottom-left"
/>
@@ -196,10 +198,10 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
label={strings.website.tools.tankopedia.visualizers.ricochet.armor}
value={
doesRicochet
? '-'
: literals(strings.common.units.percentage, [
armorPercentage.toFixed(0),
])
? "-"
: literals(strings.common.units.percentage, {
value: armorPercentage.toFixed(0),
})
}
side="bottom-right"
/>
@@ -215,7 +217,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
top="0"
left="0"
style={{
background: `radial-gradient(${Var('black-a7')}, ${Var('black-a11')})`,
background: `radial-gradient(${Var("black-a7")}, ${Var("black-a11")})`,
}}
>
<Text color="gray" align="center">
@@ -1,20 +1,20 @@
import { literals } from '@blitzkit/i18n';
import { Checkbox, Code, Flex, Tabs, Text } from '@radix-ui/themes';
import { useEffect, useRef, useState } from 'react';
import type { TankCharacteristics } from '../../../../../../../core/blitzkit/tankCharacteristics';
import { useLocale } from '../../../../../../../hooks/useLocale';
import { VisualizerCard } from '../VisualizerCard';
import { literals } from "@blitzkit/i18n";
import { Checkbox, Code, Flex, Tabs, Text } from "@radix-ui/themes";
import { useEffect, useRef, useState } from "react";
import type { TankCharacteristics } from "../../../../../../../core/blitzkit/tankCharacteristics";
import { useLocale } from "../../../../../../../hooks/useLocale";
import { VisualizerCard } from "../VisualizerCard";
export interface StatsAcceptorProps {
stats: TankCharacteristics;
}
type Terrain = 'Hard' | 'Medium' | 'Soft';
type Terrain = "Hard" | "Medium" | "Soft";
export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
const hull = useRef<HTMLDivElement>(null);
const turret = useRef<HTMLImageElement>(null);
const [terrain, setTerrain] = useState<Terrain>('Hard');
const [terrain, setTerrain] = useState<Terrain>("Hard");
const [rotateHull, setRotateHull] = useState(true);
const [rotateTurret, setRotateTurret] = useState(true);
const { strings } = useLocale();
@@ -63,10 +63,10 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
<div
ref={hull}
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
animationDuration: `${
rotateHull ? 360 / stats[`hullTraverse${terrain}Terrain`] : 0
}s`,
@@ -75,12 +75,12 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
<img
draggable="false"
style={{
width: isTurretless ? '22rem' : '12rem',
height: isTurretless ? '22rem' : '12rem',
objectFit: 'contain',
filter: 'drop-shadow(0px 0px 4px black)',
width: isTurretless ? "22rem" : "12rem",
height: isTurretless ? "22rem" : "12rem",
objectFit: "contain",
filter: "drop-shadow(0px 0px 4px black)",
}}
src={`/assets/images/tankopedia/visualizers/hull-traverse/hull${isTurretless ? '-only' : ''}.png`}
src={`/assets/images/tankopedia/visualizers/hull-traverse/hull${isTurretless ? "-only" : ""}.png`}
/>
{!isTurretless && (
@@ -88,14 +88,14 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
draggable="false"
ref={turret}
style={{
width: '27rem',
height: '27rem',
objectFit: 'contain',
position: 'absolute',
top: '50%',
left: '50%',
filter: 'drop-shadow(0px 0px 4px black)',
transform: 'translate(-50%, -50%)',
width: "27rem",
height: "27rem",
objectFit: "contain",
position: "absolute",
top: "50%",
left: "50%",
filter: "drop-shadow(0px 0px 4px black)",
transform: "translate(-50%, -50%)",
animationDuration: `${
rotateTurret ? 360 / stats.turretTraverseSpeed : 0
}s`,
@@ -110,7 +110,7 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
bottom="0"
left="50%"
style={{
transform: 'translateX(-50%)',
transform: "translateX(-50%)",
}}
>
<Tabs.Root
@@ -137,16 +137,16 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
left="50%"
bottom="7"
style={{
transform: 'translateX(-50%)',
transform: "translateX(-50%)",
}}
>
<Text color="gray" size="1">
{strings.website.tools.tankopedia.visualizers.rotator.effective}
</Text>
<Code size="1" variant="ghost">
{literals(strings.common.units.deg_s, [
`${(turretSpeedDeg + hullSpeedDeg).toFixed(1)}`,
])}
{literals(strings.common.units.deg_s, {
value: (turretSpeedDeg + hullSpeedDeg).toFixed(1),
})}
</Code>
</Flex>
@@ -160,15 +160,15 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
gap="5"
justify="center"
style={{
transform: 'translateX(-50%)',
userSelect: 'none',
transform: "translateX(-50%)",
userSelect: "none",
}}
>
<Flex
align="center"
gap="2"
onClick={() => setRotateHull((state) => !state)}
style={{ cursor: 'pointer' }}
style={{ cursor: "pointer" }}
>
<Checkbox variant="classic" checked={rotateHull} />
<Text size="2">
@@ -180,7 +180,7 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) {
align="center"
gap="2"
onClick={() => setRotateTurret((state) => !state)}
style={{ cursor: 'pointer' }}
style={{ cursor: "pointer" }}
>
<Checkbox variant="classic" checked={rotateTurret} />
<Text size="2">
@@ -1,20 +1,20 @@
import { literals } from '@blitzkit/i18n';
import { EyeOpenIcon } from '@radix-ui/react-icons';
import { Box, Card, Code, Flex, Slider, Tabs, Text } from '@radix-ui/themes';
import { useRef, useState } from 'react';
import { Var } from '../../../../../../core/radix/var';
import { useLocale } from '../../../../../../hooks/useLocale';
import type { StatsAcceptorProps } from './TraverseVisualizer';
import { literals } from "@blitzkit/i18n";
import { EyeOpenIcon } from "@radix-ui/react-icons";
import { Box, Card, Code, Flex, Slider, Tabs, Text } from "@radix-ui/themes";
import { useRef, useState } from "react";
import { Var } from "../../../../../../core/radix/var";
import { useLocale } from "../../../../../../hooks/useLocale";
import type { StatsAcceptorProps } from "./TraverseVisualizer";
const maps = [
{
id: 'ghost-factory',
string: 'ghost_factory',
id: "ghost-factory",
string: "ghost_factory",
size: 512,
},
{
id: 'dead-rail',
string: 'dead_rail',
id: "dead-rail",
string: "dead_rail",
size: 512,
},
] as const;
@@ -32,7 +32,7 @@ export function ViewRangeVisualizer({ stats }: StatsAcceptorProps) {
return (
<Card
style={{ aspectRatio: '1 / 1', touchAction: 'none' }}
style={{ aspectRatio: "1 / 1", touchAction: "none" }}
mb="6"
variant="classic"
>
@@ -61,8 +61,8 @@ export function ViewRangeVisualizer({ stats }: StatsAcceptorProps) {
style={{
backgroundImage: `url(/assets/images/tankopedia/visualizers/range/${map.id}.png)`,
filter: `grayscale(0.5) brightness(0.8) contrast(1.2)`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundSize: "cover",
backgroundPosition: "center",
}}
/>
@@ -75,16 +75,18 @@ export function ViewRangeVisualizer({ stats }: StatsAcceptorProps) {
align="center"
justify="center"
style={{
aspectRatio: '1 / 1',
transform: 'translate(-50%, -50%)',
background: `radial-gradient(${Var('black-a6')}, ${Var('gray-a12')})`,
borderRadius: '50%',
aspectRatio: "1 / 1",
transform: "translate(-50%, -50%)",
background: `radial-gradient(${Var("black-a6")}, ${Var("gray-a12")})`,
borderRadius: "50%",
}}
>
<Text style={{ userSelect: 'none' }}>
<Text style={{ userSelect: "none" }}>
<Flex align="center" gap="1">
<EyeOpenIcon width="1.25rem" height="1.25rem" />
{literals(strings.common.units.m, [resolvedViewRange.toFixed(0)])}
{literals(strings.common.units.m, {
value: resolvedViewRange.toFixed(0),
})}
</Flex>
</Text>
</Flex>
@@ -100,8 +102,8 @@ export function ViewRangeVisualizer({ stats }: StatsAcceptorProps) {
gap="3"
align="center"
style={{
backgroundColor: Var('black-a7'),
backdropFilter: 'blur(4rem)',
backgroundColor: Var("black-a7"),
backdropFilter: "blur(4rem)",
}}
>
<Text size="2">
@@ -120,11 +122,9 @@ export function ViewRangeVisualizer({ stats }: StatsAcceptorProps) {
<Code size="1" color="gray" variant="ghost">
<Flex justify="center" width="2em">
{literals(
strings.website.tools.tankopedia.visualizers.view_range
.percentage,
[`${Math.round(camouflage * 100)}`.padStart(3, ' ')],
)}
{literals(strings.common.units.percentage, {
percentage: `${Math.round(camouflage * 100)}`.padStart(3, " "),
})}
</Flex>
</Code>
</Flex>
@@ -136,7 +136,7 @@ export function ViewRangeVisualizer({ stats }: StatsAcceptorProps) {
left="0"
width="100%"
style={{
background: `radial-gradient(at 50% 100%, ${Var('gray-1')}, transparent 70%)`,
background: `radial-gradient(at 50% 100%, ${Var("gray-1")}, transparent 70%)`,
}}
>
<Tabs.Root
@@ -151,7 +151,7 @@ export function CustomShellButton() {
}}
>
<TextField.Slot side="right">
{literals(strings.common.units.mm, [""])}
{literals(strings.common.units.mm, { value: "" })}
</TextField.Slot>
</TextField.Root>
</Flex>
@@ -188,7 +188,7 @@ export function CustomShellButton() {
}}
>
<TextField.Slot side="right">
{literals(strings.common.units.mm, [""])}
{literals(strings.common.units.mm, { value: "" })}
</TextField.Slot>
</TextField.Root>
</Flex>
@@ -212,9 +212,9 @@ export function CustomShellButton() {
minWidth="8rem"
>
<Text color="gray">
{literals(strings.common.units.deg, [
(customShell.normalization ?? 0).toFixed(0),
])}
{literals(strings.common.units.deg, {
value: (customShell.normalization ?? 0).toFixed(0),
})}
</Text>
<Slider
variant="classic"
@@ -243,9 +243,9 @@ export function CustomShellButton() {
<Flex align="center" gap="2" flexGrow="1" maxWidth="15rem">
<Text color="gray">
{literals(strings.common.units.deg, [
(customShell.ricochet ?? 90).toFixed(0),
])}
{literals(strings.common.units.deg, {
value: (customShell.ricochet ?? 90).toFixed(0),
})}
</Text>
<Slider
variant="classic"
@@ -59,15 +59,15 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
>
<Flex direction="column" align="end" justify="between">
<Text color="gray" size="1">
{literals(strings.common.units.mm, [
(thicknessRange.value * 1.5).toFixed(0),
])}
{literals(strings.common.units.mm, {
value: (thicknessRange.value * 1.5).toFixed(0),
})}
</Text>
<Text size="1">
{strings.website.tools.tankopedia.sandbox.static.primary}
</Text>
<Text color="gray" size="1">
{literals(strings.common.units.mm, ["0"])}
{literals(strings.common.units.mm, { value: 0 })}
</Text>
</Flex>
@@ -100,15 +100,15 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
>
<Flex direction="column" align="end" justify="between">
<Text color="gray" size="1">
{literals(strings.common.units.mm, [
thicknessRange.value.toFixed(0),
])}
{literals(strings.common.units.mm, {
value: thicknessRange.value.toFixed(0),
})}
</Text>
<Text size="1">
{strings.website.tools.tankopedia.sandbox.static.spaced}
</Text>
<Text color="gray" size="1">
{literals(strings.common.units.mm, ["0"])}
{literals(strings.common.units.mm, { value: 0 })}
</Text>
</Flex>
@@ -92,14 +92,14 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
>
{!hasCustomShell && (
<Text color="gray" size={{ initial: "1", sm: "2" }}>
{literals(strings.common.units.mm, [
(
{literals(strings.common.units.mm, {
value: (
resolvePenetrationCoefficient(
hasCalibratedShells,
antagonistShell.type
) * antagonistShell.penetration.near
).toFixed(0),
])}
})}
</Text>
)}
@@ -156,9 +156,9 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
secondaryDiscriminator={
<Text style={{ fontSize: "0.75em" }}>
{literals(strings.common.units.mm, [
gun.shells[0].caliber.toFixed(0),
])}
{literals(strings.common.units.mm, {
value: gun.shells[0].caliber.toFixed(0),
})}
</Text>
}
/>
@@ -135,7 +135,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
? strings.website.tools.tankopedia.tech_tree.battle
: literals(
strings.website.tools.tankopedia.tech_tree.battles,
[`${games}`]
{ count: games }
))}
</Text>
)}
@@ -158,7 +158,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
pl="1"
>
<Text size="2">
{literals(strings.common.units.x, [`${multiplier}`])}
{literals(strings.common.units.x, { value: multiplier })}
</Text>
</Flex>
);
@@ -218,10 +218,10 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
<CaretLeftIcon />
</IconButton>
<Text>
{literals(strings.website.tools.tankopedia.tech_tree.route, [
`${lineIndex + 1}`,
`${lines.length}`,
])}
{literals(strings.website.tools.tankopedia.tech_tree.route, {
index: lineIndex + 1,
count: lines.length,
})}
</Text>
<IconButton
size="2"