diff --git a/package.json b/package.json index 597538f..9b06203 100644 --- a/package.json +++ b/package.json @@ -18,14 +18,17 @@ }, "dependencies": { "@astrojs/mdx": "^5.0.3", - "@astrojs/vercel": "^10.0.3", "@astrojs/rss": "^4.0.18", "@astrojs/sitemap": "^3.7.2", + "@astrojs/vercel": "^10.0.3", "@giscus/react": "^3.1.0", "@pilcrowjs/object-parser": "^0.0.4", "@react-hook/intersection-observer": "^3.1.2", "@rehype-pretty/transformers": "^0.13.2", + "@vercel/analytics": "^2.0.1", + "@vercel/speed-insights": "^2.0.0", "arctic": "^3.7.0", + "ioredis": "^5.10.1", "lucide-react": "^0.468.0", "marked": "^15.0.12", "react": "^18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aaabe9e..4e49a36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@astrojs/mdx': specifier: ^5.0.3 - version: 5.0.3(astro@6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3)) + version: 5.0.3(astro@6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3)) '@astrojs/rss': specifier: ^4.0.18 version: 4.0.18 @@ -19,7 +19,7 @@ importers: version: 3.7.2 '@astrojs/vercel': specifier: ^10.0.3 - version: 10.0.3(astro@6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(react@18.3.1)(rollup@4.60.1) + version: 10.0.3(astro@6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(react@18.3.1)(rollup@4.60.1) '@giscus/react': specifier: ^3.1.0 version: 3.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -32,9 +32,18 @@ importers: '@rehype-pretty/transformers': specifier: ^0.13.2 version: 0.13.2 + '@vercel/analytics': + specifier: ^2.0.1 + version: 2.0.1(react@18.3.1) + '@vercel/speed-insights': + specifier: ^2.0.0 + version: 2.0.0(react@18.3.1) arctic: specifier: ^3.7.0 version: 3.7.0 + ioredis: + specifier: ^5.10.1 + version: 5.10.1 lucide-react: specifier: ^0.468.0 version: 0.468.0(react@18.3.1) @@ -80,7 +89,7 @@ importers: version: 5.0.2(@types/node@24.12.0)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(jiti@1.21.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(yaml@2.8.3) '@astrojs/tailwind': specifier: ^6.0.2 - version: 6.0.2(astro@6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(tailwindcss@3.4.19(yaml@2.8.3)) + version: 6.0.2(astro@6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(tailwindcss@3.4.19(yaml@2.8.3)) '@tailwindcss/typography': specifier: ^0.5.19 version: 0.5.19(tailwindcss@3.4.19(yaml@2.8.3)) @@ -92,7 +101,7 @@ importers: version: 18.3.7(@types/react@18.3.28) astro: specifier: ^6.1.2 - version: 6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) + version: 6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) tailwindcss: specifier: ^3.4.19 version: 3.4.19(yaml@2.8.3) @@ -563,6 +572,9 @@ packages: cpu: [x64] os: [win32] + '@ioredis/commands@1.5.1': + resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==} + '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -913,6 +925,9 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@upstash/redis@1.37.0': + resolution: {integrity: sha512-LqOJ3+XWPLSZ2rGSed5DYG3ixybxb8EhZu3yQqF7MdZX1wLBG/FRcI6xcUZXHy/SS7mmXWyadrud0HJHkOc+uw==} + '@vercel/analytics@1.6.1': resolution: {integrity: sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==} peerDependencies: @@ -939,6 +954,35 @@ packages: vue-router: optional: true + '@vercel/analytics@2.0.1': + resolution: {integrity: sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==} + peerDependencies: + '@remix-run/react': ^2 + '@sveltejs/kit': ^1 || ^2 + next: '>= 13' + nuxt: '>= 3' + react: ^18 || ^19 || ^19.0.0-rc + svelte: '>= 4' + vue: ^3 + vue-router: ^4 + peerDependenciesMeta: + '@remix-run/react': + optional: true + '@sveltejs/kit': + optional: true + next: + optional: true + nuxt: + optional: true + react: + optional: true + svelte: + optional: true + vue: + optional: true + vue-router: + optional: true + '@vercel/functions@3.4.3': resolution: {integrity: sha512-kA14KIUVgAY6VXbhZ5jjY+s0883cV3cZqIU3WhrSRxuJ9KvxatMjtmzl0K23HK59oOUjYl7HaE/eYMmhmqpZzw==} engines: {node: '>= 20'} @@ -948,6 +992,11 @@ packages: '@aws-sdk/credential-provider-web-identity': optional: true + '@vercel/kv@3.0.0': + resolution: {integrity: sha512-pKT8fRnfyYk2MgvyB6fn6ipJPCdfZwiKDdw7vB+HL50rjboEBHDVBEcnwfkEpVSp2AjNtoaOUH7zG+bVC/rvSg==} + engines: {node: '>=14.6'} + deprecated: 'Vercel KV is deprecated. If you had an existing KV store, it should have moved to Upstash Redis which you will see under Vercel Integrations. For new projects, install a Redis integration from Vercel Marketplace: https://vercel.com/marketplace?category=storage&search=redis' + '@vercel/nft@1.5.0': resolution: {integrity: sha512-IWTDeIoWhQ7ZtRO/JRKH+jhmeQvZYhtGPmzw/QGDY+wDCQqfm25P9yIdoAFagu4fWsK4IwZXDFIjrmp5rRm/sA==} engines: {node: '>=20'} @@ -960,6 +1009,32 @@ packages: '@vercel/routing-utils@5.3.3': resolution: {integrity: sha512-KYm2sLNUD48gDScv8ob4ejc3Gww2jcJyW80hTdYlenAPz/5BQar1Gyh38xrUuZ532TUwSb5mV1uRbAuiykq0EQ==} + '@vercel/speed-insights@2.0.0': + resolution: {integrity: sha512-jwkNcrTeafWxjmWq4AHBaptSqZiJkYU5adLC9QBSqeim0GcqDMgN5Ievh8OG1rJ6W3A4l1oiP7qr9CWxGuzu3w==} + peerDependencies: + '@sveltejs/kit': ^1 || ^2 + next: '>= 13' + nuxt: '>= 3' + react: ^18 || ^19 || ^19.0.0-rc + svelte: '>= 4' + vue: ^3 + vue-router: ^4 + peerDependenciesMeta: + '@sveltejs/kit': + optional: true + next: + optional: true + nuxt: + optional: true + react: + optional: true + svelte: + optional: true + vue: + optional: true + vue-router: + optional: true + '@vitejs/plugin-react@5.2.0': resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1115,6 +1190,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -1195,6 +1274,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1457,6 +1540,10 @@ packages: resolution: {integrity: sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==} deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. + ioredis@5.10.1: + resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==} + engines: {node: '>=12.22.0'} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -1550,6 +1637,12 @@ packages: lit@3.3.2: resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -2061,6 +2154,14 @@ packages: recma-stringify@1.0.0: resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -2202,6 +2303,9 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + stream-replace-string@2.0.0: resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} @@ -2559,12 +2663,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@5.0.3(astro@6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))': + '@astrojs/mdx@5.0.3(astro@6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))': dependencies: '@astrojs/markdown-remark': 7.1.0 '@mdx-js/mdx': 3.1.1 acorn: 8.16.0 - astro: 6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) + astro: 6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) es-module-lexer: 2.0.0 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 @@ -2619,9 +2723,9 @@ snapshots: stream-replace-string: 2.0.0 zod: 4.3.6 - '@astrojs/tailwind@6.0.2(astro@6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(tailwindcss@3.4.19(yaml@2.8.3))': + '@astrojs/tailwind@6.0.2(astro@6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(tailwindcss@3.4.19(yaml@2.8.3))': dependencies: - astro: 6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) + astro: 6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) autoprefixer: 10.4.27(postcss@8.5.8) postcss: 8.5.8 postcss-load-config: 4.0.2(postcss@8.5.8) @@ -2641,14 +2745,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/vercel@10.0.3(astro@6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(react@18.3.1)(rollup@4.60.1)': + '@astrojs/vercel@10.0.3(astro@6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3))(react@18.3.1)(rollup@4.60.1)': dependencies: '@astrojs/internal-helpers': 0.8.0 '@vercel/analytics': 1.6.1(react@18.3.1) '@vercel/functions': 3.4.3 '@vercel/nft': 1.5.0(rollup@4.60.1) '@vercel/routing-utils': 5.3.3 - astro: 6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) + astro: 6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3) esbuild: 0.27.4 tinyglobby: 0.2.15 transitivePeerDependencies: @@ -2975,6 +3079,8 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@ioredis/commands@1.5.1': {} + '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.3 @@ -3327,14 +3433,28 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@upstash/redis@1.37.0': + dependencies: + uncrypto: 0.1.3 + optional: true + '@vercel/analytics@1.6.1(react@18.3.1)': optionalDependencies: react: 18.3.1 + '@vercel/analytics@2.0.1(react@18.3.1)': + optionalDependencies: + react: 18.3.1 + '@vercel/functions@3.4.3': dependencies: '@vercel/oidc': 3.2.0 + '@vercel/kv@3.0.0': + dependencies: + '@upstash/redis': 1.37.0 + optional: true + '@vercel/nft@1.5.0(rollup@4.60.1)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 @@ -3363,6 +3483,10 @@ snapshots: optionalDependencies: ajv: 6.14.0 + '@vercel/speed-insights@2.0.0(react@18.3.1)': + optionalDependencies: + react: 18.3.1 + '@vitejs/plugin-react@5.2.0(vite@7.3.1(@types/node@24.12.0)(jiti@1.21.7)(yaml@2.8.3))': dependencies: '@babel/core': 7.29.0 @@ -3420,7 +3544,7 @@ snapshots: astring@1.9.0: {} - astro@6.1.2(@types/node@24.12.0)(@vercel/functions@3.4.3)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3): + astro@6.1.2(@types/node@24.12.0)(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1)(jiti@1.21.7)(rollup@4.60.1)(typescript@5.7.3)(yaml@2.8.3): dependencies: '@astrojs/compiler': 3.0.1 '@astrojs/internal-helpers': 0.8.0 @@ -3470,7 +3594,7 @@ snapshots: ultrahtml: 1.6.0 unifont: 0.7.4 unist-util-visit: 5.1.0 - unstorage: 1.17.5(@vercel/functions@3.4.3) + unstorage: 1.17.5(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1) vfile: 6.0.3 vite: 7.3.1(@types/node@24.12.0)(jiti@1.21.7)(yaml@2.8.3) vitefu: 1.1.2(vite@7.3.1(@types/node@24.12.0)(jiti@1.21.7)(yaml@2.8.3)) @@ -3593,6 +3717,8 @@ snapshots: clsx@2.1.1: {} + cluster-key-slot@1.1.2: {} + collapse-white-space@2.1.0: {} comma-separated-tokens@2.0.3: {} @@ -3655,6 +3781,8 @@ snapshots: defu@6.1.4: {} + denque@2.1.0: {} + dequal@2.0.3: {} destr@2.0.5: {} @@ -4037,6 +4165,20 @@ snapshots: intersection-observer@0.10.0: {} + ioredis@5.10.1: + dependencies: + '@ioredis/commands': 1.5.1 + cluster-key-slot: 1.1.2 + debug: 4.4.3 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + iron-webcrypto@1.2.1: {} is-alphabetical@2.0.1: {} @@ -4113,6 +4255,10 @@ snapshots: lit-element: 4.2.2 lit-html: 3.3.2 + lodash.defaults@4.2.0: {} + + lodash.isarguments@3.1.0: {} + longest-streak@3.1.0: {} loose-envify@1.4.0: @@ -4868,6 +5014,12 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -5132,6 +5284,8 @@ snapshots: space-separated-tokens@2.0.2: {} + standard-as-callback@2.1.0: {} + stream-replace-string@2.0.0: {} stringify-entities@4.0.4: @@ -5325,7 +5479,7 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - unstorage@1.17.5(@vercel/functions@3.4.3): + unstorage@1.17.5(@upstash/redis@1.37.0)(@vercel/functions@3.4.3)(@vercel/kv@3.0.0)(ioredis@5.10.1): dependencies: anymatch: 3.1.3 chokidar: 5.0.0 @@ -5336,7 +5490,10 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.3 optionalDependencies: + '@upstash/redis': 1.37.0 '@vercel/functions': 3.4.3 + '@vercel/kv': 3.0.0 + ioredis: 5.10.1 update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: diff --git a/src/components/analytics.tsx b/src/components/analytics.tsx new file mode 100644 index 0000000..8487ace --- /dev/null +++ b/src/components/analytics.tsx @@ -0,0 +1,11 @@ +import { Analytics } from "@vercel/analytics/react"; +import { SpeedInsights } from "@vercel/speed-insights/react"; + +export default function VercelAnalytics() { + return ( + <> + + + + ); +} diff --git a/src/components/blog/post-list.tsx b/src/components/blog/post-list.tsx index 5adfcba..ddf10f4 100644 --- a/src/components/blog/post-list.tsx +++ b/src/components/blog/post-list.tsx @@ -77,7 +77,7 @@ export const BlogPostList = ({ posts }: BlogPostListProps) => { className="text-xs md:text-base text-aqua hover:text-aqua-bright transition-colors duration-200" onClick={(e) => { e.preventDefault(); - window.location.href = `/blog/tag/${tag}`; + window.location.href = `/blog/tags/${encodeURIComponent(tag)}`; }} > #{tag} diff --git a/src/components/blog/tag-list.tsx b/src/components/blog/tag-list.tsx index a53759c..986822e 100644 --- a/src/components/blog/tag-list.tsx +++ b/src/components/blog/tag-list.tsx @@ -1,4 +1,5 @@ -import React, { useMemo } from 'react'; +import { useMemo } from 'react'; +import { AnimateIn } from "@/components/animate-in"; interface BlogPost { title: string; @@ -11,47 +12,49 @@ interface TagListProps { posts: BlogPost[]; } -const TagList: React.FC = ({ posts }) => { - const spectrumColors = [ - 'text-red-bright', - 'text-orange-bright', - 'text-yellow-bright', - 'text-green-bright', - 'text-aqua-bright', - 'text-blue-bright', - 'text-purple-bright' - ]; +const spectrumColors = [ + 'text-red-bright', + 'text-orange-bright', + 'text-yellow-bright', + 'text-green-bright', + 'text-aqua-bright', + 'text-blue-bright', + 'text-purple-bright' +]; +const sizeClasses = [ + 'text-3xl sm:text-4xl', + 'text-2xl sm:text-3xl', + 'text-xl sm:text-2xl', + 'text-lg sm:text-xl', + 'text-base sm:text-lg', +]; + +const TagList = ({ posts }: TagListProps) => { const tagData = useMemo(() => { if (!Array.isArray(posts)) return []; - - const tagMap = new Map(); + + const tagMap = new Map(); posts.forEach(post => { - if (post?.data?.tags && Array.isArray(post.data.tags)) { - post.data.tags.forEach(tag => { - if (!tagMap.has(tag)) { - tagMap.set(tag, { - name: tag, - count: 1 - }); - } else { - const data = tagMap.get(tag); - data.count++; - } - }); - } + post?.data?.tags?.forEach(tag => { + tagMap.set(tag, (tagMap.get(tag) || 0) + 1); + }); }); - const tagArray = Array.from(tagMap.values()); - const maxCount = Math.max(...tagArray.map(t => t.count)); - - return tagArray - .sort((a, b) => b.count - a.count) - .map((tag, index) => ({ - ...tag, - color: spectrumColors[index % spectrumColors.length], - frequency: tag.count / maxCount - })); + const tags = Array.from(tagMap.entries()) + .sort((a, b) => b[1] - a[1]); + const maxCount = tags[0]?.[1] || 1; + + return tags.map(([name, count], i) => { + const ratio = count / maxCount; + const sizeIndex = ratio > 0.8 ? 0 : ratio > 0.6 ? 1 : ratio > 0.4 ? 2 : ratio > 0.2 ? 3 : 4; + return { + name, + count, + color: spectrumColors[i % spectrumColors.length], + size: sizeClasses[sizeIndex], + }; + }); }, [posts]); if (tagData.length === 0) { @@ -63,50 +66,25 @@ const TagList: React.FC = ({ posts }) => { } return ( -
-
- {tagData.map(({ name, count, color, frequency }) => ( + ); }; diff --git a/src/components/blog/tagged-posts.tsx b/src/components/blog/tagged-posts.tsx new file mode 100644 index 0000000..a44ad0b --- /dev/null +++ b/src/components/blog/tagged-posts.tsx @@ -0,0 +1,123 @@ +import { AnimateIn } from "@/components/animate-in"; +import { RssIcon, TagIcon, TrendingUpIcon } from "lucide-react"; + +type BlogPost = { + id: string; + data: { + title: string; + author: string; + date: string; + tags: string[]; + description: string; + image?: string; + imagePosition?: string; + }; +}; + +interface TaggedPostsProps { + tag: string; + posts: BlogPost[]; +} + +const formatDate = (dateString: string) => { + return new Date(dateString).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric" + }); +}; + +const TaggedPosts = ({ tag, posts }: TaggedPostsProps) => { + return ( + + ); +}; + +export default TaggedPosts; diff --git a/src/layouts/content.astro b/src/layouts/content.astro index ced33e5..9ab81e7 100644 --- a/src/layouts/content.astro +++ b/src/layouts/content.astro @@ -7,6 +7,7 @@ import Footer from "@/components/footer"; import Background from "@/components/background"; import ThemeSwitcher from "@/components/theme-switcher"; import AnimationSwitcher from "@/components/animation-switcher"; +import VercelAnalytics from "@/components/analytics"; import { THEME_LOADER_SCRIPT, THEME_NAV_SCRIPT } from "@/lib/themes/loader"; import { ANIMATION_LOADER_SCRIPT, ANIMATION_NAV_SCRIPT } from "@/lib/animations/loader"; @@ -68,6 +69,7 @@ const ogImage = "https://timmypidashev.dev/og-image.jpg";
+