From bd70bae0068c399a2959a4d6b07be67886e13bb8 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:39:58 +0200 Subject: [PATCH 01/18] chore(deps): update deps --- bun.lock | 80 +++++++++++++++++++++++++++----------------------- next.config.ts | 1 - package.json | 16 +++++----- 3 files changed, 51 insertions(+), 46 deletions(-) diff --git a/bun.lock b/bun.lock index be8f87e..d6996b2 100644 --- a/bun.lock +++ b/bun.lock @@ -4,20 +4,20 @@ "": { "name": "sorting-algorithms", "dependencies": { - "@icons-pack/react-simple-icons": "^13.3.0", + "@icons-pack/react-simple-icons": "^13.4.0", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-slider": "^1.3.5", "@radix-ui/react-slot": "^1.2.3", "@t3-oss/env-nextjs": "^0.13.8", - "@tanstack/react-query": "^5.81.5", - "@tanstack/react-query-devtools": "^5.81.5", + "@tanstack/react-query": "^5.83.0", + "@tanstack/react-query-devtools": "^5.83.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "d3": "^7.9.0", "hast-util-to-jsx-runtime": "^2.3.6", "lucide-react": "^0.525.0", - "motion": "^12.23.0", - "next": "15.4.0-canary.111", + "motion": "^12.23.3", + "next": "15.3.5", "next-intl": "^4.3.4", "nuqs": "^2.4.3", "react": "^19.1.0", @@ -27,13 +27,13 @@ "shiki": "^3.7.0", "tailwind-merge": "^3.3.1", "tw-animate-css": "^1.3.5", - "zod": "^3.25.71", + "zod": "^3.25.76", }, "devDependencies": { - "@biomejs/biome": "^2.0.6", + "@biomejs/biome": "2.0.0", "@tailwindcss/postcss": "^4.1.11", "@types/d3": "^7.4.3", - "@types/node": "^24.0.10", + "@types/node": "^24.0.13", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "babel-plugin-react-compiler": "^19.1.0-rc.2", @@ -54,23 +54,23 @@ "@babel/types": ["@babel/types@7.28.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg=="], - "@biomejs/biome": ["@biomejs/biome@2.0.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.6", "@biomejs/cli-darwin-x64": "2.0.6", "@biomejs/cli-linux-arm64": "2.0.6", "@biomejs/cli-linux-arm64-musl": "2.0.6", "@biomejs/cli-linux-x64": "2.0.6", "@biomejs/cli-linux-x64-musl": "2.0.6", "@biomejs/cli-win32-arm64": "2.0.6", "@biomejs/cli-win32-x64": "2.0.6" }, "bin": { "biome": "bin/biome" } }, "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA=="], + "@biomejs/biome": ["@biomejs/biome@2.0.0", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.0", "@biomejs/cli-darwin-x64": "2.0.0", "@biomejs/cli-linux-arm64": "2.0.0", "@biomejs/cli-linux-arm64-musl": "2.0.0", "@biomejs/cli-linux-x64": "2.0.0", "@biomejs/cli-linux-x64-musl": "2.0.0", "@biomejs/cli-win32-arm64": "2.0.0", "@biomejs/cli-win32-x64": "2.0.0" }, "bin": { "biome": "bin/biome" } }, "sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.6", "", { "os": "win32", "cpu": "x64" }, "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg=="], "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], @@ -92,7 +92,7 @@ "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.5.10", "", { "dependencies": { "tslib": "2" } }, "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q=="], - "@icons-pack/react-simple-icons": ["@icons-pack/react-simple-icons@13.3.0", "", { "peerDependencies": { "react": "^16.13 || ^17 || ^18 || ^19" } }, "sha512-+7IxrUMLjfHBdDtW3aBlvELvjbQTX29cbrpc3UcCBhE51uL7GP1bkIEcaFM30f5rSB0oSqeg3MAmTIZV6nzY7A=="], + "@icons-pack/react-simple-icons": ["@icons-pack/react-simple-icons@13.4.0", "", { "peerDependencies": { "react": "^16.13 || ^17 || ^18 || ^19" } }, "sha512-SBBR2Jq88IEhr5NEg22MtF5WxlC6B5uRgXCL8tFHS/CBr5WBepEIFIcHG8ijNjfjIQa8YuO73Q2UvVHoM148fA=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.2", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.1.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg=="], @@ -146,23 +146,23 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], - "@next/env": ["@next/env@15.4.0-canary.111", "", {}, "sha512-tpr5As3Eqbhv0mx4jO9D0k7gFZ/5rL0DB8NZXx5PVWUUJKv1N2vlHPqr6to97ERVmRofParTKZgQGGzY/los8A=="], + "@next/env": ["@next/env@15.3.5", "", {}, "sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.111", "", { "os": "darwin", "cpu": "arm64" }, "sha512-24D4v+IB+b/A59z1B+8VJ0OMUHDpPxkTPU0n5qcJuf4UA9B6b7PZP6OeokSI/iJ2naTFwZcwi/0VE17uqEyklA=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.111", "", { "os": "darwin", "cpu": "x64" }, "sha512-WxRTH9FS15O6wbitxLKylO4j/l1qWEMWNSNlU7bLK3zwcFJjTdGD5vrjJc1vTk5dVnF1lJOecUWP8DRnzslJrA=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.111", "", { "os": "linux", "cpu": "arm64" }, "sha512-8AU3nfOAWQHlv6PbPBTdOE7Bw+dOWI6odebAFiDJIzAYDZEztR8QmvP0fwVwwUJxgsEE31pB5NDBI3jkSgX+VA=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.111", "", { "os": "linux", "cpu": "arm64" }, "sha512-6fbfZDEPTW+bv9HPhkorsN1UesniAbvVn3N+qtzJvPpNHjHcxfo5qs9Q2F/AcRWcPQnWiHdqCeT+qYNgMg5cpQ=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.111", "", { "os": "linux", "cpu": "x64" }, "sha512-iuZRMRwxM9nTgOpKS9z/ANHGKy5xf0Xhigswj0hFgqttF8fGmaEuRoyTeKvMVwwGNd7tftpzcOxgKfQKCr9cPA=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.111", "", { "os": "linux", "cpu": "x64" }, "sha512-1WHr+8ICeHb71bd5y60KvsrLsr0wz9+6H2sw8SuScrLuHWfTmbwYMue517EFV5RbO7dCxe/TIP9p1P0CxZ2uuQ=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.111", "", { "os": "win32", "cpu": "arm64" }, "sha512-repGty63yqiAkaUEJaTyzb8/+E5t6fjkKo+g4U5snMkEC5HDpJJh0BVBbT/WUQKmByuqiL3hRQR4sRYLBqhclw=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.111", "", { "os": "win32", "cpu": "x64" }, "sha512-yBC26Zq278JT2AwAKascL4CfphZsNULEmVuguBXTGTAtkVPpuJqZwHR5MW76l5ByptSbk9ZPYmiNhCeNEaCg7Q=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.5", "", { "os": "win32", "cpu": "x64" }, "sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw=="], "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], @@ -234,6 +234,8 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], "@t3-oss/env-core": ["@t3-oss/env-core@0.13.8", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-L1inmpzLQyYu4+Q1DyrXsGJYCXbtXjC4cICw1uAKv0ppYPQv656lhZPU91Qd1VS6SO/bou1/q5ufVzBGbNsUpw=="], @@ -270,13 +272,13 @@ "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.11", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "postcss": "^8.4.41", "tailwindcss": "4.1.11" } }, "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA=="], - "@tanstack/query-core": ["@tanstack/query-core@5.81.5", "", {}, "sha512-ZJOgCy/z2qpZXWaj/oxvodDx07XcQa9BF92c0oINjHkoqUPsmm3uG08HpTaviviZ/N9eP1f9CM7mKSEkIo7O1Q=="], + "@tanstack/query-core": ["@tanstack/query-core@5.83.0", "", {}, "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA=="], "@tanstack/query-devtools": ["@tanstack/query-devtools@5.81.2", "", {}, "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg=="], - "@tanstack/react-query": ["@tanstack/react-query@5.81.5", "", { "dependencies": { "@tanstack/query-core": "5.81.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-lOf2KqRRiYWpQT86eeeftAGnjuTR35myTP8MXyvHa81VlomoAWNEd8x5vkcAfQefu0qtYCvyqLropFZqgI2EQw=="], + "@tanstack/react-query": ["@tanstack/react-query@5.83.0", "", { "dependencies": { "@tanstack/query-core": "5.83.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ=="], - "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.81.5", "", { "dependencies": { "@tanstack/query-devtools": "5.81.2" }, "peerDependencies": { "@tanstack/react-query": "^5.81.5", "react": "^18 || ^19" } }, "sha512-lCGMu4RX0uGnlrlLeSckBfnW/UV+KMlTBVqa97cwK7Z2ED5JKnZRSjNXwoma6sQBTJrcULvzgx2K6jEPvNUpDw=="], + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.83.0", "", { "dependencies": { "@tanstack/query-devtools": "5.81.2" }, "peerDependencies": { "@tanstack/react-query": "^5.83.0", "react": "^18 || ^19" } }, "sha512-yfp8Uqd3I1jgx8gl0lxbSSESu5y4MO2ThOPBnGNTYs0P+ZFu+E9g5IdOngyUGuo6Uz6Qa7p9TLdZEX3ntik2fQ=="], "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], @@ -354,7 +356,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@24.0.10", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA=="], + "@types/node": ["@types/node@24.0.13", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], @@ -368,6 +370,8 @@ "babel-plugin-react-compiler": ["babel-plugin-react-compiler@19.1.0-rc.2", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-kSNA//p5fMO6ypG8EkEVPIqAjwIXm5tMjfD1XRPL/sRjYSbJ6UsvORfaeolNWnZ9n310aM0xJP7peW26BuCVzA=="], + "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], + "caniuse-lite": ["caniuse-lite@1.0.30001726", "", {}, "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -484,7 +488,7 @@ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], - "framer-motion": ["framer-motion@12.23.0", "", { "dependencies": { "motion-dom": "^12.22.0", "motion-utils": "^12.19.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-xf6NxTGAyf7zR4r2KlnhFmsRfKIbjqeBupEDBAaEtVIBJX96sAon00kMlsKButSIRwPSHjbRrAPnYdJJ9kyhbA=="], + "framer-motion": ["framer-motion@12.23.3", "", { "dependencies": { "motion-dom": "^12.23.2", "motion-utils": "^12.23.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-llmLkf44zuIZOPSrE4bl4J0UTg6bav+rlKEfMRKgvDMXqBrUtMg6cspoQeRVK3nqRLxTmAJhfGXk39UDdZD7Kw=="], "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], @@ -614,11 +618,11 @@ "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], - "motion": ["motion@12.23.0", "", { "dependencies": { "framer-motion": "^12.23.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-PPNwblArRH9GRC4F3KtOTiIaYd+mtp324vYq3HIL+ueseoAVqPRK5TPFTAQBcIprfVd0NWo3DLzZSiyWaYFXXQ=="], + "motion": ["motion@12.23.3", "", { "dependencies": { "framer-motion": "^12.23.3", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-7N0Q4c+Xms+wuq2o27rPONLYNOhQUsLRpMi8CcVxv9FFJhGAqRNjJ1hrBCxHh+Rx6B2W2WPbCOLQc+QJ3rw6Rw=="], - "motion-dom": ["motion-dom@12.22.0", "", { "dependencies": { "motion-utils": "^12.19.0" } }, "sha512-ooH7+/BPw9gOsL9VtPhEJHE2m4ltnhMlcGMhEqA0YGNhKof7jdaszvsyThXI6LVIKshJUZ9/CP6HNqQhJfV7kw=="], + "motion-dom": ["motion-dom@12.23.2", "", { "dependencies": { "motion-utils": "^12.23.2" } }, "sha512-73j6xDHX/NvVh5L5oS1ouAVnshsvmApOq4F3VZo5MkYSD/YVsVLal4Qp9wvVgJM9uU2bLZyc0Sn8B9c/MMKk4g=="], - "motion-utils": ["motion-utils@12.19.0", "", {}, "sha512-BuFTHINYmV07pdWs6lj6aI63vr2N4dg0vR+td0rtrdpWOhBzIkEklZyLcvKBoEtwSqx8Jg06vUB5RS0xDiUybw=="], + "motion-utils": ["motion-utils@12.23.2", "", {}, "sha512-cIEXlBlXAOUyiAtR0S+QPQUM9L3Diz23Bo+zM420NvSd/oPQJwg6U+rT+WRTpp0rizMsBGQOsAwhWIfglUcZfA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -626,7 +630,7 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "next": ["next@15.4.0-canary.111", "", { "dependencies": { "@next/env": "15.4.0-canary.111", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.111", "@next/swc-darwin-x64": "15.4.0-canary.111", "@next/swc-linux-arm64-gnu": "15.4.0-canary.111", "@next/swc-linux-arm64-musl": "15.4.0-canary.111", "@next/swc-linux-x64-gnu": "15.4.0-canary.111", "@next/swc-linux-x64-musl": "15.4.0-canary.111", "@next/swc-win32-arm64-msvc": "15.4.0-canary.111", "@next/swc-win32-x64-msvc": "15.4.0-canary.111", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Ar2qfxiFZIqfnZsHGrgq1iSQn9kMpj8eHwJWOYUx/f/v1xHU16XgVojufOg47xWMjHOGxal46FQv68U7MEKOFA=="], + "next": ["next@15.3.5", "", { "dependencies": { "@next/env": "15.3.5", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.5", "@next/swc-darwin-x64": "15.3.5", "@next/swc-linux-arm64-gnu": "15.3.5", "@next/swc-linux-arm64-musl": "15.3.5", "@next/swc-linux-x64-gnu": "15.3.5", "@next/swc-linux-x64-musl": "15.3.5", "@next/swc-win32-arm64-msvc": "15.3.5", "@next/swc-win32-x64-msvc": "15.3.5", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RkazLBMMDJSJ4XZQ81kolSpwiCt907l0xcgcpF4xC2Vml6QVcPNXW0NQRwQ80FFtSn7UM52XN0anaw8TEJXaiw=="], "next-intl": ["next-intl@4.3.4", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", "use-intl": "^4.3.4" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-VWLIDlGbnL/o4LnveJTJD1NOYN8lh3ZAGTWw2krhfgg53as3VsS4jzUVnArJdqvwtlpU/2BIDbWTZ7V4o1jFEw=="], @@ -686,6 +690,8 @@ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], "style-to-js": ["style-to-js@1.1.17", "", { "dependencies": { "style-to-object": "1.0.9" } }, "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA=="], @@ -734,7 +740,7 @@ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - "zod": ["zod@3.25.71", "", {}, "sha512-BsBc/NPk7h8WsUWYWYL+BajcJPY8YhjelaWu2NMLuzgraKAz4Lb4/6K11g9jpuDetjMiqhZ6YaexFLOC0Ogi3Q=="], + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], diff --git a/next.config.ts b/next.config.ts index bad3fc6..3fe7cb0 100644 --- a/next.config.ts +++ b/next.config.ts @@ -11,7 +11,6 @@ const nextConfig: NextConfig = { reactStrictMode: true, experimental: { reactCompiler: true, - ppr: true, }, typescript: { ignoreBuildErrors: true, diff --git a/package.json b/package.json index db11af2..a1ed77b 100644 --- a/package.json +++ b/package.json @@ -13,20 +13,20 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@icons-pack/react-simple-icons": "^13.3.0", + "@icons-pack/react-simple-icons": "^13.4.0", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-slider": "^1.3.5", "@radix-ui/react-slot": "^1.2.3", "@t3-oss/env-nextjs": "^0.13.8", - "@tanstack/react-query": "^5.81.5", - "@tanstack/react-query-devtools": "^5.81.5", + "@tanstack/react-query": "^5.83.0", + "@tanstack/react-query-devtools": "^5.83.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "d3": "^7.9.0", "hast-util-to-jsx-runtime": "^2.3.6", "lucide-react": "^0.525.0", - "motion": "^12.23.0", - "next": "15.4.0-canary.111", + "motion": "^12.23.3", + "next": "15.3.5", "next-intl": "^4.3.4", "nuqs": "^2.4.3", "react": "^19.1.0", @@ -36,13 +36,13 @@ "shiki": "^3.7.0", "tailwind-merge": "^3.3.1", "tw-animate-css": "^1.3.5", - "zod": "^3.25.71" + "zod": "^3.25.76" }, "devDependencies": { - "@biomejs/biome": "^2.0.6", + "@biomejs/biome": "2.0.0", "@tailwindcss/postcss": "^4.1.11", "@types/d3": "^7.4.3", - "@types/node": "^24.0.10", + "@types/node": "^24.0.13", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "babel-plugin-react-compiler": "^19.1.0-rc.2", From adbf029d910ae37e9696fc0e1b79501566e533ee Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:40:18 +0200 Subject: [PATCH 02/18] chore(biome): try to mitigate lsp memory leak issue --- biome.jsonc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/biome.jsonc b/biome.jsonc index c835a21..e164054 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -5,7 +5,10 @@ "clientKind": "git", "useIgnoreFile": true }, - "files": { "ignoreUnknown": false, "includes": ["**"] }, + "files": { "ignoreUnknown": true, "includes": ["src/**/*.{ts,tsx}"], "experimentalScannerIgnores": [ + "node_modules", + ".next" + ] }, "formatter": { "enabled": true }, "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { From e694c01ac02308128c34f13bf146c6af204a5f8b Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:40:52 +0200 Subject: [PATCH 03/18] fix: typo --- src/lib/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 001e362..c9d3315 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -12,13 +12,13 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } -export const ALGORITMS = [ +export const ALGORITHMS = [ "bubble", "selection", "insertion", "quicksort", ] as const; -export type Algorithm = (typeof ALGORITMS)[number]; +export type Algorithm = (typeof ALGORITHMS)[number]; export const playgroundSearchParams = { algorithm: parseAsStringLiteral(ALGORITMS).withDefault("bubble"), From fb2662bf35f8bee87038fa06ab427ac83472640f Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:41:38 +0200 Subject: [PATCH 04/18] fix: remove unnecessary useEffect --- src/components/sorting-visualizer.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/sorting-visualizer.tsx b/src/components/sorting-visualizer.tsx index d35d3c1..82ecdc4 100644 --- a/src/components/sorting-visualizer.tsx +++ b/src/components/sorting-visualizer.tsx @@ -82,6 +82,7 @@ export default function SortingVisualizer({ // Initialize data on component mount useEffect(() => { + d3.select(svgRef.current).selectAll("*").remove(); generateRandomData(arrayLength); return () => { // Cleanup any animations on unmount @@ -91,10 +92,6 @@ export default function SortingVisualizer({ }; }, [arrayLength, generateRandomData]); - useEffect(() => { - d3.select(svgRef.current).selectAll("*").remove(); - }, [arrayLength]); - // Draw the visualization whenever data changes useEffect(() => { if (!svgRef.current || data.length === 0) { From 389e1db752931a7ecf788e2b5ad415ff47654ad4 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:48:36 +0200 Subject: [PATCH 05/18] fix: use next-intl Link component --- src/app/[locale]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx index 5eba0df..0f86735 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/[locale]/page.tsx @@ -9,11 +9,11 @@ import { Split, } from "lucide-react"; import { motion } from "motion/react"; -import Link from "next/link"; import { useTranslations } from "next-intl"; import { BubbleBackground } from "@/components/animate-ui/bubble-background"; import { GradientText } from "@/components/animate-ui/gradient-text"; import { RippleButton } from "@/components/animate-ui/ripple-button"; +import { Link } from "@/i18n/navigation"; export default function HomePage() { const t = useTranslations("home"); From 2c601eb070c853bcc839737039e8f7276be9c9c4 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:52:02 +0200 Subject: [PATCH 06/18] fix: remove redundant check in layout --- src/app/[locale]/layout.tsx | 12 ++++++------ src/app/{[locale] => }/globals.css | 0 2 files changed, 6 insertions(+), 6 deletions(-) rename src/app/{[locale] => }/globals.css (100%) diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index a27da00..408e485 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -1,12 +1,12 @@ +import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; -import { notFound } from "next/navigation"; -import { hasLocale, type Locale, NextIntlClientProvider } from "next-intl"; +import { type Locale, NextIntlClientProvider } from "next-intl"; import { getTranslations, setRequestLocale } from "next-intl/server"; -import { routing } from "@/i18n/routing"; -import "./globals.css"; import Footer from "@/components/footer"; import Navigation from "@/components/navigation"; import Providers from "@/components/providers"; +import { routing } from "@/i18n/routing"; +import "@/app/globals.css"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -18,6 +18,7 @@ const geistMono = Geist_Mono({ subsets: ["latin"], }); +export const dynamicParams = false; export function generateStaticParams() { return routing.locales.map((locale) => ({ locale })); } @@ -26,7 +27,7 @@ export async function generateMetadata({ params, }: { params: Promise<{ locale: Locale }>; -}) { +}): Promise { const { locale } = await params; const t = await getTranslations({ locale, namespace: "metadata" }); @@ -44,7 +45,6 @@ export default async function LocaleLayout({ params: Promise<{ locale: Locale }>; }) { const { locale } = await params; - if (!hasLocale(routing.locales, locale)) notFound(); // Enable static rendering setRequestLocale(locale); diff --git a/src/app/[locale]/globals.css b/src/app/globals.css similarity index 100% rename from src/app/[locale]/globals.css rename to src/app/globals.css From 298219b024b411de4a7961df6320344490dc7156 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:53:33 +0200 Subject: [PATCH 07/18] refactor(navigation): get link active status with recommended method by next-intl docs --- src/components/navigation.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index 993c6a7..4071e0c 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -2,6 +2,7 @@ import { motion } from "motion/react"; import Image from "next/image"; +import { useSelectedLayoutSegment } from "next/navigation"; import { useTranslations } from "next-intl"; import { useEffect, useState } from "react"; import { Link, usePathname } from "@/i18n/navigation"; @@ -75,7 +76,6 @@ export default function Navbar() { function NavLink({ name, href, - isActive, className, }: { name: "home" | "playground"; @@ -85,8 +85,13 @@ function NavLink({ }) { const t = useTranslations("navigation"); + const selectedLayoutSegment = useSelectedLayoutSegment(); + const pathname = selectedLayoutSegment ? `/${selectedLayoutSegment}` : "/"; + const isActive = pathname === href; + return ( Date: Sat, 12 Jul 2025 20:54:10 +0200 Subject: [PATCH 08/18] feat(locale-switcher): maintain searchParams when locale is changed --- src/components/locale-switcher.tsx | 14 ++++++++++++-- src/components/navigation.tsx | 6 ++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/locale-switcher.tsx b/src/components/locale-switcher.tsx index a866271..6f749a8 100644 --- a/src/components/locale-switcher.tsx +++ b/src/components/locale-switcher.tsx @@ -1,7 +1,7 @@ "use client"; import { Globe } from "lucide-react"; -import { useParams } from "next/navigation"; +import { useParams, useSearchParams } from "next/navigation"; import { type Locale, useLocale, useTranslations } from "next-intl"; import { useTransition } from "react"; import { useIsMobile } from "@/hooks"; @@ -24,12 +24,22 @@ export default function LocaleSwitcher({ className }: { className?: string }) { const [isPending, startTransition] = useTransition(); const pathname = usePathname(); const params = useParams(); + const searchParams = useSearchParams(); const isMobile = useIsMobile(); function onSelectChange(val: Locale) { startTransition(() => { - router.replace({ pathname, query: params }, { locale: val }); + router.replace( + { + pathname, + query: { + ...params, + ...Object.fromEntries(searchParams.entries()), + }, + }, + { locale: val }, + ); }); } diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index 4071e0c..b7b1d7f 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -4,7 +4,7 @@ import { motion } from "motion/react"; import Image from "next/image"; import { useSelectedLayoutSegment } from "next/navigation"; import { useTranslations } from "next-intl"; -import { useEffect, useState } from "react"; +import { Suspense, useEffect, useState } from "react"; import { Link, usePathname } from "@/i18n/navigation"; import { cn } from "@/lib/utils"; import LocaleSwitcher from "./locale-switcher"; @@ -66,7 +66,9 @@ export default function Navbar() { {/* Language Selector */} - + + + From 07a382d4668360f0ed188fd9a3d1e842f48e3cf4 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 20:56:09 +0200 Subject: [PATCH 09/18] feat: use static path for visualizer algorithm and generate highlighter data on build --- .../[locale]/playground/[algorithm]/page.tsx | 32 ++++++ src/app/[locale]/playground/page.tsx | 28 +---- src/components/algorithm-explanation.tsx | 11 +- src/components/playground-state.tsx | 33 +++--- src/components/sorting-visualizer.tsx | 101 +++++++++++++++++- src/lib/utils.ts | 7 +- 6 files changed, 158 insertions(+), 54 deletions(-) create mode 100644 src/app/[locale]/playground/[algorithm]/page.tsx diff --git a/src/app/[locale]/playground/[algorithm]/page.tsx b/src/app/[locale]/playground/[algorithm]/page.tsx new file mode 100644 index 0000000..92f89a2 --- /dev/null +++ b/src/app/[locale]/playground/[algorithm]/page.tsx @@ -0,0 +1,32 @@ +import { HexagonBackground } from "@/components/animate-ui/hexagon-background"; +import PlaygroundState from "@/components/playground-state"; +import { highlight } from "@/lib/highlight"; +import { ALGORITHMS, type Algorithm, getAlgorithmCode } from "@/lib/utils"; + +export const dynamicParams = false; +export function generateStaticParams() { + return ALGORITHMS.map((algorithm) => ({ algorithm })); +} + +export default async function Playground({ + params, +}: { + params: Promise<{ algorithm: Algorithm }>; +}) { + const { algorithm } = (await params) as { algorithm: Algorithm }; + + const highlightedAlgorithmCode = await highlight( + getAlgorithmCode(algorithm), + "ts", + ); + + return ( +
+ + +
+ ); +} diff --git a/src/app/[locale]/playground/page.tsx b/src/app/[locale]/playground/page.tsx index a6e4f50..10179dd 100644 --- a/src/app/[locale]/playground/page.tsx +++ b/src/app/[locale]/playground/page.tsx @@ -1,27 +1,5 @@ -import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; -import type { SearchParams } from "nuqs/server"; -import { HexagonBackground } from "@/components/animate-ui/hexagon-background"; -import PlaygroundState from "@/components/playground-state"; -import { getQueryClient } from "@/lib/get-query-client"; -import { codeHighlightOptions, loadSearchParams } from "@/lib/utils"; +import { redirect } from "next/navigation"; -export default async function Playground({ - searchParams, -}: { - searchParams: Promise; -}) { - const { algorithm } = await loadSearchParams(searchParams); - const queryClient = getQueryClient(); - - await queryClient.prefetchQuery(codeHighlightOptions(algorithm)); - - return ( -
- - - - - -
- ); +export default function RootPlayground() { + redirect("/playground/bubble"); } diff --git a/src/components/algorithm-explanation.tsx b/src/components/algorithm-explanation.tsx index 17014d6..691da3a 100644 --- a/src/components/algorithm-explanation.tsx +++ b/src/components/algorithm-explanation.tsx @@ -3,7 +3,7 @@ import { useQuery } from "@tanstack/react-query"; import { Check, Copy, Loader2 } from "lucide-react"; import { useTranslations } from "next-intl"; -import { useState } from "react"; +import { type JSX, useState } from "react"; import { Tabs, TabsContent, @@ -30,6 +30,7 @@ interface AlgorithmExplanationProps { algorithm: Algorithm; iterations: number; swaps: number; + highlightedAlgorithmCode: JSX.Element; className?: string; } @@ -37,13 +38,15 @@ export default function AlgorithmExplanation({ algorithm, iterations, swaps, + highlightedAlgorithmCode, className, }: AlgorithmExplanationProps) { const t = useTranslations("algorithm-explanation"); - const { data, isPending, isError } = useQuery( - codeHighlightOptions(algorithm), - ); + const { data, isPending, isError } = useQuery({ + ...codeHighlightOptions(algorithm), + initialData: highlightedAlgorithmCode, + }); const [copied, setCopied] = useState(null); diff --git a/src/components/playground-state.tsx b/src/components/playground-state.tsx index 059df18..f1dd83a 100644 --- a/src/components/playground-state.tsx +++ b/src/components/playground-state.tsx @@ -1,29 +1,32 @@ "use client"; -import { useQueryState } from "nuqs"; -import { useState } from "react"; -import { playgroundSearchParams } from "@/lib/utils"; +import { type JSX, Suspense, useState } from "react"; +import type { Algorithm } from "@/lib/utils"; import AlgorithmExplanation from "./algorithm-explanation"; -import SortingVisualizer from "./sorting-visualizer"; +import SortingVisualizer, { VisualizerSkeleton } from "./sorting-visualizer"; -export default function PlaygroundState() { - const [algorithm, setAlgorithm] = useQueryState( - "algorithm", - playgroundSearchParams.algorithm, - ); +export default function PlaygroundState({ + algorithm, + highlightedAlgorithmCode, +}: { + algorithm: Algorithm; + highlightedAlgorithmCode: JSX.Element; +}) { const [iterations, setIterations] = useState(0); const [swaps, setSwaps] = useState(0); return (
- + }> + + number) | number) => void; export default function SortingVisualizer({ algorithm, - setAlgorithm, setIterations, setSwaps, className, }: { algorithm: Algorithm; - setAlgorithm: (algorithm: Algorithm) => void; setIterations: NumberSetState; setSwaps: NumberSetState; className?: string; }) { const t = useTranslations("visualizer"); + const router = useRouter(); + const [arrayLength, setArrayLength] = useQueryState( "length", playgroundSearchParams.length, @@ -835,7 +836,15 @@ export default function SortingVisualizer({ + + + + + Bubble Sort + Selection Sort + Insertion Sort + Quick Sort + + +
+ + +
+ +
+ +
+
+
+ {t("unsorted")} +
+
+
+ {t("comparing")} +
+
+
+ + {algorithm === "quicksort" ? t("pivot") : t("current")} + +
+
+
+ {t("sorted")} +
+ +
+ {t("length")}: + + +
+
+
+ ); +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index c9d3315..7a29d34 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,10 +1,6 @@ import { queryOptions } from "@tanstack/react-query"; import { type ClassValue, clsx } from "clsx"; -import { - createLoader, - parseAsInteger, - parseAsStringLiteral, -} from "nuqs/server"; +import { createLoader, parseAsInteger } from "nuqs/server"; import { twMerge } from "tailwind-merge"; import { highlight } from "./highlight"; @@ -21,7 +17,6 @@ export const ALGORITHMS = [ export type Algorithm = (typeof ALGORITHMS)[number]; export const playgroundSearchParams = { - algorithm: parseAsStringLiteral(ALGORITMS).withDefault("bubble"), speed: parseAsInteger.withDefault(50), length: parseAsInteger.withDefault(20), }; From a08f3fbe9d10becf70567d2fef95f261debf3a5d Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 21:02:42 +0200 Subject: [PATCH 10/18] chore: lint --- biome.jsonc | 9 +++++---- src/components/animate-ui/bubble-background.tsx | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/biome.jsonc b/biome.jsonc index e164054..7fa5c7c 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -5,10 +5,11 @@ "clientKind": "git", "useIgnoreFile": true }, - "files": { "ignoreUnknown": true, "includes": ["src/**/*.{ts,tsx}"], "experimentalScannerIgnores": [ - "node_modules", - ".next" - ] }, + "files": { + "ignoreUnknown": true, + "includes": ["src/**/*.{ts,tsx}"], + "experimentalScannerIgnores": ["node_modules", ".next"] + }, "formatter": { "enabled": true }, "assist": { "actions": { "source": { "organizeImports": "on" } } }, "linter": { diff --git a/src/components/animate-ui/bubble-background.tsx b/src/components/animate-ui/bubble-background.tsx index 6f8c9c8..476738f 100644 --- a/src/components/animate-ui/bubble-background.tsx +++ b/src/components/animate-ui/bubble-background.tsx @@ -39,6 +39,8 @@ function BubbleBackground({ }, ...props }: BubbleBackgroundProps) { + const filterId = React.useId(); + const isMobile = useIsMobile(); const containerRef = React.useRef(null); @@ -97,7 +99,7 @@ function BubbleBackground({ style={{ transform: "translate3d(0, 0, 0)" }} > - + From 61682d7742de6e136742686aaf2465225daa67e6 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 21:39:27 +0200 Subject: [PATCH 11/18] fix(home): use new path to link to playground instead of queryParams --- src/app/[locale]/page.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx index 0f86735..aef50a3 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/[locale]/page.tsx @@ -166,7 +166,7 @@ export default function HomePage() { ), description: t("algorithms.bubble-sort.description"), - queryParam: "bubble", + path: "bubble", }, { name: t("algorithms.selection-sort.title"), @@ -174,7 +174,7 @@ export default function HomePage() { ), description: t("algorithms.selection-sort.description"), - queryParam: "selection", + path: "selection", }, { name: t("algorithms.insertion-sort.title"), @@ -182,7 +182,7 @@ export default function HomePage() { ), description: t("algorithms.insertion-sort.description"), - queryParam: "insertion", + path: "insertion", }, { name: t("algorithms.quick-sort.title"), @@ -190,7 +190,7 @@ export default function HomePage() { ), description: t("algorithms.quick-sort.description"), - queryParam: "quicksort", + path: "quicksort", }, ].map((algorithm, index) => ( From ea61623386e48cd32f35028f4fdc964b2ae8b8e9 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 21:40:20 +0200 Subject: [PATCH 12/18] feat: redirect `/playground` to `/playground/bubble` using `next.config.ts` instead of manual redirect to avoid flashing page --- next.config.ts | 9 +++++++++ src/app/[locale]/playground/page.tsx | 5 ----- 2 files changed, 9 insertions(+), 5 deletions(-) delete mode 100644 src/app/[locale]/playground/page.tsx diff --git a/next.config.ts b/next.config.ts index 3fe7cb0..c2a9fee 100644 --- a/next.config.ts +++ b/next.config.ts @@ -6,6 +6,7 @@ import createNextIntlPlugin from "next-intl/plugin"; * for Docker builds. */ import "@/env"; +import { routing } from "@/i18n/routing"; const nextConfig: NextConfig = { reactStrictMode: true, @@ -15,6 +16,14 @@ const nextConfig: NextConfig = { typescript: { ignoreBuildErrors: true, }, + + async redirects() { + return [...routing.locales.map(locale => ({ + source: `/${locale}/playground`, + destination: `/${locale}/playground/bubble`, + permanent: true, + }))] + } }; const withNextIntl = createNextIntlPlugin(); diff --git a/src/app/[locale]/playground/page.tsx b/src/app/[locale]/playground/page.tsx deleted file mode 100644 index 10179dd..0000000 --- a/src/app/[locale]/playground/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { redirect } from "next/navigation"; - -export default function RootPlayground() { - redirect("/playground/bubble"); -} From be88e74b24634fe8faf6dbfb8bebdfb081c9805e Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 12 Jul 2025 22:15:42 +0200 Subject: [PATCH 13/18] chore: add ul and li inside nav --- src/components/navigation.tsx | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/components/navigation.tsx b/src/components/navigation.tsx index b7b1d7f..3ab77e7 100644 --- a/src/components/navigation.tsx +++ b/src/components/navigation.tsx @@ -50,19 +50,25 @@ export default function Navbar() { {/* Navigation */} -