diff --git a/bun.lockb b/bun.lockb
index e1f371ddd..f554250ad 100644
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index ce7ce2fbe..ca9008dd1 100644
--- a/package.json
+++ b/package.json
@@ -8,8 +8,8 @@
"packages/*"
],
"scripts": {
- "build:assets": "bun scripts/buildAssets/index.ts",
- "build:averages": "bun scripts/averages",
+ "build:assets": "bun --filter=@blitzkit/scripts run build:assets",
+ "build:averages": "bun --filter=@blitzkit/scripts run build:averages",
"build:bot": "rm -rf dist/bot && mkdir -p dist/bot/prisma && bun build src/index.ts src/workers/bot.ts src/workers/render.ts --outdir dist/bot --target=bun --splitting --sourcemap=inline --external sharp && cp -R src/lang dist/bot/lang && prisma generate && cp prisma/schema.prisma dist/bot/prisma/schema.prisma && cp -R node_modules/prisma/*.node dist/bot/prisma && cd dist/bot && bun init -y && bun add sharp",
"build:website": "prisma generate & bun build:sitemap && next build",
"build:sitemap": "bun scripts/sitemap.ts",
@@ -17,7 +17,8 @@
"build:ratingleaderboard": "bun --script scripts/buildRatingLeaderboard.ts",
"dev:bot": "bun build:bot && bun start:bot",
"dev:docs": "vitepress dev docs",
- "dev:website": "next dev",
+ "dev:website": "bun --filter=@blitzkit/website run dev",
+ "dev:api": "bun --filter=@blitzkit/api run dev",
"start:bot": "bun dist/bot/index.js",
"start:website": "next start",
"host:docs": "vitepress preview docs",
diff --git a/packages/api/next-env.d.ts b/packages/api/next-env.d.ts
new file mode 100644
index 000000000..40c3d6809
--- /dev/null
+++ b/packages/api/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/packages/api/next.config.js b/packages/api/next.config.js
new file mode 100644
index 000000000..4678774e6
--- /dev/null
+++ b/packages/api/next.config.js
@@ -0,0 +1,4 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {};
+
+export default nextConfig;
diff --git a/packages/api/package.json b/packages/api/package.json
new file mode 100644
index 000000000..071a58790
--- /dev/null
+++ b/packages/api/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@blitzkit/api",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build"
+ },
+ "dependencies": {
+ "react": "^18",
+ "react-dom": "^18",
+ "next": "14.2.8"
+ },
+ "devDependencies": {
+ "@types/react": "^18",
+ "@types/react-dom": "^18"
+ }
+}
diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json
new file mode 100644
index 000000000..4c3b321ed
--- /dev/null
+++ b/packages/api/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "plugins": [{ "name": "next" }]
+ },
+ "include": ["src/**/*", "dist/website/types/**/*.ts"]
+}
diff --git a/packages/bot/package.json b/packages/bot/package.json
index 6f5e0d645..626d16645 100644
--- a/packages/bot/package.json
+++ b/packages/bot/package.json
@@ -7,6 +7,7 @@
"satori": "^0.10.14",
"@resvg/resvg-js": "^2.6.2",
"discord.js": "^14.15.3",
+ "react": "^18.3.1",
"@radix-ui/colors": "^3.0.0",
"fuzzysort": "^2.0.4",
"bepaint": "^0.1.0-beta.3",
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index 362df4984..7c7ff9d4d 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -2,6 +2,10 @@
"name": "@blitzkit/scripts",
"private": true,
"type": "module",
+ "scripts": {
+ "build:assets": "bun src/buildAssets/index.ts",
+ "build:averages": "bun src/averages.ts"
+ },
"dependencies": {
"progress": "^2.0.3",
"@gltf-transform/core": "^3.10.1",
diff --git a/packages/website/next.config.js b/packages/website/next.config.js
index 08d77937f..98415da83 100644
--- a/packages/website/next.config.js
+++ b/packages/website/next.config.js
@@ -4,6 +4,8 @@ const withVanillaExtract = createVanillaExtractPlugin({ tests: /\.css$/ });
/** @type {import('next').NextConfig} */
const nextConfig = {
+ output: 'export',
+
...withVanillaExtract({
webpack: (config) => {
config.module.rules.push(
@@ -21,15 +23,6 @@ const nextConfig = {
},
}),
- async headers() {
- return [
- {
- source: '/(.*)?',
- headers: [{ key: 'X-Frame-Options', value: 'DENY' }],
- },
- ];
- },
-
distDir: 'dist/website',
reactStrictMode: true,
diff --git a/packages/website/package.json b/packages/website/package.json
index 4ca157b56..ae6c64684 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -3,7 +3,8 @@
"private": true,
"type": "module",
"scripts": {
- "dev": "next dev"
+ "dev": "next dev",
+ "build": "next build"
},
"dependencies": {
"@radix-ui/themes": "^3.1.3",
@@ -31,6 +32,7 @@
"react-promise-suspense": "^0.3.4"
},
"devDependencies": {
+ "@types/react-dom": "^18",
"@types/lodash": "^4.17.7",
"@types/three": "^0.160.0",
"@vanilla-extract/next-plugin": "^2.4.5",
diff --git a/packages/website/src/app/tools/tankopedia/[id]/components/Content.tsx b/packages/website/src/app/tools/tankopedia/[id]/components/Content.tsx
new file mode 100644
index 000000000..fa5534f76
--- /dev/null
+++ b/packages/website/src/app/tools/tankopedia/[id]/components/Content.tsx
@@ -0,0 +1,107 @@
+'use client';
+
+import { TIER_ROMAN_NUMERALS } from '@blitzkit/core';
+import strings from '@blitzkit/core/lang/en-US.json';
+import { invalidate } from '@react-three/fiber';
+import { useEffect } from 'react';
+import { AdMidSectionResponsive } from '../../../../../components/AdMidSectionResponsive';
+import PageWrapper from '../../../../../components/PageWrapper';
+import { useAdExempt } from '../../../../../hooks/useAdExempt';
+import * as Duel from '../../../../../stores/duel';
+import * as TankopediaEphemeral from '../../../../../stores/tankopediaEphemeral';
+import { CalloutsSection } from './CalloutsSection';
+import { HistorySection } from './HistorySection';
+import { CharacteristicsSection } from './Model/CharacteristicsSection';
+import { GameModeSection } from './Model/GameModeSection';
+import { HeroSection } from './Model/HeroSection';
+import { MetaSection } from './Model/MetaSection';
+import { TechTreeSection } from './Model/TechTreeSection';
+import { ShotDisplaySection } from './ShotDisplaySection';
+import { VideoSection } from './VideoSection';
+
+export function Content() {
+ const exempt = useAdExempt();
+ const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation();
+ const mutateDuel = Duel.useMutation();
+ const duelStore = Duel.useStore();
+ const tank = Duel.use((state) => state.protagonist.tank);
+ const title = `${tank.name} - Tier ${TIER_ROMAN_NUMERALS[tank.tier]} ${
+ (strings.common.nations_adjectives as Record)[tank.nation]
+ } ${strings.common.tank_class_short[tank.class]}`;
+
+ useEffect(() => {
+ function handleKeyDown(event: KeyboardEvent) {
+ if (document.activeElement instanceof HTMLInputElement) return;
+
+ function wipeShot() {
+ mutateTankopediaEphemeral((draft) => {
+ draft.shot = undefined;
+ });
+ }
+
+ if (event.key === '1') {
+ invalidate();
+ wipeShot();
+ mutateDuel((draft) => {
+ draft.antagonist.shell = draft.antagonist.gun.shells[0];
+ });
+ } else if (event.key === '2') {
+ invalidate();
+ wipeShot();
+ mutateDuel((draft) => {
+ if (draft.antagonist.gun.shells[1]) {
+ draft.antagonist.shell = draft.antagonist.gun.shells[1];
+ }
+ });
+ } else if (event.key === '3') {
+ invalidate();
+ wipeShot();
+ mutateDuel((draft) => {
+ if (draft.antagonist.gun.shells[2]) {
+ draft.antagonist.shell = draft.antagonist.gun.shells[2];
+ }
+ });
+ }
+ }
+
+ window.addEventListener('keydown', handleKeyDown);
+
+ return () => {
+ window.removeEventListener('keydown', handleKeyDown);
+ };
+ }, []);
+
+ useEffect(() => {
+ const unsubscribe = duelStore.subscribe(
+ (state) => state.protagonist?.tank.id,
+ () => {
+ mutateTankopediaEphemeral((draft) => {
+ draft.shot = undefined;
+ });
+ },
+ );
+
+ return unsubscribe;
+ }, []);
+
+ return (
+ <>
+ {title}
+
+
+
+
+
+
+ {!exempt && }
+
+
+ {!exempt && }
+
+
+
+ {!exempt && }
+
+ >
+ );
+}
diff --git a/packages/website/src/app/tools/tankopedia/[id]/layout.tsx b/packages/website/src/app/tools/tankopedia/[id]/layout.tsx
index 68acdf667..11db19895 100644
--- a/packages/website/src/app/tools/tankopedia/[id]/layout.tsx
+++ b/packages/website/src/app/tools/tankopedia/[id]/layout.tsx
@@ -2,6 +2,7 @@ import {
modelDefinitions,
provisionDefinitions,
tankDefinitions,
+ tankDefinitionsArray,
tankIcon,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
@@ -10,6 +11,11 @@ import { ReactNode } from 'react';
import * as Duel from '../../../../stores/duel';
import * as TankopediaEphemeral from '../../../../stores/tankopediaEphemeral';
+export async function generateStaticParams() {
+ const tanks = await tankDefinitionsArray;
+ return tanks.map((tank) => ({ id: `${tank.id}` }));
+}
+
export default async function Layout({
children,
params,
diff --git a/packages/website/tsconfig.json b/packages/website/tsconfig.json
index 95694aa6c..a549a9571 100644
--- a/packages/website/tsconfig.json
+++ b/packages/website/tsconfig.json
@@ -9,6 +9,7 @@
},
"include": [
"src/**/*",
- "dist/website/types/**/*.ts"
+ "dist/website/types/**/*.ts",
+ ".next/types/**/*.ts"
]
}