diff --git a/README.md b/README.md index dbab72d..ec869d0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # stereo.cat frontend -written in typescript with qwik +written in typescript with qwik & bun -## running in dev env -``` +## development +https://bun.sh/docs/installation + +```bash git clone https://git.iwakura.rip/stereo.cat/frontend.git git submodule update --init --recursive bun install @@ -11,4 +13,5 @@ bun dev ``` ## disclaimer + All graphic assets belonging to stereo.cat may not be used in unofficial instances, forks or versions of our software. Please replace them if you are hosting our software yourself, they can be found in the ``public`` folder in this repository. More information (like the full license) can be found [here](https://git.iwakura.rip/stereo.cat/public) diff --git a/bun.lock b/bun.lock index fc926af..9ac6a3b 100644 --- a/bun.lock +++ b/bun.lock @@ -33,9 +33,9 @@ "packages": { "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], - "@builder.io/qwik": ["@builder.io/qwik@1.14.1", "", { "dependencies": { "csstype": "^3.1", "rollup": ">= 4.39.0" }, "peerDependencies": { "vite": "^5" }, "bin": { "qwik": "qwik-cli.cjs" } }, "sha512-bq1fkt8RhjkENQOs/bxh8i1IuQ0DIfIZ1RgyoZYmADxcyKjhVRcCx+6kaRUkjovb5sR06pqH96YaHjI7iI/CMw=="], + "@builder.io/qwik": ["@builder.io/qwik@1.15.0", "", { "dependencies": { "csstype": "^3.1", "rollup": ">= 4.39.0" }, "peerDependencies": { "vite": ">=5 <8" }, "bin": { "qwik": "qwik-cli.cjs" } }, "sha512-0PUXGbH+9htHPq0Br+M/QsY7aGJhG7C1A4dZziDGONAB2INcOUkzoOF3Qv2S7JzFyUlgBtN1drmw6P9jLryRyQ=="], - "@builder.io/qwik-city": ["@builder.io/qwik-city@1.14.1", "", { "dependencies": { "@mdx-js/mdx": "^3", "@types/mdx": "^2", "source-map": "^0.7.4", "svgo": "^3.3", "undici": "*", "valibot": ">=0.36.0 <2", "vfile": "6.0.2", "vite": "^5", "vite-imagetools": "^7", "zod": "3.22.4" } }, "sha512-VsAvk7u2HyyTnL9GhpT+h10t2XAIlxtv6LFL3Xt9/1QZ6lMfGWMcMEAMuZB1Ib+D/oTfu7QRqZngRg3FsrIKyg=="], + "@builder.io/qwik-city": ["@builder.io/qwik-city@1.15.0", "", { "dependencies": { "@mdx-js/mdx": "^3", "@types/mdx": "^2", "source-map": "^0.7.4", "svgo": "^3.3", "undici": "*", "valibot": ">=0.36.0 <2", "vfile": "6.0.2", "vite": ">=5 <8", "vite-imagetools": "^7", "zod": "3.22.4" } }, "sha512-fy84pb6fat8YKDBh06tw+vgZ+Iwlhq/qEnFLUpMFLAG+Vll8diH6t0MeK9rtiPG9VsPZYZa32euTy4LnHj2Tiw=="], "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], @@ -97,7 +97,7 @@ "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], - "@eslint/js": ["@eslint/js@9.28.0", "", {}, "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg=="], + "@eslint/js": ["@eslint/js@9.31.0", "", {}, "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw=="], "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], @@ -215,35 +215,35 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.42.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA=="], - "@tailwindcss/node": ["@tailwindcss/node@4.1.8", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.8" } }, "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q=="], + "@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.8", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.8", "@tailwindcss/oxide-darwin-arm64": "4.1.8", "@tailwindcss/oxide-darwin-x64": "4.1.8", "@tailwindcss/oxide-freebsd-x64": "4.1.8", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", "@tailwindcss/oxide-linux-x64-musl": "4.1.8", "@tailwindcss/oxide-wasm32-wasi": "4.1.8", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8", "", { "os": "linux", "cpu": "arm" }, "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.8", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.8", "", { "os": "win32", "cpu": "x64" }, "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="], - "@tailwindcss/vite": ["@tailwindcss/vite@4.1.8", "", { "dependencies": { "@tailwindcss/node": "4.1.8", "@tailwindcss/oxide": "4.1.8", "tailwindcss": "4.1.8" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-CQ+I8yxNV5/6uGaJjiuymgw0kEQiNKRinYbZXPdx1fk5WgiyReG0VaUx/Xq6aVNSUNJFzxm6o8FNKS5aMaim5A=="], + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.11", "", { "dependencies": { "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "tailwindcss": "4.1.11" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw=="], "@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="], @@ -443,7 +443,7 @@ "eslint": ["eslint@9.25.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.13.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.25.1", "@eslint/plugin-kit": "^0.2.8", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ=="], - "eslint-plugin-qwik": ["eslint-plugin-qwik@1.14.1", "", { "dependencies": { "@typescript-eslint/utils": "^8.31.0", "jsx-ast-utils": "^3.3.5" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-N0C8V/6F4EfRdC6EmE7o0VnUod9T16u94C+AD325xA0U4IQoC191uvkGeeJtJ7RXv6UPWz0Evh1aHTfaEILhkg=="], + "eslint-plugin-qwik": ["eslint-plugin-qwik@1.15.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.31.0", "jsx-ast-utils": "^3.3.5" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-teDkSl0N/2vDCYK1LKfagnbE0A1RCV00tMekitVX6sfrfpM/nJ199Kyco5nrgp6qvhLJgFXsRBXGEEobPgn2jA=="], "eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="], @@ -635,7 +635,7 @@ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - "ky": ["ky@1.8.1", "", {}, "sha512-7Bp3TpsE+L+TARSnnDpk3xg8Idi8RwSLdj6CMbNWoOARIrGrbuLGusV0dYwbZOm4bB3jHNxSw8Wk/ByDqJEnDw=="], + "ky": ["ky@1.8.2", "", {}, "sha512-XybQJ3d4Ea1kI27DoelE5ZCT3bSJlibYTtQuMsyzKox3TMyayw1asgQdl54WroAm+fIA3ZCr8zXW2RpR7qWVpA=="], "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], @@ -811,7 +811,7 @@ "prettier": ["prettier@3.3.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew=="], - "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.12", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-OuTQKoqNwV7RnxTPwXWzOFXy6Jc4z8oeRZYGuMpRyG3WbuR3jjXdQFK8qFBMBx8UHWdHrddARz2fgUenild6aw=="], + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.14", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg=="], "prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="], @@ -909,7 +909,7 @@ "tailwind-scrollbar": ["tailwind-scrollbar@4.0.2", "", { "dependencies": { "prism-react-renderer": "^2.4.1" }, "peerDependencies": { "tailwindcss": "4.x" } }, "sha512-wAQiIxAPqk0MNTPptVe/xoyWi27y+NRGnTwvn4PQnbvB9kp8QUBiGl/wsfoVBHnQxTmhXJSNt9NHTmcz9EivFA=="], - "tailwindcss": ["tailwindcss@4.1.8", "", {}, "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="], + "tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="], "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], @@ -943,7 +943,7 @@ "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], - "undici": ["undici@7.10.0", "", {}, "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw=="], + "undici": ["undici@7.12.0", "", {}, "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug=="], "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], @@ -1009,7 +1009,7 @@ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="], - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.10", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], @@ -1051,6 +1051,8 @@ "typescript-eslint/@typescript-eslint/utils": ["@typescript-eslint/utils@8.26.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.26.1", "@typescript-eslint/types": "8.26.1", "@typescript-eslint/typescript-estree": "8.26.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], + "@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.26.1", "", {}, "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ=="], "@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.26.1", "", {}, "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ=="], diff --git a/package.json b/package.json index c95d6ca..58a2349 100644 --- a/package.json +++ b/package.json @@ -24,27 +24,27 @@ "qwik": "qwik" }, "devDependencies": { - "@builder.io/qwik": "^1.14.1", - "@builder.io/qwik-city": "^1.14.1", + "@builder.io/qwik": "^1.15.0", + "@builder.io/qwik-city": "^1.15.0", "@eslint/js": "latest", - "@tailwindcss/vite": "^4.0.0", + "@tailwindcss/vite": "^4.1.11", "@types/node": "20.14.11", "eslint": "9.25.1", - "eslint-plugin-qwik": "^1.14.1", + "eslint-plugin-qwik": "^1.15.0", "globals": "16.0.0", "prettier": "3.3.3", - "prettier-plugin-tailwindcss": "^0.6.11", - "tailwindcss": "^4.0.0", + "prettier-plugin-tailwindcss": "^0.6.14", + "tailwindcss": "^4.1.11", "typescript": "5.4.5", "typescript-eslint": "8.26.1", - "undici": "*", + "undici": "^7.12.0", "vite": "5.3.5", - "vite-tsconfig-paths": "^4.2.1" + "vite-tsconfig-paths": "^4.3.2" }, "dependencies": { "@types/aos": "^3.0.7", "aos": "^3.0.0-beta.6", - "ky": "^1.8.1", + "ky": "^1.8.2", "nanostores": "^1.0.1", "tailwind-scrollbar": "^4.0.2" } diff --git a/public b/public index a4f5725..ffdd93a 160000 --- a/public +++ b/public @@ -1 +1 @@ -Subproject commit a4f5725fbaf2db1053be198d81d08e1ea0c3e843 +Subproject commit ffdd93af120e3bec48736bfc8a2ae7824c941cfa diff --git a/src/components/dashboard/Actionbar.tsx b/src/components/dashboard/Actionbar.tsx new file mode 100644 index 0000000..26d32f9 --- /dev/null +++ b/src/components/dashboard/Actionbar.tsx @@ -0,0 +1,50 @@ +import { component$ } from "@builder.io/qwik"; +import { useNanostore$ } from "~/hooks/nanostores"; +import { isSettingsOpen } from "~/lib/stores"; +import { SolarLibraryLinear, SolarQuestionCircleLinear, SolarRoundedMagniferLinear, SolarSettingsLinear, SolarUploadMinimalisticLinear, StereoCircularProgress, StereoLogoLinear } from "../misc/Icons"; + +export default component$(() => { + const used = 3.8; + const total = 15; + + const settingsOpen = useNanostore$(isSettingsOpen); + + return ( +
+
+ + +
+ + + +
+ +
+
+ ) +}) \ No newline at end of file diff --git a/src/components/dashboard/Controlbar.tsx b/src/components/dashboard/Controlbar.tsx deleted file mode 100644 index 45310c1..0000000 --- a/src/components/dashboard/Controlbar.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { $, component$, noSerialize, NoSerialize, useSignal, useVisibleTask$ } from "@builder.io/qwik"; -import { useNanostore$ } from "~/hooks/nanostores"; -import { api } from "~/lib/api"; -import { areFilesLoaded, dashboardFiles } from "~/lib/stores"; -import { StereoFile } from "~/lib/types"; -import { SolarUploadLinear, SvgSpinnersBarsRotateFade } from "../misc/Icons"; -import StereoLogo from "../misc/StereoLogo"; - -export default component$(() => { - const loaded = useNanostore$(areFilesLoaded); - const files = useNanostore$(dashboardFiles); - const fileInputRef = useSignal(); - const uploadingFiles = useSignal | undefined>(); - const now = useSignal(new Date()); - - useVisibleTask$(() => { - const interval = setInterval(() => { - now.value = new Date(); - }, 500); - return () => clearInterval(interval); - }); - - const uploadFiles = $(async () => { - if (!uploadingFiles.value) { - console.error("No file(s) selected for upload."); - return; - } - - try { - const ufiles = uploadingFiles.value as File[]; - - for (const file of ufiles) { - const name = file.name.replace(/[^a-zA-Z0-9_.-]/g, "_"); - const f = new File([file], name, { type: file.type }); - - await api.upload(f); - } - - files.value = await api.list(); - } catch (error) { - console.error("Error uploading file:", error); - } - }) - return ( -
- { - uploadingFiles.value = noSerialize(Object.values((e.target as HTMLInputElement).files || {})); - await uploadFiles(); - }} - multiple - /> - -
- {/* TODO: replace this button with a modal with options like settings log out etc */} - - -

|

- - -
-

{now.value.toLocaleTimeString()}

-
- ) -}) \ No newline at end of file diff --git a/src/components/dashboard/File.tsx b/src/components/dashboard/File.tsx deleted file mode 100644 index 50c1e02..0000000 --- a/src/components/dashboard/File.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import { $, component$, Signal, useSignal, useTask$ } from "@builder.io/qwik"; -import { useNanostore$ } from "~/hooks/nanostores"; -import { api } from "~/lib/api"; -import { dashboardFiles } from "~/lib/stores"; -import { StereoFile } from "~/lib/types"; -import { SolarClipboardAddBold, SolarDownloadMinimalisticBold, SolarTrashBin2Bold } from "../misc/Icons"; - -type FileProps = { - file: StereoFile; -} - -const formatSize = (bytes: number) => { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; - return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`; -} - -export default component$(({ file }: FileProps) => { - const files = useNanostore$(dashboardFiles); - - const deleteFile = $(async (id: string) => { - if (!confirm("Are you sure you want to delete this file?")) return; - await api.delete(id); - files.value = await api.list(); - }); - - const addFileToClipboard = $(async () => { - const response = await api.file(file.Name); - const data = await response.blob(); - let mime = data.type || "application/octet-stream"; - let clip; - - if (navigator.clipboard && window.ClipboardItem) { - if (mime === "image/jpeg" || mime === "image/jpg") { - const img = document.createElement("img"); - img.src = URL.createObjectURL(data); - await new Promise((res) => (img.onload = res)); - const canvas = document.createElement("canvas"); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext("2d"); - ctx?.drawImage(img, 0, 0); - const png = await new Promise((resolve) => - canvas.toBlob((b) => resolve(b!), "image/png") - ); - mime = "image/png"; - clip = new ClipboardItem({ [mime]: png }); - } else { - clip = new ClipboardItem({ [mime]: data }); - } - - try { - await navigator.clipboard.write([clip]); - alert("File added to clipboard successfully!"); - } catch (error) { - console.error("Failed to add file to clipboard:", error); - alert("Failed to add file to clipboard. Please try again."); - } - } else { - alert("Clipboard API not supported in this browser."); - } - }); - - return ( -
-
- -
- -
-
- - -
- -
-
-

- { file.Name || "Untitled" } -

-
- { formatSize(file.Size) } - - Uploaded on { new Date(file.CreatedAt).toLocaleDateString() } -
-
-
-
- ) -}) - -const FilePreview = component$(({ file }: FileProps) => { - type FileType = - | "image" - | "video" - | "audio" - | "other"; - - const type: Signal = useSignal("other"); - const extension = file.Name.split('.').pop()?.toLowerCase() || ""; - - useTask$(async () => { - if ( - ["png", "jpg", "jpeg", "gif"] - .includes(extension)) type.value = "image"; - - else if ( - ["mp4", "webm", "ogg", "avi", "mov", "mkv"] - .includes(extension)) type.value = "video"; - else if ( - ["mp3", "wav", "ogg", "flac", "aac"] - .includes(extension)) type.value = "audio"; - - else type.value = "other"; - }); - - switch (type.value) { - case "image": - return ( -
- {file.Name} -
- ); - case "video": - return ( -
- -
- ); - case "audio": - return ( -
- -
- ); - case "other": - default: - return ( -
-

- Preview not available -

-
- ); - } -}); \ No newline at end of file diff --git a/src/components/dashboard/Settings.tsx b/src/components/dashboard/Settings.tsx new file mode 100644 index 0000000..818322a --- /dev/null +++ b/src/components/dashboard/Settings.tsx @@ -0,0 +1,152 @@ +// import { component$ } from "@builder.io/qwik"; +// import { useNanostore$ } from "~/hooks/nanostores"; +// import { isSettingsOpen, userInfo } from "~/lib/stores"; +// import { StereoUser } from "~/lib/types"; + +import { $, component$, useComputed$, useSignal, useTask$ } from "@builder.io/qwik"; +import ky from "ky"; +import { useNanostore$ } from "~/hooks/nanostores"; +import { isSettingsOpen } from "~/lib/stores"; + +const StorageAndPlan = component$(() => { + return ( +
+

current plan: stereo pro+

+

storage used: 3.8 / 15 GB

+

upgrade your plan for more features

+
+ ); +}); + +const Integrations = component$(() => { + return ( +
+

manage your api keys and integrations here

+
+ ); +}); + +const PrivacyAndSecurity = component$(() => { + return ( +
+

manage your privacy settings and security options

+
+ ); +}); + +const DangerZone = component$(() => { + const handleLogout = $(() => { + ky.get("/api/auth/logout", { credentials: "include" }) + .then(() => { + window.location.href = "/"; + }); + }); + + return ( +
+

delete your account and data here

+ +
+ ); +}); + + +export default component$(() => { + const open = useNanostore$(isSettingsOpen); + const visible = useSignal(false); + const shouldRender = useSignal(open.value); + + useTask$(({ track }) => { + track(() => open.value); + if (open.value) { + shouldRender.value = true; + setTimeout(() => { visible.value = true; }, 10); + } else { + visible.value = false; + setTimeout(() => { shouldRender.value = false; }, 300); + } + }); + + const categories = useSignal([ + { + name: "storage & plan", + description: "manage your storage and plan details", + component: StorageAndPlan + }, + { + name: "api & integrations", + description: "manage your api keys and integrations", + component: Integrations + }, + { + name: "privacy & security", + description: "manage your privacy settings and security options", + component: PrivacyAndSecurity + }, + { + name: "danger zone", + description: "delete your account and data", + component: DangerZone + } + ]) + + const selectedCategory = useSignal(0); + const SelectedComponent = useComputed$(() => { + return categories.value[selectedCategory.value].component; + }); + + if (!shouldRender.value) return <>; + return ( +
(open.value = false)} + style={{ + opacity: visible.value ? 1 : 0, + }} + class="z-[50] fixed inset-0 flex items-center justify-center bg-black/50 text-white text-6xl backdrop-blur-3xl transition-opacity duration-300" + > +
e.stopPropagation()} + class="flex gap-8 bg-black/30 bg-gradient-to-t from-stereo/20 to-transparent p-8 rounded-3xl shadow-lg w-4/7 h-4/7" + > +
+

settings

+
+ {categories.value.map((category, i) => ( +
(selectedCategory.value = i)} + > +

{category.name}

+

{category.description}

+
+ ))} +
+
+
+

{categories.value[selectedCategory.value].name}

+ +
+
+
+ ); +}); \ No newline at end of file diff --git a/src/components/dashboard/TitleBar.tsx b/src/components/dashboard/TitleBar.tsx new file mode 100644 index 0000000..5782756 --- /dev/null +++ b/src/components/dashboard/TitleBar.tsx @@ -0,0 +1,60 @@ +import { component$, useTask$ } from "@builder.io/qwik"; +import { useNanostore$ } from "~/hooks/nanostores"; +import { userInfo } from "~/lib/stores"; +import { StereoUser } from "~/lib/types"; + +export default component$(() => { + const greetings = [ + "what's on the agenda today, ?", + "what's on your mind, ?", + "what's the plan, ?", + "ready to rock, ?", + "what's brewing, ?", + "what's the latest, ?", + "how's your day going, ?", + "need some inspiration, ?", + "let's make some noise, !", + "welcome back, !", + "good to see you, !", + "what are we making today, ?", + "time to make some magic, !", + "let's get creative, !", + "what's the vibe today, ?", + "let's create something awesome, !", + "what's the next big thing, ?", + "let's turn ideas into reality, !", + "let's see your next masterpiece, !", + "let's make some art, !", + "what's the next hit, ?", + "let's make some music, !", + "let's make some waves, !", + "what brilliance awaits, ?", + "ready to brainstorm, ?", + "let's bring ideas to life, !", + "don't get any ideas, ...", + "let's try this again, !", + "let's get this party started, !", + "let's make something unforgettable, !", + ] + + const greeting = greetings[Math.floor(Math.random() * greetings.length)]; + const user = useNanostore$(userInfo); + + const splits = greeting.split(""); + + useTask$(({ track }) => { + track(() => user.value); + }) + + return ( +
+

+ {splits[0]} + + @{user.value?.username || "..."} + + {splits[1]} +

+
+ ) +}) \ No newline at end of file diff --git a/src/components/landing/CallToAction.tsx b/src/components/landing/CallToAction.tsx index 1b4fc31..cbdb4f1 100644 --- a/src/components/landing/CallToAction.tsx +++ b/src/components/landing/CallToAction.tsx @@ -1,5 +1,4 @@ import { component$ } from "@builder.io/qwik"; -import { OAUTH_LINK } from "~/lib/constants"; export default component$(() => (
( join over 100k other people hosting their files with stereo!

get started diff --git a/src/components/landing/Footer.tsx b/src/components/landing/Footer.tsx index 16fef9d..363619f 100644 --- a/src/components/landing/Footer.tsx +++ b/src/components/landing/Footer.tsx @@ -1,5 +1,5 @@ import { component$ } from "@builder.io/qwik"; -import StereoLogo from "../misc/StereoLogo"; +import { StereoLogoBold } from "../misc/Icons"; export default component$(() => { return ( @@ -7,7 +7,7 @@ export default component$(() => {
- + stereo.cat diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx index 298c35b..c7b30b5 100644 --- a/src/components/landing/Hero.tsx +++ b/src/components/landing/Hero.tsx @@ -1,6 +1,5 @@ import { component$, useSignal } from "@builder.io/qwik"; import { useRelativeMouse } from "~/hooks/mouse"; -import { OAUTH_LINK } from "~/lib/constants"; import GradientBorder from "../misc/GradientBorder"; export default component$(() => { @@ -22,7 +21,7 @@ export default component$(() => {

get started diff --git a/src/components/landing/Navbar.tsx b/src/components/landing/Navbar.tsx index 85fb716..4e23a25 100644 --- a/src/components/landing/Navbar.tsx +++ b/src/components/landing/Navbar.tsx @@ -1,5 +1,5 @@ import { component$ } from "@builder.io/qwik"; -import StereoLogo from "../misc/StereoLogo"; +import { StereoLogoBold } from "../misc/Icons"; export default component$(() => { const items = [ @@ -17,7 +17,7 @@ export default component$(() => { data-aos-duration="1000" class="fixed flex items-center justify-start top-6 left-1/2 transform -translate-x-1/2 bg-neutral-950 p-8 h-10 rounded-full lg:w-2/3 md:w-4/5 w-4/5 z-[9999999] shadow-lg">
- +

diff --git a/src/components/misc/Icons.tsx b/src/components/misc/Icons.tsx index 5500ae1..6718fbe 100644 --- a/src/components/misc/Icons.tsx +++ b/src/components/misc/Icons.tsx @@ -1,5 +1,7 @@ import { QwikIntrinsicElements } from "@builder.io/qwik"; +// Solar - https://icones.js.org/collection/solar + export function SolarUploadLinear(props: QwikIntrinsicElements['svg'], key: string) { return ( @@ -26,7 +28,6 @@ export function SolarLinkRoundBold(props: QwikIntrinsicElements['svg'], key: str ) } - export function SolarDownloadMinimalisticBold(props: QwikIntrinsicElements['svg'], key: string) { return ( @@ -36,4 +37,93 @@ export function SvgSpinnersBarsRotateFade(props: QwikIntrinsicElements['svg'], k return ( ) +} + +export function SolarLibraryLinear(props: QwikIntrinsicElements['svg'], key: string) { + return ( + + ) +} + +export function SolarUploadMinimalisticLinear(props: QwikIntrinsicElements['svg'], key: string) { + return ( + + ) +} + + +export function SolarRoundedMagniferLinear(props: QwikIntrinsicElements['svg'], key: string) { + return ( + + ) +} + + +export function SolarSettingsLinear(props: QwikIntrinsicElements['svg'], key: string) { + return ( + + ) +} + + +export function SolarQuestionCircleLinear(props: QwikIntrinsicElements['svg'], key: string) { + return ( + + ) +} + +// Stereo + +export function StereoLogoBold(props: QwikIntrinsicElements['svg'], key: string) { + return ( + + ) +} + +export function StereoLogoLinear(props: QwikIntrinsicElements['svg'], key: string) { + return ( + + ) +} + +export function StereoCircularProgress( + { value, ...svgProps }: QwikIntrinsicElements['svg'] & { value: number }, + key: string +) { + const radius = 10; + const circumference = 2 * Math.PI * radius; + const dashOffset = circumference * (1 - value); + + return ( + + + + + ); } \ No newline at end of file diff --git a/src/components/misc/StereoLogo.tsx b/src/components/misc/StereoLogo.tsx deleted file mode 100644 index 892ea95..0000000 --- a/src/components/misc/StereoLogo.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { component$, QwikIntrinsicElements } from "@builder.io/qwik"; - -export default component$((props: QwikIntrinsicElements['svg']) => { - return ( - - - - ) -}) \ No newline at end of file diff --git a/src/lib/api.ts b/src/lib/api.ts index c75a9b8..bc5c2e6 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -15,4 +15,5 @@ export const api = { return await client.post('upload', { body: formData }); }, delete: async (uid: string) => await client.delete(uid).json(), + me: async () => (await client.get('auth/me').json() as any).user, } \ No newline at end of file diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 60a2ab9..c55705b 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1 +1 @@ -export const OAUTH_LINK = "https://discord.com/oauth2/authorize?client_id=1368939221678817382&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8081%2Fapi%2Fauth%2Fcallback&scope=identify+email" \ No newline at end of file +//export const OAUTH_LINK = "https://discord.com/oauth2/authorize?client_id=1368939221678817382&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8081%2Fapi%2Fauth%2Fcallback&scope=identify+email" \ No newline at end of file diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 444b668..d467cee 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -1,5 +1,13 @@ import { atom } from "nanostores"; -import { StereoFile } from "./types"; +import { StereoFile, StereoUser } from "./types"; export const areFilesLoaded = atom(false); -export const dashboardFiles = atom([]); \ No newline at end of file +export const dashboardFiles = atom([]); +export const userInfo = atom({ + id: "1", + username: "user", + blacklisted: false, + email: "user@example.com", + created_at: Date.now().toString(), +}); +export const isSettingsOpen = atom(false); \ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts index 861bacc..72bec61 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -5,4 +5,12 @@ export type StereoFile = { Size: number; CreatedAt: string; Mime: string; +} + +export type StereoUser = { + id: string; + username: string; + blacklisted: boolean; + email: string; + created_at: string; } \ No newline at end of file diff --git a/src/routes/dashboard/index.tsx b/src/routes/dashboard/index.tsx index 98a3864..ac4466c 100644 --- a/src/routes/dashboard/index.tsx +++ b/src/routes/dashboard/index.tsx @@ -1,9 +1,9 @@ -import { component$, useVisibleTask$ } from "@builder.io/qwik"; +import { component$, Signal, useSignal, useTask$, useVisibleTask$ } from "@builder.io/qwik"; import { routeLoader$, type DocumentHead } from "@builder.io/qwik-city"; -import Controlbar from "~/components/dashboard/Controlbar"; +import Actionbar from "~/components/dashboard/Actionbar"; +import Settings from "~/components/dashboard/Settings"; +import Titlebar from "~/components/dashboard/Titlebar"; // import Dropzone from "~/components/Dropzone"; -import File from "~/components/dashboard/File"; -import { SolarUploadLinear, SvgSpinnersBarsRotateFade } from "~/components/misc/Icons"; import { useNanostore$ } from "~/hooks/nanostores"; import { api } from "~/lib/api"; import { areFilesLoaded, dashboardFiles } from "~/lib/stores"; @@ -12,7 +12,7 @@ import { StereoFile } from "~/lib/types"; export const useAuthCheck = routeLoader$(({ cookie, redirect: r }) => { const jwt = cookie.get("jwt"); if (jwt) return {}; - throw r(302, "/"); + throw r(302, "/api/auth/login"); }); export default component$(() => { @@ -22,50 +22,109 @@ export default component$(() => { useVisibleTask$(async () => { loaded.value = false; files.value = await api.list(); - console.log("Files loaded:", files.value); loaded.value = true; }); return ( <> - {/* */} - - {!loaded.value ? ( -

-

-

loading your files...

- please wait... -
- ) : ( - files.value.length === 0 ? ( -
-

{ - [ - "┻━┻︵ \\(°□°)/ ︵ ┻━┻", - "┻━┻︵ヽ(`Д´)ノ︵ ┻━┻", - "ʕノ•ᴥ•ʔノ ︵ ┻━┻", - "(╯°Д°)╯︵ /(.□ . \\)", - "┬─┬ ︵ /(.□. \\)", - "(/ .□.)\\ ︵╰(゜Д゜)╯︵ /(.□. \\)" - ].sort(() => Math.random() - 0.5)[0] - }

-

you haven't uploaded any files yet!

- click the button to get started -
- ) - : ( -
- {files.value.map((file) => ( - - ))} -
- ) - )} - + +
+ + + +
); }); +const formatSize = (bytes: number) => { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`; +} + + +const Files = component$<{ + files: Signal; + loaded: Signal; +}>(({ files }) => { + const File = component$(({ file }: { file: StereoFile }) => { + const Preview = component$(() => { + type FileType = "image" | "video" | "audio" | "other"; + const fileType: Signal = useSignal("other"); + const type = file.Mime.split("/")[1]; + useTask$(() => { + if (["jpeg", "jpg", "png", "gif", "webp"].includes(type)) fileType.value = "image"; + else if (["mp4", "webm", "ogg", "avi", "mov"].includes(type)) fileType.value = "video"; + else if (["mp3", "wav", "flac", "aac"].includes(type)) fileType.value = "audio"; + else fileType.value = "other"; + }); + return ( +
+ {fileType.value === "image" && ( + {file.Name} + )} + {fileType.value === "video" && ( +
+ ); + }); + return ( +
+ +
+

{file.Name}

+

+ {formatSize(file.Size)} + + Uploaded on {new Date(file.CreatedAt).toLocaleDateString()} +

+
+
+ ); + }); + return ( +
+
+ {files.value.map((file) => ( + + ))} +
+
+ ); +}); + export const head: DocumentHead = { title: "Welcome to Qwik", meta: [ diff --git a/src/routes/index.tsx b/src/routes/index.tsx index b1dc77d..4fca559 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -13,7 +13,7 @@ export const useAuthCheck = routeLoader$(({ cookie, redirect: r, query }) => { const set = Boolean(query.get("jwt_set")); if (jwt && set) { - throw r(302, "/dashboard"); + throw r(302, "/api/auth/login"); } return {}; }); diff --git a/src/routes/layout.tsx b/src/routes/layout.tsx index fe816c8..1abb748 100644 --- a/src/routes/layout.tsx +++ b/src/routes/layout.tsx @@ -1,9 +1,24 @@ -import { $, component$, Slot, useOnDocument } from '@builder.io/qwik'; +import { $, component$, Slot, useOnDocument, useVisibleTask$ } from '@builder.io/qwik'; import AOS from 'aos'; import 'aos/dist/aos.css'; +import { useNanostore$ } from '~/hooks/nanostores'; +import { api } from '~/lib/api'; +import { userInfo } from '~/lib/stores'; +import { StereoUser } from '~/lib/types'; export default component$(() => { - useOnDocument("DOMContentLoaded", $(() => { + const info = useNanostore$(userInfo); + + useVisibleTask$(async () => { + try { + info.value = await api.me(); + } catch (err) { + console.error("failed to fetch user info:", err); + } + }) + + useOnDocument("DOMContentLoaded", $(async () => { + AOS.init({ once: true, duration: 1000,