diff --git a/_package.json b/_package.json
index 03b0424f9..de8f5957c 100644
--- a/_package.json
+++ b/_package.json
@@ -54,7 +54,6 @@
"crc": "^4.3.2",
"dotenv": "^16.4.5",
"lz4js": "^0.2.0",
- "ts-proto": "^2.2.2",
"yaml": "^2.5.0",
"ts-deepmerge": "^7.0.2",
"@types/decompress": "^4.2.7",
@@ -69,34 +68,25 @@
"progress": "^2.0.3",
"puppeteer": "^23.10.4",
"@astrojs/check": "^0.9.4",
- "@astrojs/sitemap": "^3.2.1",
"@playform/compress": "^0.1.9",
"@playform/inline": "^0.1.2",
"@types/js-cookie": "^3.0.6",
- "@types/three": "^0.160.0",
"astro-compressor": "^1.0.0",
- "@astrojs/partytown": "^2.1.4",
"@fontsource/inter": "^5.2.5",
"@nanostores/persistent": "^0.10.2",
"@nanostores/react": "^0.7.3",
"@nivo/bar": "^0.87.0",
"@nivo/line": "^0.88.0",
- "@react-three/drei": "^9.115.0",
- "@react-three/fiber": "^8.17.10",
"compare-versions": "^6.1.1",
"echarts": "^5.5.1",
- "immer": "^10.1.1",
"js-cookie": "^3.0.5",
"markdown-to-jsx": "^7.5.0",
"nanostores": "^0.11.3",
"react-device-detect": "^2.2.3",
"react-promise-suspense": "^0.3.4",
- "three": "^0.169.0",
"urlon": "^3.1.0",
- "zustand": "^4.5.5",
"@types/bun": "latest",
"@types/node": "^22.14.1",
- "prettier": "^3.5.3",
"prisma": "^6.6.0",
"typescript": "^5.8.3",
"googleapis": "^144.0.0",
diff --git a/bun.lockb b/bun.lockb
index 80bdc4b51..88ec7b0d0 100644
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 8e3bb4dc3..edc98de5a 100644
--- a/package.json
+++ b/package.json
@@ -39,5 +39,8 @@
"build:ids": "cd packages/scripts && bun build:ids",
"prunestrings": "cd packages/scripts && bun prunestrings"
},
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "prettier": "^3.5.3"
+ }
}
diff --git a/packages/core/package.json b/packages/core/package.json
index fa0c8aa5b..f39be73b5 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -2,5 +2,8 @@
"name": "@blitzkit/core",
"private": true,
"type": "module",
- "main": "src/index.ts"
+ "main": "src/index.ts",
+ "dependencies": {
+ "ts-proto": "^2.2.2"
+ }
}
diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json
index caa25ce0f..3cc2d7809 100644
--- a/packages/i18n/strings/en.json
+++ b/packages/i18n/strings/en.json
@@ -1254,6 +1254,7 @@
},
"gallery": {
"name": "Gallery",
+ "unnamed_avatar": "Unnamed avatar",
"button": "Browse all",
"description": "Blitz player avatars",
"seo_description": "All World of Tanks Blitz player avatars",
diff --git a/packages/website-ue/astro.config.mjs b/packages/website-ue/astro.config.mjs
index 4e39c0f77..75acc1237 100644
--- a/packages/website-ue/astro.config.mjs
+++ b/packages/website-ue/astro.config.mjs
@@ -3,7 +3,6 @@ import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
-// https://astro.build/config
export default defineConfig({
integrations: [react()],
devToolbar: { enabled: false },
diff --git a/packages/website-ue/package.json b/packages/website-ue/package.json
index cf10d08e9..34316915e 100644
--- a/packages/website-ue/package.json
+++ b/packages/website-ue/package.json
@@ -8,22 +8,30 @@
"astro": "astro"
},
"dependencies": {
+ "@astrojs/partytown": "^2.1.4",
"@astrojs/react": "^4.3.0",
+ "@astrojs/sitemap": "^3.2.1",
"@astrolib/seo": "^1.0.0-beta.8",
"@blitzkit/closed": "workspace:*",
"@blitzkit/core": "workspace:*",
"@radix-ui/colors": "^3.0.0",
- "@radix-ui/react-icons": "^1.3.0",
+ "@radix-ui/react-icons": "^1.3.2",
"@radix-ui/themes": "^3.1.4",
+ "@react-three/drei": "^9.115.0",
+ "@react-three/fiber": "^8.17.10",
"@types/js-yaml": "^4.0.9",
"@types/lodash-es": "^4.17.12",
"@types/react": "^19.1.5",
"@types/react-dom": "^19.1.5",
+ "@types/three": "^0.160.0",
"astro": "^5.8.0",
+ "immer": "^10.1.1",
"js-yaml": "^4.1.0",
"lodash-es": "^4.17.21",
"minimatch": "^10.0.1",
"react": "^19.1.0",
- "react-dom": "^19.1.0"
+ "react-dom": "^19.1.0",
+ "three": "^0.169.0",
+ "zustand": "^4.5.5"
}
}
diff --git a/packages/website-ue/src/pages/[...locale]/gallery/_index.tsx b/packages/website-ue/src/pages/[...locale]/gallery/_index.tsx
index 38c6301a0..e14927f57 100644
--- a/packages/website-ue/src/pages/[...locale]/gallery/_index.tsx
+++ b/packages/website-ue/src/pages/[...locale]/gallery/_index.tsx
@@ -1,11 +1,17 @@
import { ProfileAvatarComponent } from '@protos/blitz_static_profile_avatar_component';
import { StuffUIComponent } from '@protos/blitz_static_stuff_ui_component';
import { BlitzkitAllAvatarsComponent } from '@protos/blitzkit_static_all_avatars_component';
-import { Flex, Text } from '@radix-ui/themes';
+import { Badge, Box, Flex, Grid, Text } from '@radix-ui/themes';
import { CatalogItemAccessor } from 'packages/core/src/blitz/catalogItemAccessor';
+import { PageWrapper } from 'packages/website-ue/src/components/PageWrapper';
import { metadata } from 'packages/website-ue/src/core/blitz/metadata';
import { Var } from 'packages/website-ue/src/core/radix/var';
-import type { LocaleAcceptorProps } from 'packages/website-ue/src/hooks/useLocale';
+import {
+ LocaleProvider,
+ useLocale,
+ type LocaleAcceptorProps,
+} from 'packages/website-ue/src/hooks/useLocale';
+import { useMemo } from 'react';
const allAvatars = await metadata
.get('BlitzkitAllAvatarsEntity.blitzkit_all_avatars')
@@ -17,40 +23,107 @@ const allAvatars = await metadata
export function Page({ localeData }: LocaleAcceptorProps) {
return (
-
- {allAvatars.map((avatar) => {
- const item = new CatalogItemAccessor(avatar);
+
+
+
+ );
+}
+const dx = '4px';
+const MAX_AVATARS_PREVIEW = 5;
+
+function Content() {
+ const { gameStrings, strings } = useLocale();
+ const groups = useMemo(() => {
+ const avatars = allAvatars
+ .map((avatar) => {
+ const item = new CatalogItemAccessor(avatar);
const stuffComponent = item.get(StuffUIComponent, 'UIComponent');
const avatarComponent = item.get(
ProfileAvatarComponent,
'profileAvatarComponent',
);
+ const name =
+ gameStrings[stuffComponent.display_name] ??
+ strings.website.tools.gallery.unnamed_avatar;
- return (
-
-
- {stuffComponent.display_name}
-
- );
- })}
-
+ return { avatar, name, stuffComponent, avatarComponent };
+ })
+ .sort((a, b) => a.stuffComponent.grade - b.stuffComponent.grade)
+ .sort((a, b) => a.name.localeCompare(b.name));
+ const groups: Record = {};
+
+ for (const avatar of avatars) {
+ if (avatar.name in groups) {
+ groups[avatar.name].push(avatar);
+ } else {
+ groups[avatar.name] = [avatar];
+ }
+ }
+
+ return Object.entries(groups);
+ }, []);
+
+ return (
+
+
+ {groups.map(([name, avatars]) => {
+ const avatarsReduced = [...avatars]
+ .reverse()
+ .slice(0, MAX_AVATARS_PREVIEW);
+
+ return (
+
+
+ {avatarsReduced.map((avatar, index) => (
+
+ ))}
+
+ {avatars.length > 1 && (
+
+
+ x{avatars.length}
+
+
+ )}
+
+
+ {name}
+
+ );
+ })}
+
+
);
}