diff --git a/package-lock.json b/package-lock.json index 47931eb..2416fec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,9 @@ "@radix-ui/react-toggle": "^1.1.9", "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-tooltip": "^1.2.7", + "@sinm/react-file-tree": "^1.1.1", + "@uiw/codemirror-theme-vscode": "^4.25.4", + "@uiw/react-codemirror": "^4.25.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -51,7 +54,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", - "@types/node": "^20", + "@types/node": "^20.19.30", "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4", @@ -81,6 +84,99 @@ "node": ">=6.9.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", + "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.1.tgz", + "integrity": "sha512-uWDWFypNdQmz2y1LaNJzK7fL7TYKLeUAU0npEC685OKTF3KcQ2Vu3klIM78D7I6wGhktme0lh3CuQLv0ZCrD9Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz", + "integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.3.tgz", + "integrity": "sha512-y3YkYhdnhjDBAe0VIA0c4wVoFOvnp8CnAvfLqi0TqotIv92wIlAAP7HELOpLBsKwjAX6W92rSflA6an/2zBvXw==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz", + "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz", + "integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.39.12", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.12.tgz", + "integrity": "sha512-f+/VsHVn/kOA9lltk/GFzuYwVVAKmOnNjxbrhkk3tPHntFqjWeI2TbIXx006YkBkqC10wZ4NsnWXCQiFPeAISQ==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@date-fns/tz": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz", @@ -651,6 +747,36 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.0.tgz", + "integrity": "sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA==", + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", + "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, "node_modules/@next/env": { "version": "16.1.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", @@ -2428,6 +2554,16 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, + "node_modules/@sinm/react-file-tree": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@sinm/react-file-tree/-/react-file-tree-1.1.1.tgz", + "integrity": "sha512-XlHLLNV18zWx7mYEH5gtlBb7jYFX7GaBA5jmhsG9YXhgwyyK33JgymrbU+IHDDBakAoZ+xISLKzu2ldzKbA/wg==", + "license": "MIT", + "dependencies": { + "@types/react-virtualized": "^9.21.15", + "react-virtualized": "^9.22.3" + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -2781,11 +2917,16 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", - "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2801,6 +2942,100 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-virtualized": { + "version": "9.22.3", + "resolved": "https://registry.npmjs.org/@types/react-virtualized/-/react-virtualized-9.22.3.tgz", + "integrity": "sha512-UKRWeBIrECaKhE4O//TSFhlgwntMwyiEIOA7WZoVkr52Jahv0dH6YIOorqc358N2V3oKFclsq5XxPmx2PiYB5A==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "@types/react": "*" + } + }, + "node_modules/@uiw/codemirror-extensions-basic-setup": { + "version": "4.25.4", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.4.tgz", + "integrity": "sha512-YzNwkm0AbPv1EXhCHYR5v0nqfemG2jEB0Z3Att4rBYqKrlG7AA9Rhjc3IyBaOzsBu18wtrp9/+uhTyu7TXSRng==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/autocomplete": ">=6.0.0", + "@codemirror/commands": ">=6.0.0", + "@codemirror/language": ">=6.0.0", + "@codemirror/lint": ">=6.0.0", + "@codemirror/search": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, + "node_modules/@uiw/codemirror-theme-vscode": { + "version": "4.25.4", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-vscode/-/codemirror-theme-vscode-4.25.4.tgz", + "integrity": "sha512-9ob5EtLqrXBFl8uf4eFRkXjyjfyfBRVsJdt7xbc33f+2/I29/2v2nEdU/xw40+dhloxF/h1Ry281f8wAs97MWQ==", + "license": "MIT", + "dependencies": { + "@uiw/codemirror-themes": "4.25.4" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/@uiw/codemirror-themes": { + "version": "4.25.4", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.4.tgz", + "integrity": "sha512-2SLktItgcZC4p0+PfFusEbAHwbuAWe3bOOntCevVgHtrWGtGZX3IPv2k8IKZMgOXtAHyGKpJvT9/nspPn/uCQg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/language": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, + "node_modules/@uiw/react-codemirror": { + "version": "4.25.4", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.4.tgz", + "integrity": "sha512-ipO067oyfUw+DVaXhQCxkB0ZD9b7RnY+ByrprSYSKCHaULvJ3sqWYC/Zen6zVQ8/XC4o5EPBfatGiX20kC7XGA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@codemirror/commands": "^6.1.0", + "@codemirror/state": "^6.1.1", + "@codemirror/theme-one-dark": "^6.0.0", + "@uiw/codemirror-extensions-basic-setup": "4.25.4", + "codemirror": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.11.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/theme-one-dark": ">=6.0.0", + "@codemirror/view": ">=6.0.0", + "codemirror": ">=6.0.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, "node_modules/aria-hidden": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", @@ -2885,6 +3120,27 @@ "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -3664,6 +3920,12 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, "node_modules/react-remove-scroll": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", @@ -3764,6 +4026,33 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-virtualized": { + "version": "9.22.6", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.6.tgz", + "integrity": "sha512-U5j7KuUQt3AaMatlMJ0UJddqSiX+Km0YJxSqbAzIiGw5EmNz0khMyqP2hzgu4+QUtm+QPIrxzUX4raJxmVJnHg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.7.2", + "clsx": "^1.0.4", + "dom-helpers": "^5.1.3", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-virtualized/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/recharts": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", @@ -3869,6 +4158,12 @@ "node": ">=0.10.0" } }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -4047,6 +4342,12 @@ "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" } } } diff --git a/package.json b/package.json index 083f6bd..924bf88 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "@radix-ui/react-toggle": "^1.1.9", "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-tooltip": "^1.2.7", + "@sinm/react-file-tree": "^1.1.1", + "@uiw/codemirror-theme-vscode": "^4.25.4", + "@uiw/react-codemirror": "^4.25.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -51,7 +54,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", - "@types/node": "^20", + "@types/node": "^20.19.30", "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4", diff --git a/src/app/globals.css b/src/app/globals.css index 604679a..94ebdb5 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -154,7 +154,7 @@ .hover-underline::after { content: ""; - @apply absolute bottom-0 left-0 w-0 h-[2px] bg-primary transition-all duration-300; + @apply absolute bottom-0 left-0 w-0 h-0.5 bg-primary transition-all duration-300; } .hover-underline:hover::after { @@ -178,15 +178,16 @@ } .glow-gold { - box-shadow: 0 0 20px color-mix(in oklch, var(--chart-4) 40%, transparent); + box-shadow: 0 0 20px color-mix(in oklch, oklch(82.8% 0.189 84.429) 70%, transparent); + /* box-shadow: 0 0 20px color-mix(in oklch, var(--chart-4) 40%, transparent); */ } .glow-silver { - box-shadow: 0 0 20px color-mix(in oklch, var(--foreground) 30%, transparent); + box-shadow: 0 0 20px color-mix(in oklch, var(--foreground) 60%, transparent); } .glow-bronze { - box-shadow: 0 0 20px color-mix(in oklch, var(--chart-5) 30%, transparent); + box-shadow: 0 0 20px color-mix(in oklch, oklch(55.5% 0.163 48.998) 60%, transparent); } /* ===== Text glow ===== */ @@ -201,7 +202,7 @@ .hover-underline::after { content: ""; - @apply absolute left-0 bottom-0 h-[2px] w-0 bg-primary transition-all duration-300; + @apply absolute left-0 bottom-0 h-0.5 w-0 bg-primary transition-all duration-300; } .hover-underline:hover::after { @@ -232,15 +233,15 @@ /* ===== Rank badges ===== */ .rank-gold { - @apply bg-gradient-to-r from-yellow-500 to-amber-400 text-black; + @apply bg-linear-to-r from-yellow-500 to-amber-400 text-black; } .rank-silver { - @apply bg-gradient-to-r from-gray-300 to-gray-400 text-black; + @apply bg-linear-to-r from-gray-300 to-gray-400 text-black; } .rank-bronze { - @apply bg-gradient-to-r from-orange-600 to-amber-700 text-white; + @apply bg-linear-to-r from-orange-600 to-amber-700 text-white; } /* ===== Difficulty badges ===== */ @@ -311,4 +312,8 @@ transform: translateY(0); } } +} + +.cm-editor { + height: 100%;; } \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 51c2923..87e856d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -31,9 +31,7 @@ export default function RootLayout({ >
-
- {children} -
+
{children}
diff --git a/src/app/leaderboards/page.tsx b/src/app/leaderboards/page.tsx index 8eada98..93974f9 100644 --- a/src/app/leaderboards/page.tsx +++ b/src/app/leaderboards/page.tsx @@ -5,154 +5,301 @@ import LeaderboardRow from "@/components/app/LeaderboardRow"; import { useState } from "react"; const Leaderboards = () => { - const [activeTab, setActiveTab] = useState<"global" | "weekly">("global"); + const [activeTab, setActiveTab] = useState<"global" | "weekly">("global"); - const globalLeaderboard = [ - { rank: 1, username: "speedmaster_x", pb: "2:34.21", runsCount: 2341, category: "Any%" }, - { rank: 2, username: "algo_ninja", pb: "2:38.54", runsCount: 1876, category: "Any%" }, - { rank: 3, username: "clockwork", pb: "2:41.02", runsCount: 3102, category: "Any%" }, - { rank: 4, username: "fastfingers99", pb: "2:45.33", runsCount: 987, category: "Any%" }, - { rank: 5, username: "recursion_queen", pb: "2:47.89", runsCount: 1543, category: "Any%" }, - { rank: 6, username: "bitshifter", pb: "2:52.11", runsCount: 2109, category: "Any%" }, - { rank: 7, username: "nullpointer", pb: "2:54.67", runsCount: 876, category: "Any%" }, - { rank: 8, username: "stackattack", pb: "2:58.23", runsCount: 1234, category: "Any%" }, - { rank: 9, username: "looplegend", pb: "3:01.45", runsCount: 654, category: "Any%" }, - { rank: 10, username: "coderunner", pb: "3:05.78", runsCount: 1098, category: "Any%" }, - ]; + const globalLeaderboard = [ + { + rank: 1, + username: "speedmaster_x", + pb: "2:34.21", + runsCount: 2341, + category: "Any%", + }, + { + rank: 2, + username: "algo_ninja", + pb: "2:38.54", + runsCount: 1876, + category: "Any%", + }, + { + rank: 3, + username: "clockwork", + pb: "2:41.02", + runsCount: 3102, + category: "Any%", + }, + { + rank: 4, + username: "fastfingers99", + pb: "2:45.33", + runsCount: 987, + category: "Any%", + }, + { + rank: 5, + username: "recursion_queen", + pb: "2:47.89", + runsCount: 1543, + category: "Any%", + }, + { + rank: 6, + username: "bitshifter", + pb: "2:52.11", + runsCount: 2109, + category: "Any%", + }, + { + rank: 7, + username: "nullpointer", + pb: "2:54.67", + runsCount: 876, + category: "Any%", + }, + { + rank: 8, + username: "stackattack", + pb: "2:58.23", + runsCount: 1234, + category: "Any%", + }, + { + rank: 9, + username: "looplegend", + pb: "3:01.45", + runsCount: 654, + category: "Any%", + }, + { + rank: 10, + username: "coderunner", + pb: "3:05.78", + runsCount: 1098, + category: "Any%", + }, + ]; - const weeklyLeaderboard = [ - { rank: 1, username: "weekly_warrior", pb: "2:51.33", runsCount: 156, category: "Any%" }, - { rank: 2, username: "speedmaster_x", pb: "2:53.12", runsCount: 89, category: "Any%" }, - { rank: 3, username: "newbie_grinder", pb: "2:58.67", runsCount: 234, category: "Any%" }, - { rank: 4, username: "algo_ninja", pb: "3:02.45", runsCount: 67, category: "Any%" }, - { rank: 5, username: "fresh_runner", pb: "3:05.89", runsCount: 45, category: "Any%" }, - { rank: 6, username: "practice_makes", pb: "3:08.12", runsCount: 112, category: "Any%" }, - { rank: 7, username: "splitmaster", pb: "3:12.34", runsCount: 78, category: "Any%" }, - { rank: 8, username: "grindtime", pb: "3:15.56", runsCount: 92, category: "Any%" }, - { rank: 9, username: "pb_hunter", pb: "3:18.90", runsCount: 56, category: "Any%" }, - { rank: 10, username: "reset_king", pb: "3:22.11", runsCount: 187, category: "Any%" }, - ]; + const weeklyLeaderboard = [ + { + rank: 1, + username: "weekly_warrior", + pb: "2:51.33", + runsCount: 156, + category: "Any%", + }, + { + rank: 2, + username: "speedmaster_x", + pb: "2:53.12", + runsCount: 89, + category: "Any%", + }, + { + rank: 3, + username: "newbie_grinder", + pb: "2:58.67", + runsCount: 234, + category: "Any%", + }, + { + rank: 4, + username: "algo_ninja", + pb: "3:02.45", + runsCount: 67, + category: "Any%", + }, + { + rank: 5, + username: "fresh_runner", + pb: "3:05.89", + runsCount: 45, + category: "Any%", + }, + { + rank: 6, + username: "practice_makes", + pb: "3:08.12", + runsCount: 112, + category: "Any%", + }, + { + rank: 7, + username: "splitmaster", + pb: "3:12.34", + runsCount: 78, + category: "Any%", + }, + { + rank: 8, + username: "grindtime", + pb: "3:15.56", + runsCount: 92, + category: "Any%", + }, + { + rank: 9, + username: "pb_hunter", + pb: "3:18.90", + runsCount: 56, + category: "Any%", + }, + { + rank: 10, + username: "reset_king", + pb: "3:22.11", + runsCount: 187, + category: "Any%", + }, + ]; - const categories = ["Any%", "100%", "Easy Only", "No DP", "Blind"]; + const categories = ["Any%", "100%", "Easy Only", "No DP", "Blind"]; - return ( -
-
- {/* Header */} -
-

Leaderboards

-

The fastest runners in the game. Will you claim a spot?

-
- - {/* Stats Row */} -
-
- -

12,453

-

Total Runners

-
-
- -

2:34.21

-

World Record

-
-
- -

847K

-

Runs This Week

-
-
- -

5d 12h

-

Until Reset

-
-
- - {/* Tabs and Filters */} -
- {/* Global/Weekly Toggle */} -
- - -
- - {/* Category Filter */} -
- {categories.map((cat, i) => ( - - ))} -
-
- - {/* Leaderboard Table */} -
- - - - - - - - - - - - {(activeTab === "global" ? globalLeaderboard : weeklyLeaderboard).map((entry, i) => ( - - ))} - -
RankRunnerPBRunsCategory
-
- - {/* Pagination */} -
- - {[1, 2, 3, '...', 50].map((page, i) => ( - - ))} - -
-
+ return ( +
+
+ {/* Header */} +
+

+ Leaderboards +

+

+ The fastest runners in the game. Will you claim a spot? +

- ); + + {/* Stats Row */} +
+
+ +

+ 12,453 +

+

Total Runners

+
+
+ +

+ 2:34.21 +

+

World Record

+
+
+ +

847K

+

Runs This Week

+
+
+ +

+ 5d 12h +

+

Until Reset

+
+
+ + {/* Tabs and Filters */} +
+ {/* Global/Weekly Toggle */} +
+ + +
+ + {/* Category Filter */} +
+ {categories.map((cat, i) => ( + + ))} +
+
+ + {/* Leaderboard Table */} +
+ + + + + + + + + + + + {(activeTab === "global" + ? globalLeaderboard + : weeklyLeaderboard + ).map((entry, i) => ( + + ))} + +
+ Rank + + Runner + + PB + + Runs + + Category +
+
+ + {/* Pagination */} +
+ + {[1, 2, 3, 50].map((page, i) => ( + + ))} + +
+
+
+ ); }; export default Leaderboards; diff --git a/src/app/page.tsx b/src/app/page.tsx index bce7688..dfb6439 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,22 +9,26 @@ const Landing = () => { { icon: Timer, title: "Race the Clock", - description: "Built-in timer with splits tracking. Every millisecond counts in your pursuit of the perfect run.", + description: + "Built-in timer with splits tracking. Every millisecond counts in your pursuit of the perfect run.", }, { icon: Trophy, title: "Compete Globally", - description: "Weekly and all-time leaderboards. Claim your spot among the fastest runners worldwide.", + description: + "Weekly and all-time leaderboards. Claim your spot among the fastest runners worldwide.", }, { icon: Zap, title: "Quick Runs", - description: "No signup walls. Jump into a run instantly and start grinding for that PB.", + description: + "No signup walls. Jump into a run instantly and start grinding for that PB.", }, { icon: Users, title: "Community First", - description: "Share your runs, compare splits, and celebrate clean runs with fellow speedrunners.", + description: + "Share your runs, compare splits, and celebrate clean runs with fellow speedrunners.", }, ]; @@ -38,29 +42,41 @@ const Landing = () => { return (
{/* Hero Section */} -
+
{/* Background gradient */}
-
+
{/* Headline */} -

+

Code Fast.{" "} Run Faster.

{/* Subheadline */} -

- DevRun is competitive programming reimagined. Race the clock through algorithmic challenges, - track your splits, and compete for the fastest runs on the global leaderboard. +

+ Race the clock through challenges, track your splits, and compete + for the fastest runs on the global leaderboard.

{/* CTA Buttons */} -
+
@@ -110,13 +133,20 @@ const Landing = () => {
-

{feature.title}

-

{feature.description}

+

+ {feature.title} +

+

+ {feature.description} +

))}
@@ -127,7 +157,10 @@ const Landing = () => {
@@ -136,7 +169,8 @@ const Landing = () => { Ready to chase your PB?

- Join thousands of runners pushing the limits of competitive programming. + Join thousands of runners pushing the limits of competitive + programming.

- ))} -
- - {/* Difficulty Filter */} -
- {difficulties.map((diff) => ( - - ))} -
- - {/* View Toggle */} -
- - -
-
- - {/* Problem Grid */} -
- {problems.map((problem, i) => ( - - ))} -
- - {/* Load More */} -
- -
-
+ return ( +
+
+ {/* Header */} +
+

Problems

+

+ Choose your battleground. Each run brings you closer to your PB. +

- ); + + {/* Filters */} +
+ {/* Search */} +
+ + +
+ + {/* Category Filter */} +
+ {categories.map((cat) => ( + + ))} +
+ + {/* Difficulty Filter */} +
+ {difficulties.map((diff) => ( + + ))} +
+ + {/* View Toggle */} +
+ + +
+
+ + {/* Problem Grid */} +
+ {problems.map((problem, i) => ( + + ))} +
+
+
+ ); }; export default Problems; diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 114ac1f..30bda86 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -1,190 +1,350 @@ -import { Trophy, Timer, Zap, Target, Calendar, TrendingUp, Award } from "lucide-react"; +import { + Trophy, + Timer, + Zap, + Target, + Calendar, + TrendingUp, + Award, +} from "lucide-react"; import StatCard from "@/components/app/StatCard"; const Profile = () => { - const user = { - username: "speedmaster_x", - joinDate: "Jan 2024", - rank: 1, - totalRuns: 2341, - pb: "2:34.21", - cleanRuns: 892, - avgTime: "3:12.45", - favoriteCategory: "Any%", - }; + const user = { + username: "speedmaster_x", + joinDate: "Jan 2024", + rank: 1, + totalRuns: 2341, + pb: "2:34.21", + cleanRuns: 892, + avgTime: "3:12.45", + favoriteCategory: "Any%", + }; - const recentRuns = [ - { id: 1, category: "Any%", time: "2:38.54", date: "2 hours ago", status: "clean" }, - { id: 2, category: "Any%", time: "2:45.12", date: "3 hours ago", status: "clean" }, - { id: 3, category: "Easy Only", time: "1:23.45", date: "5 hours ago", status: "clean" }, - { id: 4, category: "Any%", time: "--:--", date: "5 hours ago", status: "reset" }, - { id: 5, category: "Any%", time: "2:51.33", date: "Yesterday", status: "clean" }, - { id: 6, category: "No DP", time: "3:45.67", date: "Yesterday", status: "clean" }, - ]; + const recentRuns = [ + { + id: 1, + category: "Any%", + time: "2:38.54", + date: "2 hours ago", + status: "clean", + }, + { + id: 2, + category: "Any%", + time: "2:45.12", + date: "3 hours ago", + status: "clean", + }, + { + id: 3, + category: "Easy Only", + time: "1:23.45", + date: "5 hours ago", + status: "clean", + }, + { + id: 4, + category: "Any%", + time: "--:--", + date: "5 hours ago", + status: "reset", + }, + { + id: 5, + category: "Any%", + time: "2:51.33", + date: "Yesterday", + status: "clean", + }, + { + id: 6, + category: "No DP", + time: "3:45.67", + date: "Yesterday", + status: "clean", + }, + ]; - const achievements = [ - { name: "First Blood", description: "Complete your first run", icon: Zap, unlocked: true }, - { name: "Sub-3 Club", description: "Finish a run under 3 minutes", icon: Timer, unlocked: true }, - { name: "World Record", description: "Hold a world record", icon: Trophy, unlocked: true }, - { name: "Century", description: "Complete 100 runs", icon: Target, unlocked: true }, - { name: "Perfectionist", description: "100 clean runs", icon: Award, unlocked: false }, - { name: "Marathon", description: "10 hours of running", icon: Calendar, unlocked: true }, - ]; + const achievements = [ + { + name: "First Blood", + description: "Complete your first run", + icon: Zap, + unlocked: true, + }, + { + name: "Sub-3 Club", + description: "Finish a run under 3 minutes", + icon: Timer, + unlocked: true, + }, + { + name: "World Record", + description: "Hold a world record", + icon: Trophy, + unlocked: true, + }, + { + name: "Century", + description: "Complete 100 runs", + icon: Target, + unlocked: true, + }, + { + name: "Perfectionist", + description: "100 clean runs", + icon: Award, + unlocked: false, + }, + { + name: "Marathon", + description: "10 hours of running", + icon: Calendar, + unlocked: true, + }, + ]; - return ( -
-
- {/* Profile Header */} -
- {/* Background gradient */} -
+ return ( +
+
+ {/* Profile Header */} +
+ {/* Background gradient */} +
-
- {/* Avatar */} -
-
- {user.username.charAt(0).toUpperCase()} -
-
- #1 -
-
- - {/* User Info */} -
-

{user.username}

-

Runner since {user.joinDate}

-
- - World Record Holder - - - {user.favoriteCategory} Main - -
-
- - {/* Quick Stats */} -
-

{user.pb}

-

Personal Best

-
-
-
- - {/* Stats Grid */} -
- - - - -
- -
- {/* Recent Runs */} -
-
-
-

Recent Runs

- -
- -
- {recentRuns.map((run, i) => ( -
-
-
-
-

{run.category}

-

{run.date}

-
-
-
-

- {run.time} -

-

{run.status}

-
-
- ))} -
-
-
- - {/* Achievements */} -
-
-
-

Achievements

- 5/6 -
- -
- {achievements.map((achievement, i) => ( -
-
- -
-
-

- {achievement.name} -

-

{achievement.description}

-
- {achievement.unlocked && ( -
- - - -
- )} -
- ))} -
-
- - {/* PB History */} -
-

PB History

-
-
- 2:34.21 - Today -
-
- 2:38.54 - 3 days ago -
-
- 2:45.12 - 1 week ago -
-
- 2:52.33 - 2 weeks ago -
-
-
-
-
+
+ {/* Avatar */} +
+
+ {user.username.charAt(0).toUpperCase()} +
+
+ #1 +
+ + {/* User Info */} +
+

+ {user.username} +

+

+ Runner since {user.joinDate} +

+
+ + World Record Holder + + + {user.favoriteCategory} Main + +
+
+ + {/* Quick Stats */} +
+

+ {user.pb} +

+

Personal Best

+
+
- ); + + {/* Stats Grid */} +
+ + + + +
+ +
+ {/* Recent Runs */} +
+
+
+

+ Recent Runs +

+ +
+ +
+ {recentRuns.map((run, i) => ( +
+
+
+
+

+ {run.category} +

+

+ {run.date} +

+
+
+
+

+ {run.time} +

+

+ {run.status} +

+
+
+ ))} +
+
+
+ + {/* Achievements */} +
+
+
+

+ Achievements +

+ 5/6 +
+ +
+ {achievements.map((achievement, i) => ( +
+
+ +
+
+

+ {achievement.name} +

+

+ {achievement.description} +

+
+ {achievement.unlocked && ( +
+ + + +
+ )} +
+ ))} +
+
+ + {/* PB History */} +
+

+ PB History +

+
+
+ 2:34.21 + Today +
+
+ 2:38.54 + + 3 days ago + +
+
+ 2:45.12 + + 1 week ago + +
+
+ 2:52.33 + + 2 weeks ago + +
+
+
+
+
+
+
+ ); }; export default Profile; diff --git a/src/app/run/page.tsx b/src/app/run/page.tsx index d17a98a..7ed19bf 100644 --- a/src/app/run/page.tsx +++ b/src/app/run/page.tsx @@ -1,168 +1,175 @@ -import { Play, Pause, RotateCcw, Flag, ChevronRight } from "lucide-react"; -import SplitRow from "@/components/app/SplitRow"; -import { Button } from "@/components/ui/button"; +"use client"; -const Run = () => { - const splits = [ - { problemName: "Two Sum Dash", splitTime: "0:38", delta: "-0:04", isCompleted: true, pbTime: "0:42" }, - { problemName: "Binary Sprint", splitTime: "1:12", delta: "+0:02", isCompleted: true, pbTime: "0:32" }, - { problemName: "String Scramble", splitTime: "--:--", delta: undefined, isCompleted: false, isCurrent: true, pbTime: "0:55" }, - { problemName: "Linked List Rush", splitTime: "--:--", delta: undefined, isCompleted: false, pbTime: "1:24" }, - { problemName: "Stack Overflow", splitTime: "--:--", delta: undefined, isCompleted: false, pbTime: "1:42" }, - ]; +import { FileTree } from "@sinm/react-file-tree"; +import CodeMirror, { ReactCodeMirrorRef } from "@uiw/react-codemirror"; +import { vscodeDark } from "@uiw/codemirror-theme-vscode"; +import { useEffect, useRef } from "react"; - const runInfo = { - category: "Any% Easy", - problems: 5, - pbTime: "4:35", - worldRecord: "3:58", - currentAttempt: 47, - }; - - return ( -
-
-
- {/* Main Timer Area */} -
- {/* Category Header */} -
-
-
- Category - - {runInfo.category} -
-

Current Run

-
-
-

Attempt #{runInfo.currentAttempt}

-

WR: {runInfo.worldRecord}

-
-
- - {/* Timer Display */} -
- {/* Background glow */} -
- -
- {/* Timer */} -
-

1:52.34

-

- PB: {runInfo.pbTime} | Diff: +0:12 -

-
- - {/* Progress bar */} -
-
-
- - {/* Controls */} -
- - - -
-
-
- - {/* Current Problem */} -
-
-
-

Problem 3 of 5

-

String Scramble

-
- Easy -
- -
- strings - manipulation -
- -
- PB Split: 0:55 - | - Best: 0:38 -
- - -
-
- - {/* Splits Sidebar */} -
-
-
-

Splits

- 2/5 Complete -
- -
- {splits.map((split) => ( - - ))} -
- - {/* Sum of Best */} -
-
- Sum of Best - 3:51 -
-
- Personal Best - {runInfo.pbTime} -
-
-
- - {/* Quick Stats */} -
-

Session Stats

-
-
- Runs Today - 12 -
-
- Clean Runs - 3 -
-
- Resets - 9 -
-
- Time Played - 1:42:31 -
-
-
-
-
-
-
- ); +type RunInfo = { + category: "any%" | "100%"; + problems: number; + currentAttempt: number; + pbTime: string; + worldRecord: string; }; -export default Run; +type ChatMessage = { + user: string; + message: string; + time: string; +}; + +export default function Run() { + const splits = [ + { + problemName: "Two Sum Dash", + splitTime: "0:38", + delta: "-0:04", + isCompleted: true, + pbTime: "0:42", + }, + { + problemName: "Binary Sprint", + splitTime: "1:12", + delta: "+0:02", + isCompleted: true, + pbTime: "0:32", + }, + { + problemName: "String Scramble", + splitTime: "--:--", + delta: undefined, + isCompleted: false, + isCurrent: true, + pbTime: "0:55", + }, + { + problemName: "Linked List Rush", + splitTime: "--:--", + delta: undefined, + isCompleted: false, + pbTime: "1:24", + }, + { + problemName: "Stack Overflow", + splitTime: "--:--", + delta: undefined, + isCompleted: false, + pbTime: "1:42", + }, + ]; + + const runInfo: RunInfo = { + category: "any%", + problems: 5, + pbTime: "4:35", + worldRecord: "3:58", + currentAttempt: 47, + }; + + const chatMessages = [ + { user: "Alice", message: "Nice split!", time: "1:53" }, + { user: "Bob", message: "Watch out for the string problem!", time: "1:54" }, + { user: "Charlie", message: "PB incoming? 👀", time: "1:55" }, + ]; + + return ( +
+ {/* Editor */} +
+
+ +
+
+
myfile.js
+
+ { + view.focus(); + + view.dispatch({ + selection: { anchor: 1 }, + scrollIntoView: true, + }); + }} + /> +
+
+
+ + {/* Timer & Chat */} +
+ + +
+
+ ); +} + +function Timer({ runInfo }: { runInfo: RunInfo }) { + return ( +
+
+
+

1:52.34

+

+ PB: {runInfo.pbTime} | Diff:{" "} + +0:12 +

+
+
+ ); +} + +function Chat({ chatMessages }: { chatMessages: ChatMessage[] }) { + return ( +
+ {/* Header */} +
+

Chat

+ + {chatMessages.length} messages + +
+ + {/* Messages area */} +
+ {chatMessages.map((msg, idx) => ( +
+ {msg.user}:{" "} + {msg.message} + + {msg.time} + +
+ ))} +
+ + {/* Input */} +
+ +
+
+ ); +} diff --git a/src/components/app/Footer.tsx b/src/components/app/Footer.tsx index 5729fdb..6b944c8 100644 --- a/src/components/app/Footer.tsx +++ b/src/components/app/Footer.tsx @@ -1,6 +1,13 @@ +"use client"; + import { Timer, Github, Twitter, MessageCircle } from "lucide-react"; +import { usePathname } from "next/navigation"; const Footer = () => { + const pathname = usePathname(); + + if (pathname !== "/") return null; + return (