Merge branch 'main' into contact-shadows-low-angles

This commit is contained in:
TrèsAbhi authored and GitHub committed 2026-10-07 16:35:43 -05:00
commit bc2dd8c7b7
30 files changed
+169 -55

No files matched your search

+5 -4
View File
@@ -27,9 +27,10 @@ import {
TextField,
} from "@radix-ui/themes";
import fuzzysort from "fuzzysort";
import { debounce, times } from "lodash-es";
import { debounce } from "lodash-es";
import { useCallback, useEffect, useRef, useState } from "react";
import { awaitableTankNames } from "../core/awaitables/tankNames";
import { awaitableTiers } from "../core/awaitables/tiers";
import { api } from "../core/blitzkit/api";
import { useLocale } from "../hooks/useLocale";
import { Guess, GuessState } from "../stores/guess";
@@ -38,9 +39,10 @@ import { SearchResults } from "./SearchResults";
const { go } = fuzzysort;
const [tankNames, tankDefinitions] = await Promise.all([
const [tankNames, tankDefinitions, TIERS] = await Promise.all([
awaitableTankNames,
api.tankDefinitions(),
awaitableTiers,
]);
const ids = Object.keys(tankDefinitions.tanks);
@@ -193,8 +195,7 @@ export function Guesser() {
</DropdownMenu.Trigger>
<DropdownMenu.Content>
{times(10, (index) => {
const tier = 10 - index;
{TIERS.map((tier) => {
const isSelected = tiers.includes(tier);
return (
@@ -25,8 +25,8 @@ import {
Tooltip,
type FlexProps,
} from "@radix-ui/themes";
import { times } from "lodash-es";
import { Fragment, type ComponentProps, type ReactNode } from "react";
import { awaitableTiers } from "../../../core/awaitables/tiers";
import { api } from "../../../core/blitzkit/api";
import { useLocale } from "../../../hooks/useLocale";
import { App } from "../../../stores/app";
@@ -171,7 +171,7 @@ const GUN_TYPES = Object.keys(
const MAX_ICONS = 4;
const TIERS = times(10, (i) => 10 - i);
const TIERS = await awaitableTiers;
export function FilterControl() {
return (
@@ -1,10 +1,12 @@
import { TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import { Flex, IconButton, Text } from "@radix-ui/themes";
import { times } from "lodash-es";
import { memo } from "react";
import { awaitableTiers } from "../../../core/awaitables/tiers";
import { TankFilters } from "../../../stores/tankFilters";
import { TankSort } from "../../../stores/tankopediaSort";
const TIERS = await awaitableTiers;
export const TierFilter = memo(() => {
const by = TankSort.use((state) => state.by);
const search = TankFilters.use((state) => state.search);
@@ -19,8 +21,7 @@ export const TierFilter = memo(() => {
overflow="hidden"
style={{ borderRadius: "var(--radius-full)" }}
>
{times(10, (index) => {
const tier = 10 - index;
{TIERS.map((tier) => {
const selected = tiers.includes(tier);
return (
@@ -13,6 +13,7 @@ import { modelTransformEvent } from "../../../../../../../core/blitzkit/modelTra
import { Var } from "../../../../../../../core/radix/var";
import { useLocale } from "../../../../../../../hooks/useLocale";
import { Duel } from "../../../../../../../stores/duel";
import { Tankopedia } from "../../../../../../../stores/tankopedia";
import type { MaybeSkeletonComponentProps } from "../../../../../../../types/maybeSkeletonComponentProps";
import { VisualizerCard } from "../VisualizerCard";
import { VisualizerCornerStat } from "../VisualizerCornerStat";
@@ -43,6 +44,7 @@ export function GunFlexibilityVisualizer({
const tank = Duel.use((state) => state.protagonist.tank);
const turret = Duel.use((state) => state.protagonist.turret);
const gun = Duel.use((state) => state.protagonist.gun);
const modelRequested = Tankopedia.use((state) => state.modelRequested);
const tankModel = modelDefinition.models[tank.id];
const turretModel = tankModel.turrets[turret.id];
@@ -193,7 +195,7 @@ export function GunFlexibilityVisualizer({
top="0"
left="0"
>
{!skeleton && <FlexibilityCanvas />}
{!skeleton && modelRequested && <FlexibilityCanvas />}
<Box
position="absolute"
@@ -77,6 +77,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
const revealed = Tankopedia.use((state) => state.revealed);
const disturbed = Tankopedia.use((state) => state.disturbed);
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
const modelRequested = Tankopedia.use((state) => state.modelRequested);
const equalize = Duel.use((state) => state.equalize);
const equalizer = Duel.use((state) => state.antagonist.tank.equalizer);
@@ -283,7 +284,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
<SpallLinerSwitcher />
</Flex>
{!skeleton && (
{!skeleton && modelRequested && (
<Suspense>
<DynamicArmorSwitcher />
</Suspense>
@@ -1,11 +1,14 @@
import { Flex, Heading } from "@radix-ui/themes";
import { UpdateIcon } from "@radix-ui/react-icons";
import { Button, Flex, Heading } from "@radix-ui/themes";
import { useLocale } from "../../../../../../hooks/useLocale";
import { Duel } from "../../../../../../stores/duel";
import { Tankopedia } from "../../../../../../stores/tankopedia";
import type { MaybeSkeletonComponentProps } from "../../../../../../types/maybeSkeletonComponentProps";
import { Tracker } from "./components/Tracker";
export function Title() {
const { unwrap } = useLocale();
export function Title({ skeleton }: MaybeSkeletonComponentProps) {
const { unwrap, strings } = useLocale();
const modelRequested = Tankopedia.use((state) => state.modelRequested);
const protagonist = Duel.use((state) => state.protagonist.tank);
const revealed = Tankopedia.use((state) => state.revealed);
const disturbed = Tankopedia.use((state) => state.disturbed);
@@ -21,6 +24,8 @@ export function Title() {
onPointerDown={(event) => {
event.preventDefault();
if (!modelRequested) return;
Tankopedia.mutate((draft) => {
draft.revealed = true;
});
@@ -60,7 +65,36 @@ export function Title() {
{name}
</Heading>
{!revealed && <Tracker fontSize={fontSize} />}
{!skeleton && !revealed && modelRequested && (
<Tracker fontSize={fontSize} />
)}
{!skeleton && !modelRequested && (
<Flex
position="absolute"
left="50%"
top="50%"
width="min(100%, 16rem)"
justify="center"
style={{
transform: `translate(-50%, calc(${fontSize} * 0.75 + var(--space-4) - 50%))`,
}}
>
<Button
variant="solid"
highContrast
onPointerDown={(event) => event.stopPropagation()}
onClick={() => {
Tankopedia.mutate((draft) => {
draft.modelRequested = true;
});
}}
>
<UpdateIcon />
{strings.website.tools.tankopedia.sandbox.load_model}
</Button>
</Flex>
)}
</Flex>
);
}
@@ -1,6 +1,6 @@
import { Box, Flex } from "@radix-ui/themes";
import { times } from "lodash-es";
import { useEffect, useMemo, useRef } from "react";
import { Suspense, useEffect, useMemo, useRef } from "react";
import { api } from "../../../core/blitzkit/api";
import { defaultEqualizer } from "../../../core/blitzkit/tankToDuelMember";
import { useFullScreen } from "../../../hooks/useFullScreen";
@@ -20,6 +20,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
const canvas = useRef<HTMLCanvasElement>(null!);
const isFullScreen = useFullScreen();
const protagonist = Duel.use((state) => state.protagonist.tank);
const modelRequested = Tankopedia.use((state) => state.modelRequested);
const thicknessRange = useMemo(() => {
const entries = Object.values(tankDefinitions.tanks);
const filtered = entries.filter(
@@ -131,13 +132,15 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
top="0"
left="0"
>
{!skeleton && (
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
{!skeleton && modelRequested && (
<Suspense>
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
</Suspense>
)}
</Box>
</Box>
<Title />
<Title skeleton={skeleton} />
<Options
skeleton={skeleton}
@@ -127,7 +127,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
</Text>
</Flex>
{averages && nextTanks && tank.tier !== 10 && (
{averages && nextTanks && (
<Text color="gray" size="1" mt="2">
{skeleton && <Skeleton height="1em" width="4em" />}
{!skeleton &&
@@ -191,7 +191,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
);
})}
{master.tier < 10 && (
{master.successors.length > 0 && (
<>
<Arrow />
{master.successors!.map((id, index) => (