From 86e259a5009756cce8aa7ee96ddc45a6e33391e0 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Mon, 11 May 2026 07:00:05 +0800 Subject: [PATCH] feat: add legal tabs and update header assets Switch the legal page to tabbed EULA/privacy content with URL sync, update header styling, and migrate footer icons to Ant Design. Co-Authored-By: Claude Opus 4.7 --- package.json | 3 + pnpm-lock.yaml | 173 +++++++++++++++++++++ public/nav_bg.png | Bin 19552 -> 0 bytes src/api/firearm-api.ts | 10 +- src/components/markdown-renderer/index.tsx | 17 ++ src/index.css | 3 +- src/layout/hero-layout/index.tsx | 145 ++++++++--------- src/markdown.d.ts | 6 + src/page/legal/index.tsx | 34 ++++ src/router/index.tsx | 4 + tailwind.config.ts | 10 ++ vite.config.ts | 3 +- 12 files changed, 324 insertions(+), 84 deletions(-) delete mode 100644 public/nav_bg.png create mode 100644 src/components/markdown-renderer/index.tsx create mode 100644 src/markdown.d.ts create mode 100644 src/page/legal/index.tsx create mode 100644 tailwind.config.ts diff --git a/package.json b/package.json index d7411c5..5807689 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@ant-design/cssinjs": "^2.1.2", + "@ant-design/icons": "^6.2.2", "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.2.4", "@tanstack/react-virtual": "^3.13.24", @@ -27,6 +28,7 @@ "tailwindcss": "^4.2.4" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.19", "@types/node": "^22.19.17", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", @@ -35,6 +37,7 @@ "prettier": "^3.8.3", "typescript": "~6.0.3", "vite": "^8.0.11", + "vite-plugin-markdown": "^2.2.0", "vite-plugin-port-checker": "^1.0.1" }, "pnpm": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9796d18..3751038 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@ant-design/cssinjs': specifier: ^2.1.2 version: 2.1.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@ant-design/icons': + specifier: ^6.2.2 + version: 6.2.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@reduxjs/toolkit': specifier: ^2.11.2 version: 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.6)(redux@5.0.1))(react@19.2.6) @@ -51,6 +54,9 @@ importers: specifier: ^4.2.4 version: 4.2.4 devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.19 + version: 0.5.19(tailwindcss@4.2.4) '@types/node': specifier: ^22.19.17 version: 22.19.17 @@ -75,6 +81,9 @@ importers: vite: specifier: ^8.0.11 version: 8.0.11(@types/node@22.19.17)(jiti@2.6.1) + vite-plugin-markdown: + specifier: ^2.2.0 + version: 2.2.0(vite@8.0.11(@types/node@22.19.17)(jiti@2.6.1)) vite-plugin-port-checker: specifier: ^1.0.1 version: 1.0.1(vite@8.0.11(@types/node@22.19.17)(jiti@2.6.1)) @@ -656,6 +665,11 @@ packages: resolution: {integrity: sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==} engines: {node: '>= 20'} + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@tailwindcss/vite@4.2.4': resolution: {integrity: sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==} peerDependencies: @@ -706,6 +720,12 @@ packages: react: '>=18.0.0' react-dom: '>=18.0.0' + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -731,6 +751,11 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -745,6 +770,19 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -753,6 +791,12 @@ packages: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} + entities@2.1.0: + resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -769,6 +813,11 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -791,6 +840,9 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + front-matter@4.0.2: + resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -830,6 +882,9 @@ packages: resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + immer@11.1.4: resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} @@ -840,6 +895,10 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + json2mq@0.2.0: resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} @@ -917,13 +976,23 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + linkify-it@3.0.3: + resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + markdown-it@12.3.2: + resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -944,6 +1013,10 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss@8.5.14: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} @@ -1036,6 +1109,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + string-convert@0.2.1: resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} @@ -1065,6 +1141,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@1.0.6: + resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -1073,6 +1152,14 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-plugin-markdown@2.2.0: + resolution: {integrity: sha512-eH2tXMZcx3EHb5okd+/0VIyoR8Gp9pGe24UXitOOcGkzObbJ1vl48aGOAbakoT88FBdzC8MXNkMfBIB9VK0Ndg==} + peerDependencies: + vite: '>= 2.0.0' + vite-plugin-port-checker@1.0.1: resolution: {integrity: sha512-Mx/Pj5zyu4oak+SKGwQyRLGAvnp9zx0nqjCLhaJoGJeZk2t4EarZojTcHGG0gPMJWj9X6c0MIfzaZDslg80/GA==} peerDependencies: @@ -1702,6 +1789,11 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.4 '@tailwindcss/oxide-win32-x64-msvc': 4.2.4 + '@tailwindcss/typography@0.5.19(tailwindcss@4.2.4)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 4.2.4 + '@tailwindcss/vite@4.2.4(vite@8.0.11(@types/node@22.19.17)(jiti@2.6.1))': dependencies: '@tailwindcss/node': 4.2.4 @@ -1798,6 +1890,12 @@ snapshots: - luxon - moment + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + asynckit@0.4.0: {} axios@1.16.0: @@ -1823,6 +1921,8 @@ snapshots: cookie@1.1.1: {} + cssesc@3.0.0: {} + csstype@3.2.3: {} dayjs@1.11.20: {} @@ -1831,6 +1931,24 @@ snapshots: detect-libc@2.1.2: {} + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -1842,6 +1960,10 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + entities@2.1.0: {} + + entities@2.2.0: {} + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -1857,6 +1979,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.3 + esprima@4.0.1: {} + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -1871,6 +1995,10 @@ snapshots: hasown: 2.0.3 mime-types: 2.1.35 + front-matter@4.0.2: + dependencies: + js-yaml: 3.14.2 + fsevents@2.3.3: optional: true @@ -1910,12 +2038,24 @@ snapshots: dependencies: function-bind: 1.1.2 + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + immer@11.1.4: {} is-mobile@5.0.0: {} jiti@2.6.1: {} + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + json2mq@0.2.0: dependencies: string-convert: 0.2.1 @@ -1969,12 +2109,26 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + linkify-it@3.0.3: + dependencies: + uc.micro: 1.0.6 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-it@12.3.2: + dependencies: + argparse: 2.0.1 + entities: 2.1.0 + linkify-it: 3.0.3 + mdurl: 1.0.1 + uc.micro: 1.0.6 + math-intrinsics@1.1.0: {} + mdurl@1.0.1: {} + mime-db@1.52.0: {} mime-types@2.1.35: @@ -1987,6 +2141,11 @@ snapshots: picomatch@4.0.4: {} + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss@8.5.14: dependencies: nanoid: 3.3.12 @@ -2074,6 +2233,8 @@ snapshots: source-map-js@1.2.1: {} + sprintf-js@1.0.3: {} + string-convert@0.2.1: {} stylis@4.3.6: {} @@ -2094,12 +2255,24 @@ snapshots: typescript@6.0.3: {} + uc.micro@1.0.6: {} + undici-types@6.21.0: {} use-sync-external-store@1.6.0(react@19.2.6): dependencies: react: 19.2.6 + util-deprecate@1.0.2: {} + + vite-plugin-markdown@2.2.0(vite@8.0.11(@types/node@22.19.17)(jiti@2.6.1)): + dependencies: + domhandler: 4.3.1 + front-matter: 4.0.2 + htmlparser2: 6.1.0 + markdown-it: 12.3.2 + vite: 8.0.11(@types/node@22.19.17)(jiti@2.6.1) + vite-plugin-port-checker@1.0.1(vite@8.0.11(@types/node@22.19.17)(jiti@2.6.1)): dependencies: vite: 8.0.11(@types/node@22.19.17)(jiti@2.6.1) diff --git a/public/nav_bg.png b/public/nav_bg.png deleted file mode 100644 index 582155cb61764d9882a9537295c9c0dc94bc09d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19552 zcmV(%K;pkrNk&FkOaK5^MM6+kP&gn=OaK6I!UCNEDgY0&06rNEg+d_^NIVWA2mr!b zTaz`J+|OwM^iw+Jw*u3TTe7}hTWsxh#P>41J5OVqb2@?_B!%f8N3z|Lyzx?C<1X`~Uy{@9Q7j=>P2B;J^O$ zPqLT)zQ6t3o-bFR5&!>tMt+Z><@^9cvu`}i&M`@6k(_3)tJ=ZJ=|;zq6e3Q|JgeYg zf+}uhNLvYP-c^5J*Y*8;3@}DjvvVp}I?l7K*425(PcNNkS=M!$*{I5=B=$TXTa4<; zvaG26%|#Ek>6&+YX9i1!h|``Tb8tHk!e?Qgg8udMEd(sfJ76K0OMlF}@^P29gONKw zo^L0~@*>EisUVJ(TfGoO>22KiJN5n%&9 zxGes{o_*RMb4tz^3jHT*>iWK~tLpl`t(kGv@EpfWT9|$xhvE2s6F_KR+z~Z6W`5k@ zsm$(*mtF!X_d3qAtk%pWN}?#)hF%YvZ}F`@aTLhgs0_sHt%=gqiT3uX3(Uh&@vjV? zKb(*o664MiIeVioo2Sb;?3|$vx2j;1FP!U&&XKbtTme2>0BKgU|s** z@n!|EPG4|@O+wo*e{A5&)yW}JHm$2?0US}X+AHn_HorXUI)YR2cS)MrZ$IG^1>Oqd zZmat4+L&YOX0qFjyRdo7kE3L~(ir4}{k&)L>>SGoDplWNvtnLzN|YmpQbkrE z(Z-i0pxx1LmvZN8lCxq~YcC!^0>JAS9bLI7%`E^Wpb06cW&!2P0F7O_cuN9Yto)ZV z<#}xMg|+B4Qh}NUn zIwsuEsD8Ro^a(q7TqRhm>6a1P}hFib?TVGwlWW9vJCNA8|r* zWAahIUNjlhUvm?IK9=rp33sV9@U4*z({Tfr;Y9tPFQ z&~sg=@?~rCy(i~u+qGy8RI!T=XrY&+d^8dT43{*3&SqFMa`A9%_1Z9(-jRh+3oJFL zYa28!dM5{8wf2P^35V-PgqyUNHuiCV&!zqEFgqGcwnVM8;2Qyof8|c>eZK9vt9^7h zu-iL)ipT7|V$SNex2?tC=hRd|l;Zud~#6=~{Vw*o_&xnNJ$))Y$Em^@ zuqwl(zkcV2tFyy}HiGfR|NCxLFi!UMc|29S-GX|ZrdLb<9CCZPI!NZ4^z24e=MHhn z0`9C+E8~cr@lf6-$3WRhE{=z`l&GLz{-NA_51xqBpv_PzrAl~RBg-1m=?w8RZp?KgGzC+vIow_MM9$qO%?gH*>79%^do3h(km{}LpUD} zrOAJ&Yb@Ll_<{CWC#Fihu3+O#60+sEQeG~|-b|E8^R39sFc@4IAwVMYrhVr|dGLyf zzY-pCIPp)#mE2rGD{`sdxl1J9P0w6pp2M!>H{bim^XMVOLn(3DBeel#s6Hp6LhAu) zcK@bVb+}fDOi8@nh%AQ{=wsOxjtDcHjV-R8*THuyoPs>rynO?ZtJ@kG^N06}`uX|l zPIyjY%&GG6D*Bz(y%UcmP8^>!1nKcibOHV?ok$n6uSe6fM?C|AO

1iiMINR5zrQ=_=Rr5 z^Cdp-Jn1;?AXJd7HxlhTAVo?Zbo&{EeynS%0ezxT-VA^J=aplNm(8w|1XO>7aR9IA z71YSF=IthxK5Ptp1y1?v%-bJSfmR?8rV5tQnI)`Zk!kVD<)BalvX3tj3*nPKqcFbX zV$3!*;^w318u2P!4az23GZ70pf^3j3Z|!xHKFU=@jYy~v;7h^^0?jBoPwB5(vhJF7 z7+1|dgS5PBmExRxJC*tabBAl{5RK$SY&o#=_i7DbQ4xOEMU1>4)z4(%&|qau3I<`T z{*=VC2$or@PjM*BE;2N#9lox6HWSESH?()X9OPHro-mjH2|+sC-XgVg60UKaW5~zeGp%(RHtjR9ikfvoHJBwSt?%II zaWXWVShzoPe3pY7VSD}=cyERIQT!({F>z^h@8VnJFRYjm_203uUY5VfFm$_gyI0{> zPm;%4WDv_*K`=QY{;`U866q0Zm>577+Z6d|dO&F6l+R)7;!mG8Xku<;s9V4kz3bVK z3A{oUB*G+yAa+GmBrRyvDaf~W;ng>H4w;j&t!`YK z8k&=KtiD=7`{p?bvvp%EqiJGlOUBITpH()$uzCN z12`$qYe8|>Bt zpOYU1YEV&hp9!VoSycjS)~d1BIPlqAJQ#sE*2tHcIgWs*5M0Wcrer_&lK_YMhN42S z=3eA4vBR?_WGP3maVVc;3vRYex+`&y%fvQCO$#5-4xH8lQvL!gmz59#6Kn6Q6@}D} zX?|M~+i{#>DuU6#cUEKb6y&WB45`_o|B~{Aj($)mo!u|xv(r-JA6`2)5U;Xg;M(9c zo=3%1F>+EElkP>r)A;v+X265w1?ZeFE(Q}^c}i|DO7ev&7w1kjndZJ#;6$61?l#O) z^wDb+(yZci*tw=nr{w_QyhXL@i)Ue^Hcu5@8$6J71&N~{EDmnTJQ(H-8PzR=a=)1;~ zbPNuDC_4#$UnQS$jH$(P$(bd)oE#cLg`}ZWuK9?BLO{V}h}h?X(EoeU)6>{UJU6B; zJ10J@$?pZ9|CIsX{-^lafAgJXcqjg+rmwg@`|mAELAwzbNuiE1FH@ z;J}0suT3Jp7)KV2u?W;_rBr$MtB*_)sVA-v`I*$MV(*I)%nGJlJFlmOI=GZEG3As> zaXqhTn_cJFihK)?Sjs2GN=B&0^mH7x9N!95 zNAI{kv3>Yht}UuXX^l;l+t#`pm!+wBv_R>~zuHhuM{MzQk^o#e$FvP7-_>7xll6A# zcMzT&{8Gt;Tx&a7jG=i!ggc7&;F}=N)@@r-exe{+VT{)KpoS0E;LGp#(;pt0=6#@u z^=YA?1C>jdz*=K_;{?QVtHo z?DbA92SS*RJN=5#b8^WWofP|&am^+64jl2R;P}~mC}QYnE~RqKcwNU^qm~!-N%;9@ z9FbZQJ(6e4HR5Ex5NhM|Q=kxT%)-%0!4&8XgLGQ%K1aaaE=Z+QHM>)`a*!5T^}7W} zt59+k#-E0yL~xLC1qQ6Q%i-afktuHL zN2W<$x<>S>R|WTy&J9<&MM6lp1)bsv=OfYf@t=;vL8@lRmkHc@i!yCRyM~EdkT=%4 zaK%`Avu~j(e}O&L)nvP{f!@v%Qxp@~(p*}Fkwf!>F6H9l#66lIY^pZ*Iyefu@}nw^ z^s@5V^`S>LY#7kztSustq!_G+=$t8v z6p1ZJ(KVanrjJqk6W=jPFKKiDRChM_3@fG1lPyB+2TN&`sKHNNtJBuhyaaS_6$hS? zHCxs3b6?Q0ajb%@`Qp4mbbEXm3(3`n)ZP$p0Q(Z6D4&(;>fOEby|>hH%X3k6ldwpr zCR1^eAsv2EI#h}pv}_2m;Wz>xO8PGX?Ld34P#fc-N_#x6se_dXi0mEOi+}Zfhis08 z0vMk*(7ip;O*1%KKBx?iu1PEOo9xZWc?vS=yVeVRDPdv9Qt-*IsriPmmoF@ufzndiWOLW&AMpdOc0Vd zl8h_+Y-Q&lu5hD2pGWVr-+G^%r~FOW>JJLXg(Qef+`^+bewh?edH6NrJQVKqtgdP* zilU`4UwcJ#Wnl(M=>MJNH)G+x!Lh-heUjoj$O^hC7rAKoza2y^fXkH?cS?0Wk6z)$ z<$a>dc}*n^$-jR!#US5J+jh)dkfXU?(4B3B$zn9A>EkxwzZNj2N)CsGS&F2r?1`ztPq~64 z6lA}Pna=J_%RXW&`mFCWqM)-BU;o+CO=w2#d>WX;IOGYu%&GX0uMZCNr+@W1kZ?pSMB9}$3$F_prxin(Qmhe?99$rr13Ck_co;}zD(kt|GWPvsURGk(pmyUbykXMUz8 ztjG_YzR(#>=}@IUG;DVrQC*4)-#nZuo+PbNxKWb<;s3skVM&IxvtN$hR)DTZVEWfY zUCziSVS*jd9?Xv-y5JD4Qz>|%&K7a|)~JwEln8I+z}#x>d+{r!2oH|~C!IZQ(-KOi zUeH;)rXQx=e}%l}ZL$NF09WjzlM;5(nYCu4q&|k-lGmEvO#r^CjLhBov3f7P&l3=a zD5*s}JK(btbkF$tjQDve;{p;@1hdEB-X9_xttw}{`yjsW*H1a}{t;W+VnBU(#e=rL zRsdoJG&hRHscrPvDdDDq2xO}RkV!TGL=(qan2Zz=yNEzYpL*XFhu>@*`+V5fdJZ;c$vIfrY`e{bxX&<+7!1g&2_~nXJHjS@%6H(9#am` zqK(0t8HV}L@<3->VQ+Z%Fzb?mYGQ_dKvvgb@!4t;mHLnFz`j!q!~no>Ez=E(MUdjx_*;teUMRBXojhbJna7wK!=rr)0-DAOeTh1@8& z?Wp(02c2Mh&fWBX01V?X9J&Ai{{PI6TU)poZRE~`msa)u1*r%kqyE*0bv#`lf97`W z%>Kmt=j1uuQE6~A;o(~*vc`EhYNa)Ti@)r4Y_5s#4ofHeSB!YgL4XN_(b-U+kBmKF zqd8juzPD0p#H-Q_0uH;6LW0*(R!uTfXA4~n0$$I#-x>*ViX3aT^ywr>0(DF!D+L*a zaC=Y%`yyP3_z?1{zyJvou3Uh|f4?BM1rBP@6T00JEBUEQ3}M&Rrq<=Ye55E-vc*19g^pL={U zE_!#V?q~Y!+sE-uJ$}~yW&ZDY%KimQ{`Fvq_G03OX2-d<4g1q)=O)-CEz8L}ZVY=) zcSm7md~Osfz`DwK7fFxVD;oXnTF!ShI}WO#X&BG2qJ+q>iV^Z|0u2S82Y;J|{$r*uPvLN=CP$*7)YvP;5S20`u7 z%$ea4WU2$?2M`ohImk_tb8Y#`uH!}dR(s(bo|eBHNUiiVN%fa71E2Nv)*aJ{odcyy z?ez2^BWMVyJ{El&iWb3pjPSXWo>xbaoY%`ghY^<6w~9U+(=~v#Q#>rxtZShq@6h+F zTyq8OMZReTf!3pDTgESM@z-)R6jTj^W0$xy%$DZw71b|lzjTcdn_@79xTjU9Jt#@N)=~9sq{TC2d)^|)W#oU=28?4O$f~IX)Gz1Z6wkDTRwK9j z!qJZ$H7Nn}&k07sQw>DewLFaajf*2nfM)<7?SZMH&SW zl_ZY_!mu3me8>+DiP>emusq74WBNSPqLKq;;7J;T!+Mp!MntlA z1aS5b7DAg^2Nezb9|MVK41qX?>)vJCW3yfA5H>61z zrIKiK_%nxMI!;!ZUPSb59ik+l^%rGjVqjThj2(zkY{qe+PBTe*Fe+0y%|PH#6G}Hi zry5~kmWNUf#rYhf^nQB$J;d$gFl*v^xIWG@?$f708Y}y3uM_cv8&lgc>Kl*ae{!{l z-53w*VSK})wlzJEf16IT-ps5@;vkKmW9d4{;&De~EAp$W95|vx6Z~jzNj2z)x}5e; z=vW)mcKsPj3Su=A9s}HG9LG!3%@eIhP`C8mK)B8j)?u!5UAh{Sc1Y;9=(63n9Azu$ zwza0lP1jz&qci7wnu+(+#EE80lA!T$()x=Tw`}Md%v9H)`ozzSHqE9Uf7eUM{3t)f z$J_E1a5ybW)MQy@_ZEw&15DJTA-IVF0sU{$Tkrq?WXleqF~cyqQmB4GPoW^NXXk~n zu$rmBT0?u#QG>>RE&soc8e`s!cr&FlFbB-OV#a{3kTr-+9-_~AHiY=oLphLXK^?yV zR#_@NuKbR2FS$$3t9)8T0P;KVGWefbz(2jnFQV6aTd0*~eqny3`X%SHr`FyWmesuU8cnZPa-J3A01jiu~76a*9w< z@7xIWbRog_Ac*_A$|VUHbKdyE|QjZ3jMHPfpKwMakG1kG;7>;Jfd%Q{>zG& zwcT&>%awsbkxzb6fYLvaM#l*nDH|aG^%`V*so%+RbMZ0Sw_+J zN4Lm<_l+SIrf1b;aXkjCxW2E`%B*9X=NPL%P`NRgDoX#GFC|jkF-m#L2(JGppo$eP*|No`bN}xfVBaMLJrm?>Dd^9~hC;1^WnDx_g|WFdt9ycr)jl?NN)oCf zig{Ujh7$CM?qFRPJW4yzVs>*}WlVA-S`YC=U6%K#b*Z(Rton6E`BtlWBtvqY<)vaC zvMWnIiQJHtC9mycioFrj8sjLwZKt%=8f&TZT( z#iLmPRT*n9OZelBK-U(4ERq3v(>WIDXIq29x~Qaj_xZE}ceVB}s~VCm0BNRY^25^3 z5IPF`2IHFMk*c{|A>z3k1(e5*RFJGe?5!3;B$5X%bypj0c03Obj@48Zw{TPhLD7;h zkd4sgS82?Kg~NFJGMgd&DV`CEv1WF-5J%JqTj(jLl?oXkc~%F&ckGbsrkpF}wSSh* zr?`xWZvdpRV?b&I8fCWBn%VV7ScY%5BT-v>S9PNOoI!!=k^pJzOuVwO%fLH6fcCq_ zZ_p0=w1<=QoU*vWq#8QkM%PZxh@QZ~asAQY4u)GYgP!&l z-!$>LuhG>lfqh%1L`Hn=W`G7xa+oETqXc*M@^~t_wUx55`Dh>skC%656xB6F2FU~O ziTlQ1Euj{tx1_G}#+<~KfJw7B$!t()Pf&7kXz#6;yRMRk1>5E(k=z;@AYQ+HuAm%}g4A2e9khe=fe%*6T{ zRnLQn7WQ>1!918zj(Lq}FQe~vX2DLOlQjjs7lXw_5vQc`a6j=D@DvohwY_`#Jk3DC zV!7c56B4uX)QmjX)cC;DD`Hib)5(U8_(-wek}4YZizMl6(`m3c;JgL$?eSFlcmt8{ z1RSvq6LQV?)kr#nIvtqwi~LQ1l(51 z`yoGrD)DewX`xdPQ~A)m58=pUtYDFl;j8sjMeIpESf7fqRA6!fHXR_YW^YA?=EMG< zyQ_0Fb^|F@giMvoS@JV6jMY%o0NJ4Hr&orGq_dK{N?5v~+Q2I?Oscdwp-Fel|0}oi zs*y3(>nd%{LU20mhh<~@bHXyKe{BV6`-fnK0#VxT-02em9PU1ICpub^P`jY#q~l*g zN6zN~2~joV{~_cWx#Z$!eH(T>UGEfLlD(J!pqms`B(0DBjq}OdL}V}4&Qm90psc}SUB(mMi}X3UTsUac z?jDhH$>@eVz#dxuB}NvQ1+0a4OjoPT-V`D&Nb>?;2HvuPzax7wB!6Pwjv%$e%ErdF3X-% ztjMMncLqi-c^XLdxBvh|f%mklVHF-Jjf8%dTgIF!iM5&Z33}8Bbvnq(KtYo8nl^Kg z$S!eSsYhxm>pqN(Zdw8`ZyhO{vsS2eqWO0(p!qWqJJ#83S(M;{^5!h^k(zb z#0sfTWG-x-?VBv4Jf$S?Y(4?c(AN}y#9@3lpM?#9H0HMqkJxaa_MuXxS`zR!+%#=J zD$S6lQw{>jez!kn<9FI|Yoo~%t4t{YuyH9d&unPxLni#YZz+&`cEjpm(vuv6G)kv6 zr7?9rF~GGd_->l9+H#a34NVF5a>2n2IpzkOor|K1B$(ko`xv-zCXxB@LAM7b){P!I zhprHa=hcMF3JIsKM((ul+8|$N6P3590Q!JA)9=0nHe6>(Rq18Th5Xn=GQQ3&Ia%r) zq6GLwrkkb$n=on8jpxX&tUR9K)c`7Uho-VM@mXRd4l(S{D)5gR*YPzQss$BLdMngcr6uE)OHw5x~!&> z($2~1V3`qeg`@Q5runxF*pQ&&+;ARZG2EqJ{PO-A%NDz41;{jXok=Nw)^=ofj4QY0 zYuh5GiSK?ThSL=msAA+5pxY%Q3_7rCH2S+E=h?m*mlC$Gpd5TK1_hMF;4gew;7;xq z#~282EF`so0+)LMn|FZ67R{~K7&PJW$sB2k*8my<<5#>&{O*W9?H*1+0Ak88J`1ME z!`}1~K6S4s4KRUc+c?BN6+2UQ&wsJ=%|)WZm-U18jQC~6-OG3i&fOxsq89~Y~H@sNr&s*Z2lqgdw>bJX)kb1 z2tS)(;DRiX6xaZm;snWGwZ$SL30f37z~%@HwalMcFW{Z8V3(>osAN09(X;(2c@cWq zDe_7ni-5mzUlN#Py4P@y8b$EcA0c2wkl`8*~*0V&d1rIfH+9HC6$wbYSl znmAwU%%*+T)m$t~`+Oo*G|j~9O}r*OF+dU?Yh*4wxZKE{yn~CU6XE`Q-(ePf2Xmn; z)Xh+$B*fzQjku8TBq})5b~?+sm4;+&hWky$5QZ23G{z+>`<*rb>Y~`_yeBPW;#3V? z>p9v)el}9R-*n@6NtCX;$vipW@L{Z?l)Il9<0+@C_z38;=LRNdwc1UAC#t?f4(AEK zQ2$DaWN-HNOof~^(A6glW=n}PC*7zx6|P`??YpxPFY7AHvN%T0>n!OI+M^97p|L2>Q}fN@|&rg`{1~2AHQe=#pV}n^UB;ADm7m#{gqA*07RN zme8elHfClKO}q~}k44fcUK~{{XpOONi+7raGMwCpid8S+k5@28>(T)Tf5asxL!T{t z{KPwdMnUL4Z}8!lhcu==HnR>KjP=v+nT9g~D8V9{ivO`c0iueh=NYm~C%;ANjQ(;)FwV?mOFxFQV_gkk4M-}Av0H>MnHe1%uQ>MxeM}F!`0nGj57{|2O zN=86^@q?*`*1HfrnmVNy53Zvy$2gJbSx=2WMpc)+4t(^7fT2vu&aH_ui+NX0{OD$m?TS8CK*s>%f!4bE5GHLEb+TtWY9!=;qSgDlcv&MF98Nr{(=H#~D5d z5vUQe&*cB{wS2A?n3mN%uc{^ooWjB%^eufv51nK@y7L=yJ!wyhqw_`it9-VGMnGu+zyKVM_eF+h0@Y*i;v1F%+=c^ zT{B=jz`Oz>^bMGfB0@jfQ|!;wax`qi+Rg(>^ zpkuS*bj|PP=8F*aiu6{#?|6V7ehV#CF#vT+C)-NG8Dv1m5Rt~}JysEUPfW=X?VyJP z-L>yAC&(q~8kj(uU=>;uehNYmShkF(|2-?mMqHql(Ys`IVR+$Ho8i@huEZ1dkg8SyF5zX&1c_KN^F?eRqhNRvH1!aqW%71gm^I$r6~7 zbK!e4{v{VvcyVB~Qa)O4`Ck8Kzq{e@|*K;M}NHzUZe?rEtj zE7&x!vRY@i<~!%rxa9>W!g2lrto*vBbbPHnv#+#GL5`)aI<73hUKfCvuC_omYo{0` ztb}EnlmqGHQ6GsJ;!90?8FJna4dKVM;DM7{+4|#*cS`GEvg|7BGWC>8)APSRt3uAn z=dkHl%`Zu7YH)ol9_aK$v-PLbtS=#ePtZHD^5M7y-&t|#mCAvFF^ox0*eYu;(}7X* z%A`k0sb9uM3R*|S=~2z>bW%1Euj)2iCcv()Mst?S?(ELBlKu~IY*V${q$i`5agqfL zY4|E0jQtP&LHELZ_v*82tt6DcKir}I@3CUDT-)jjlm8a(W`x8oDw& z^T)f493ysOdcP*+8Z)e_X6#96YRgU&x#I#r4&xMh}K=~{4P5>mlQE_49@WQ zhjJVE5tkDGD~3OZYv=5=VR!%1ef0Gi^q9VR(NS&s52_{7NJCe{OLB-^f4h+oBCdH0 zrV+}B;*Q0Jh{*8f;#DiZR&4@G!wfozk8-M2O7kk=ud^E2HS!OUn}=VDRjDJeQ6X@S zw;ShMY#!NiaMwwE;`+3t4(lOR#&f%zKu~g*!KisjX|^QhJyV|+h;IVzS!gYWl`L^s z6UKIkrQt{Ns&&pu`sx3YuNo~B<%0-Mkui{>IWZP~kwsAw-E;IiA#KT2oQ`-d6?m~{BjLIGOqq`S__ zeECZ91~t26x6u^EC}d@_oAwZP3*veWS(BaG@2$og(TGSy%?D2pZTHnBt`VWB?kH%eXYU`JQsANltAFK!2(N5xj#2>dCf;@ zpJL-qBsd6VHcLwD4Ns}&(rtxj@!%B_>K-ahRMA`(_y$56#Bp`Rt2j0a#aPpLhFPUq zbu-{hvrS~MTI$JG=d=N^{07WI5EiNZ>Ta)g(mg40g>+SGfCNFi0b z(E#lniqdWCjp3-pYHpA{pOFpwuS;T-b(a>rK256Vzeb(roHV~~2@xVcVaS-4Ate1y zYONqssBF_hj>Uw=q0$tVSCSLxDg8@7N7AoPR>LT!tr=M&{?z)5Nv*g>AzQ1v3spxo zmvHRKY(!2il+U*k8Q?HD)&Ukj|5VUvZba}@WTJbIiJhaMo-gdTGZ zVfpb@CBFV)AX%Xtr==4r6Lwkq2UVZ6%fW8ezk8+EnDA3GzuP}qdE3ega{e57p4uSv zttzb1+etibO2ezQ6`ll8iLs#^pYelOWJb&5ZSvC-!))s&ZaMWu?IZiXoTAP_yw?fb z<8$z}yeC{yH-j8)qYYZ(_37lfx3A>p7>ieeZ(^{VTr!fYR-L;t5~Pn1)XN$LFRI6L z-ypI0i~>{d4A#EX_0(pRt56OXWN58M`67t0cxHqztqT}p3dfh=55bVp+f?C0AXSjy z@muF&AZNqPvD+5#qM`aOsiK&A2nxE-?4A1u2EIlUbD^2ie|g{je<1j{R`gU`rv6G# zBo${2)5=KUGQU(Y*{xsdik~dsh6(9WqBHeUea(4E?Q${pIMa%gj2ZQ$I-#$JDFbY9 zY{06B*S{2x+F+>T(t>I|O?ujw z;>9O=ko|<-kGd`?UjJ0W#ONWO12JztqnP0`=?*+wqnn%S;+e&GM~(?BbIC|C>?? zA5k@S3;iRQ!&BK1k}||K0f5aJc4HrzO~oeDA5o=RW>ld6jubolHwi0>QrLiuEzIT< z?eH13Kx4%|<7_hPGEO*FQ~1{kB>q9(@JX!eD*3o@Xi5o+_fZVzXyak{i@xZVZmsfi zuDee?!*BYty9k5eOqkQpZ(H9@#9UH2E=UmI(8geTLAuPe5W8}I$4Aqw2g5mnOC*ig z=2~7Sz9pe>d=L;!$%56G`1Javk2&HMScC~i7WR?HC#vDd_}A=0at7?`QE zO`K%)NkFCQ!F54q92=7@q0Y2Q^3`3|wS@VU20_2PDudtEn%#byRi9>Mf^N54VYJRw zSEofaYjPZx&NslzITxe8a;7Ow8*o;H8@~aWQucTeuywM( z2`>M`7KCGUJsBn&5Y7SjsvIhgjs(C!GX3q z3?(6@F8NeGd4}`grL>{m8_UiyBuq3&C7(r6?*i3d3)UYv<6bhUi@VRNgU*)*LIJU? z(5!iaoaZu3YC5NV3q6Kau{)$EMx)&27^>M@+dOUZQ3xBNN&tw6Zl|zN3*Tds7de75 zf#+uvva}zNveVBtW3q~ndwSZZ+CYd@!S6`)u}lTJ;08fR1?=vO6-QoY`=Jy+RO}T` z_;cw%7DnsttU-UZi^o1j!VpaiL+x8vRb*^?a&R%em!`aQ~63t zOjNkg955ihDgww`@!O{|O;!D5v4df`a_UCq(gn1ActL?Q%DD$r9Y9|(0O0<|Ra5*q zZM_dwqabDK9aMOa$dYt>?h%|w3#;2~7U@LD!T}DS6G=k0d@D3*KdrOHDk#wmV>N^A zfBip;_*vyb`T`)MKc$?MfKS2Dk5ym+iav_PUC<429djVeAQaA2#%HKD6AOq{p4=vB zXa_JA;D=?$^qV~o1Iw@A2PfTkl=Bm!TAQ!a2LlJCr@kPAM%c)1F04z3WuI95geBkuzaNchyaRz; zUN;JiDfnm1Y5zM%#UKFUayY}&s9GZW+leV|CCR#d*6n3O7q!3d<|h_E@S&?o!=!jy za@0Msfi$Q8pl;kS1jtgIr{#Q%+IXQ%iy`!h)OVRfTq$Sqb~$_ED~%|@810NByDJA+ zKr*Cd60mBQfTLciBd#>~ak&DKmA%-DM)aN*Jx0V#Um1Qf`8dHa+9X#jI!vDB=Gz8{ zQY%9GzlBLmZWq6C^DjEvA3fZLPz0AoYKd^LS=!uH(}B+Zns2*yPukiFU+r?-#)p1k zA)0%H4>eps74&TfiYNUB2f(SJ>?j6z8qz|8==$-x;3VCHbb z)j-k-BN|}sI_9un{xi{b-$i;)nZiN!W{6f4ZvLW&4z_N9!^=3P0!>6pflF<&7AK;z z6h9H+6G29D^R8Tv>b~9}s`RZzKE1UX zGdvuUmZ28p>AM=#2Ev$5*{}=>oD$8*L0V-f5rh6%YW%Rk+Aa(Kp@m=FLRKjhRv0t` zC!=QkZUnIeQ7U(&X_xkI*w>uS_rSm`oGD}nd#cPS*PyLi=jK!7If=9(bWwhQg$eJT z&6f>7)G5@w?adc$i_lzl8gt@W$f9P%c> zb#p~Km6hLEz-E2cYcqp2XX^`Rvbat4mFdvUKMdZ)X}a*Zn{}dK0BcnA6_o^#&J?U} zzA#FuV!0s5f{}7^0`q05vH|mg@wgM8jrAY>`><7`8~WZwInPW_AA2iCi_CgeL zz&S1jdo+u+&teogd|rwmrjQdZ#T;A`k#QLBP)gRf?T2< zc_*U<2DqywbOm54+wkAR|K2w8xO}uI^0f17eOE3p-UDE$T(K+-Gocg^s}U*}1<{lp z9d0deajEyEnOArvDqWH#$NbIBAv?!5z*^sIKQH1^r*(LeSqzyVX9B%|EINdMjF%UZ zCyA7o4az3-4vUim>x*gASGnto#~)v>h$34{SuEpDBT5%OBd z;vT&87bh``m*RJ9FlBBtA>c(Ll#78iHNhXQ7z=%7KA8TOJg zMG|$GT_w+&m#hUU$!ICn0H|Ux4pbZeFtd88rBMkdIXBirutEepD-u>~B}+0+VP1n3 zk~=qpv!`f=TVH0ExMV~U()&~=gs5FD-R|*%prs`^NKnIL*23f#jsVSPLM>2SRZ$~V zgDg4UAyqJXjI2BPP>ig{Yl}}i+N4hTJR@)&l}xeM+2+iQX2S&C_}bA53bJVMr_(2Y zaNtIjKBgPhWlhykK(}8)hqfm%2HhJ*3IIO(SxLU5*U&`H?vz?X0&Q$uj>?#oZOeP( z*FTu1xMTQqV89sgaz?mO2tEuQbbaV(3=~Uv2hwW|GyPhqtid61Q`h-nbS56ln*GR9 zWi0=4_RXqEz?%-;V?W9p_C{t6FlhS?&Tp-Q{|W>VfSjn zX1p(5YI!RK^EQ!8&8lt4RHjgQ>XW&!tg=H=PPC-~PzUFQufnd8`j7;AgU~Q83#I0> z=X{pK>REc&(O=$OIYQXkkPfrdMT^VYk60dR$=W=rGLyb`uLx|q(|GlI9wX{7Q(E8k zDVXTU(VBo5n!CE7{et!hM>yGa`s~3qIWf8hu9&!1*H_(ojgoPi0YePD^Vw^ep~H5+ zV^SF%O$wJ@mQ-G?%hF9{bBcKc5|ssi3iW1> zCDzvvIILk0vNtv?w?&Wg6ZgH_zH3Z6VjL!Dj!k99Lz=+=Cxr53bcx9Sjq+)d!34Kc zsDMmGm}HJ%zZPcfiP6DI@l<6pJ8gr5TeF#lHN4T%kG#9m!#&}506{A)$HL!UdX{Q4$A8{>fJ;L|5{frN^3&RG+Q9X zq0%gY_d0zMj4}WL!&UvI5|C`gEaQB=fSqF{#Vy}GqpOG&0D$u!6~L+b2$1;LW#^O_ z$suefT@%*RMPM}bxs0oo?LS-0QE1=h2>CqDkwM>wd@pI2 zc(I;z9lqR$58(yCOzU-aV#{))xWT$m_uHTd$o^5+U-8z8adBroDz_67@+? z?Gs0&j9ijxwpuVPm!~Rfd*1qUR%>gS%~NZjcGvp2Tjsq!t^5qn+NnNMzDq-jI^aTS z2$MV3+r$9_ERq3a=`m?x16d?RIer+kyqLDYap4@S3+)7|{-w{&HS3 z8ke;Z5Of=$Z`sOvF8`gJHbaI4@8ZO484OAsva5C!^AVoJCc{h`fu{X|Fx_u!+

5 zVed@>R&v1{$3iXq$uOOH9j8enMyYSG6Pi|n8g{fZ)aHAQo2dAJ?5Ssm(AmznAmktk zxiECH**G_(00)RkKyQV5S|8II!^o~#iuIPzF5-|{;`KQg?@~(I)kxlAT34i=WHcCf zEgf(Q2Go2$b}-IF00000Icf=638Q63u(v)1#W;CY#$sR4--B$U^=DLGUCnOL$qVSv z95lUOr?Wsmfphl7nnXlTHeVD`Co0mFJex8+F3K(s)|#E}MBPMFuc_8gxHU@<`#1iS zUIT7;Li2dt3})nT9=3DP3d(V;^kjBRPDr*~Nc_)*erj>sj`7{blSYn;3P%=1(#uW zruo_9y#Pn(oa3!HrJ$8=e9s!G8qB~Z&)vxrodb~Up?tkX4)C3GcaiNx>K<|IqgZG= zP~?dH(O;}2hG_elyE{+`TG9a!QGrJ*?22@~?a%(%N1)FR^q4NjNtop4_oo!nafmcL z!Vb9?WtX<|;l1SmJlAcT2Qd<5KN^Oy;%s38>1n?maK9*qiQ3U?M3JEbpQMYWaVr2# z{8;8vDf3=5Q%_hc&5OJZsH7jNcgbZm_#KYU8M{^SSvMC;1!)YSh#oa_VkpbRFy%a| zYiy#f)~#37WHSFB6A;haEIWoNm%ex!MO)5F*o2nGzjte=ZL1N;d2*Slh0y*u6j`Cg z6PlBgY|w@kY0PA~^9wpx;OXYyFXDirfF|9!Wz=VjvIdO3z%@an1tun=CztM7xK?fz zXOzWBQ+si8Fn!=z$+IoD2k$$|-@Oi}E>k#5?U-b`!bb#r@0iE4d2f{X0G*NP|ZsNX}ptyIiL8k-uP7-+zyOt}AN&4LZl6 zghN=s=L>I0$S~MhHps*TuWDSp_8zAFY4NslS0=w;>rpePAS#Yyf<(`-O&Gh<9+h4B zm)b=Fttx96Y$q2|=?c>m;t+tk^Eq-g|`A?%_)AOEv)FyT7h z<73!Z+sAZ#IgtW~b9ANR4U`w7apP~bGqz#t^2Yz^C{2uxFK62o9HQ<^iv!V4iDewj z&9fkqdl3AMw6gtYg&cc?X@RY`2;)2@9Ekf=ZZiPhNCa(boxpVYTAAA{v zZ`sn(!63ED63Q#{sj&IfK-NfJ5|re2O*7BZ zs_L~?3SdX4m$CNO4uxktuF2mE)ez00l^u?FlY}tpkaOb^v`so(G41|T#H24-R(BUk z@#wukM`jD%Y{S3T$T5EE^bf4lI*%3JlTJB|HjS-OKhu2l0J9|9s|N%Rq>;ol@Eo^h zK_TqWs_f@s1s3ectKz%aX-JFu_Yj~3O#V#AYkgNn-PCpfj(?lnK7R88z&Jzqih}E~ z0000009ez1PF+Cl^t66eaMqYfafDV>(*s428g>d{tf<1PL}%ulomy}zg5Q{*IIVF9 zWeaq`Vq1(k`jJy6?6`vegQw267?nwQ1_NR+QcXeBPIZV6LHzl_U%@nqP~`mMW0S#= z?2w|V9}z{U!)^#9P}w2ta$cV7jqI;JyLg-5z$SJ8HlMz58O^di#)f_Zie{5dtC9Q* zX8tTtULna2&uPaFwvgk7IpDs*$XIU(;p;~~-Zf?viE z`L?n{QnA)h{dcW#+pb8IHPL7%Mr}GduUQB*k|K3OK;eBQfgc8~7Dhj|AXSq;gDL4t zH~x!a;3c%2 zZ!&;PGhXI8gfz0qSjv_yMUC2&JX6qze z_>oI`ZxTmf4#2F;e&v-4mwGT>cJHV0mq0M=Oxr>Ol9dX^OyC_ zZUbWstUbBeJP=n~P$VCfXF1mP9uAhFaZ8h9B~s}=?eb{mM^({aGUoIl8uh3>5+`$6 zK*7MIT2O(VbP6x$(Xwa}1oE-^3hh=g#CP!FKLVUt{^=O&OAp*^%1h?=VcQgJ{-W&G z21cigPZh01eg+gEKmY&$0UDH)YH`aMqeRUiNl`+8GE>6Z$cW0q)8YFT2F~&sMw$izD{ePQa9gdxXr2sGov3LJnj*)ydvzxI!LrBQ-w2t$q zM@HhvCsJuR!}Tttq!~=Laifu_IT#b!xp7MNsKL09x%tK_^apan&ahGF6142Dl@Iyt@)U<8L>z{m%MTCYo`7EA7bJ?%$6lzG{I8`^MuH~cuxsA1 z>f6eGF_D)+mHOWLTT60kWIog`^*_c=nRD*&*Y}7!PPtoco=97=FLLS@6 z%t{_Ih4&@}UE3xDQgkj@dK_3wNwZC6OJYYdkzfyKU6v>d-KNocX3txw9O6VW|H_TC zorg#hEWwy&<(^doP|h)r_H=i1<(RaHkoEZ7MWzDbV PI!54}eSncZsbByAmE`!g diff --git a/src/api/firearm-api.ts b/src/api/firearm-api.ts index 9b0bbba..c62cb31 100644 --- a/src/api/firearm-api.ts +++ b/src/api/firearm-api.ts @@ -7,9 +7,9 @@ interface FirearmParams extends PageQueryParams { } /** - * 查询武器列表 + * Fetch firearm list * - * @param params 分页查询参数¬ + * @param params Paged query parameters */ export async function getFirearms(params?: FirearmParams): Promise> { let uri = "/firearms" @@ -28,9 +28,9 @@ export async function getFirearms(params?: FirearmParams): Promise } /** - * 根据 ID 查询武器 + * Fetch firearm by ID * - * @param id 武器 ID + * @param id Firearm ID */ export async function getFirearm(id: number): Promise { const { data } = await WebClient.get(`/firearms/${id}`) @@ -38,7 +38,7 @@ export async function getFirearm(id: number): Promise { } /** - * 新建武器 + * Create firearm * @param request */ export async function addFirearm(request: AddFirearmRequest): Promise { diff --git a/src/components/markdown-renderer/index.tsx b/src/components/markdown-renderer/index.tsx new file mode 100644 index 0000000..732fbe3 --- /dev/null +++ b/src/components/markdown-renderer/index.tsx @@ -0,0 +1,17 @@ +import React from "react" + +interface MarkdownRendererProps { + /** HTML string processed by vite-plugin-markdown */ + html: string + /** Optional custom class name */ + className?: string +} + +export default function MarkdownRenderer({ html, className = "" }: MarkdownRendererProps) { + return ( +

+ ) +} diff --git a/src/index.css b/src/index.css index 63db6f0..a03856c 100644 --- a/src/index.css +++ b/src/index.css @@ -1,6 +1,7 @@ @layer theme, base, antd, components, utilities; @import 'tailwindcss'; +@plugin "@tailwindcss/typography"; html, body { margin: 0; @@ -56,7 +57,7 @@ html, body { .nav-item:hover::after, .nav-item.active::after { - height: 80%; /* 向上打光的高度,可以调整 */ + height: 80%; /* Height of the upward glow; adjustable. */ } .nav-item:hover, diff --git a/src/layout/hero-layout/index.tsx b/src/layout/hero-layout/index.tsx index f45b8bc..ef83e32 100644 --- a/src/layout/hero-layout/index.tsx +++ b/src/layout/hero-layout/index.tsx @@ -2,10 +2,16 @@ import { Outlet, Link, NavLink } from "react-router-dom" import { useMemo } from "react" import dayjs from "dayjs" import { Dropdown } from "antd" +import { + FileTextOutlined, + GithubOutlined, + LockOutlined, + LoginOutlined, +} from "@ant-design/icons" import { AuthApi } from "@/api" import { useAppDispatch, useAppSelector } from "@/store/hooks" import { clearCurrentUser } from "@/store/auth-slice" -import { useState } from 'react'; +import { useState } from "react" /** * Main application component that serves as the root layout. @@ -15,7 +21,7 @@ export default function HeroLayout() { const today = useMemo(() => dayjs(), []) const user = useAppSelector((state) => state.auth.user) const dispatch = useAppDispatch() - const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isDropdownOpen, setIsDropdownOpen] = useState(false) async function handleLogout() { try { @@ -28,29 +34,29 @@ export default function HeroLayout() { return (
{/* Navigation Header */} -
-
-
+
+
+

《三角洲》指南

-
diff --git a/src/markdown.d.ts b/src/markdown.d.ts new file mode 100644 index 0000000..ad2af77 --- /dev/null +++ b/src/markdown.d.ts @@ -0,0 +1,6 @@ +declare module "*.md" { + const attributes: Record + const html: string + const toc: { level: string; content: string; slug: string }[] + export { attributes, html, toc } +} diff --git a/src/page/legal/index.tsx b/src/page/legal/index.tsx new file mode 100644 index 0000000..c108514 --- /dev/null +++ b/src/page/legal/index.tsx @@ -0,0 +1,34 @@ +import { Tabs } from "antd" +import { useSearchParams } from "react-router-dom" +import MarkdownRenderer from "@/components/markdown-renderer" +import { html as EulaHtml } from "@/docs/EULA.md" +import { html as PrivacyHtml } from "@/docs/PrivacyPolicy.md" + +const tabKeys = new Set(["eula", "privacy"]) + +export default function LegalPage() { + const [searchParams, setSearchParams] = useSearchParams() + const rawTab = searchParams.get("tab") + const activeTab = rawTab && tabKeys.has(rawTab) ? rawTab : "eula" + + return ( +
+ setSearchParams({ tab: key })} + items={[ + { + key: "eula", + label: "最终用户许可协议", + children: , + }, + { + key: "privacy", + label: "隐私政策", + children: , + }, + ]} + /> +
+ ) +} diff --git a/src/router/index.tsx b/src/router/index.tsx index c45341f..a9bd1aa 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -40,6 +40,10 @@ const router = createBrowserRouter( path: "mod-codes", lazy: lazy(() => import("@/page/mod-codes")), }, + { + path: "legal", + lazy: lazy(() => import("@/page/legal")) + } ], }, { diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..5170b8b --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,10 @@ +import type { Config } from "tailwindcss" +import typography from "@tailwindcss/typography" + +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [typography], +} satisfies Config diff --git a/vite.config.ts b/vite.config.ts index dde1d6d..629a4ba 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,10 +3,11 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" import tailwindcss from "@tailwindcss/vite" import portChecker from "vite-plugin-port-checker" +import { Mode, plugin as markdown } from "vite-plugin-markdown" // https://vite.dev/config/ export default defineConfig({ - plugins: [react(), tailwindcss(), portChecker()], + plugins: [react(), tailwindcss(), portChecker(), markdown({ mode: [Mode.HTML, Mode.TOC] })], base: "/", build: { rolldownOptions: {