From 1c5679df4c2ceea14f1337b3246c94e60d877cfa Mon Sep 17 00:00:00 2001 From: Deepraj Date: Wed, 16 Oct 2024 23:53:29 +0530 Subject: [PATCH 1/3] major updates --- client/package-lock.json | 1307 +++++++++++++++-- client/package.json | 4 +- client/src/App.tsx | 44 +- .../components/Auth/user-auth-form-login.tsx | 51 +- .../components/Auth/user-auth-form-signup.tsx | 68 +- client/src/components/Chat/chat.tsx | 102 +- .../MobileSidebar/MobileSidebar.tsx | 85 +- client/src/components/Navbar/navbar.tsx | 2 +- client/src/components/Sidebar/HomeSidebar.tsx | 129 -- client/src/components/Sidebar/Sidebar.tsx | 11 +- .../ui/placeholders-and-vanish-input.tsx | 3 - client/src/index.css | 70 +- client/src/pages/Home.tsx | 41 +- client/src/pages/Landing.tsx | 2 +- client/src/pages/Login.tsx | 20 +- client/src/pages/Profile.tsx | 395 ++--- server/api/handlers/auth/userauth.py | 10 +- server/api/handlers/query/querymodel.py | 109 +- server/api/urls.py | 6 +- server/config.py | 1 + server/extensions.py | 6 + server/requirements.txt | 3 +- 22 files changed, 1597 insertions(+), 872 deletions(-) delete mode 100644 client/src/components/Sidebar/HomeSidebar.tsx diff --git a/client/package-lock.json b/client/package-lock.json index 7ea38bc..8f3151c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -11,7 +11,7 @@ "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-dialog": "^1.1.1", - "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-navigation-menu": "^1.2.0", @@ -36,6 +36,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.52.1", "react-icons": "^5.3.0", + "react-markdown": "^9.0.1", "react-redux": "^9.1.2", "react-router-dom": "^6.25.1", "react-wrap-balancer": "^1.1.1", @@ -43,6 +44,7 @@ "sonner": "^1.5.0", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", + "typewriter-effect": "^2.21.0", "vaul": "^0.9.1", "zod": "^3.23.8" }, @@ -2552,11 +2554,47 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { "version": "20.16.10", @@ -2570,14 +2608,12 @@ "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { "version": "18.3.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2592,6 +2628,11 @@ "@types/react": "*" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -2785,8 +2826,7 @@ "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "node_modules/@vitejs/plugin-react": { "version": "4.3.2", @@ -2964,6 +3004,15 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3068,6 +3117,15 @@ } ] }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3082,6 +3140,42 @@ "node": ">=4" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3537,6 +3631,15 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -3584,14 +3687,12 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -3604,6 +3705,18 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3618,11 +3731,31 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -4002,6 +4135,15 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4011,6 +4153,11 @@ "node": ">=0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4359,6 +4506,53 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4419,6 +4613,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==" + }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -4427,6 +4626,28 @@ "loose-envify": "^1.0.0" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4452,6 +4673,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4479,6 +4709,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4496,6 +4735,17 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4638,6 +4888,15 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4666,118 +4925,682 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" + "node_modules/mdast-util-from-markdown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", + "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">=8.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "dependencies": { - "mime-db": "1.52.0" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">= 0.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mini-svg-data-uri": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", - "bin": { - "mini-svg-data-uri": "cli.js" + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/next-themes": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", - "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", + "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/next-themes": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", + "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", "peerDependencies": { "react": "^16.8 || ^17 || ^18", "react-dom": "^16.8 || ^17 || ^18" @@ -4895,6 +5718,30 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4955,6 +5802,11 @@ "node": ">=8" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "node_modules/phenomenon": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/phenomenon/-/phenomenon-1.6.0.tgz", @@ -5148,6 +6000,25 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -5181,6 +6052,14 @@ } ] }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5227,6 +6106,36 @@ "react": "*" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-redux": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", @@ -5400,6 +6309,37 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -5588,6 +6528,15 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -5648,6 +6597,19 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5683,6 +6645,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -5844,6 +6814,24 @@ "node": ">=8.0" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -5903,12 +6891,106 @@ "node": ">=14.17" } }, + "node_modules/typewriter-effect": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/typewriter-effect/-/typewriter-effect-2.21.0.tgz", + "integrity": "sha512-Y3VL1fuJpUBj0gS4OTXBLzy1gnYTYaBuVuuO99tGNyTkkub5CXi+b/hsV7Og9fp6HlhogOwWJwgq7iXI5sQlEg==", + "dependencies": { + "prop-types": "^15.8.1", + "raf": "^3.4.1" + }, + "peerDependencies": { + "react": "^17.x || ^18.x", + "react-dom": "^17.x || ^18.x" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -6014,6 +7096,32 @@ "react-dom": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "5.4.8", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", @@ -6255,6 +7363,15 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/client/package.json b/client/package.json index 9c0290d..0b6ef8b 100644 --- a/client/package.json +++ b/client/package.json @@ -13,7 +13,7 @@ "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-dialog": "^1.1.1", - "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-navigation-menu": "^1.2.0", @@ -38,6 +38,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.52.1", "react-icons": "^5.3.0", + "react-markdown": "^9.0.1", "react-redux": "^9.1.2", "react-router-dom": "^6.25.1", "react-wrap-balancer": "^1.1.1", @@ -45,6 +46,7 @@ "sonner": "^1.5.0", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", + "typewriter-effect": "^2.21.0", "vaul": "^0.9.1", "zod": "^3.23.8" }, diff --git a/client/src/App.tsx b/client/src/App.tsx index 5683f61..2f6d5c8 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -10,52 +10,18 @@ import Home from './pages/Home'; import Profile from './pages/Profile'; import { Toaster } from "@/components/ui/sonner"; -const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; - const App: React.FC = () => { - const [authenticated, setAuthenticated] = useState(false); - const [username, setUsername] = useState(null); - - useEffect(() => { - const checkAuth = async () => { - try { - const response = await axios.get(`${backendUrl}/check_auth`, { withCredentials: true }); - if (response.data.authenticated) { - setAuthenticated(true); - setUsername(response.data.username); - } else { - setAuthenticated(false); - } - } catch (error) { - console.error('Failed to check authentication status:', error); - } - }; - - checkAuth(); - }, []); - - const handleLogout = async () => { - try { - const response = await axios.get(`${backendUrl}/logout`, { withCredentials: true }); - if (response.data.message === 'Logout successful') { - setAuthenticated(false); - setUsername(null); - } - } catch (error) { - console.error('Failed to logout:', error); - } - }; return ( - : } /> + } /> } /> - { setAuthenticated(true); setUsername(username); }} />} /> - : } /> - } /> - } /> + } /> + } /> + } /> + 404} /> diff --git a/client/src/components/Auth/user-auth-form-login.tsx b/client/src/components/Auth/user-auth-form-login.tsx index 2ab98e3..7c25c02 100644 --- a/client/src/components/Auth/user-auth-form-login.tsx +++ b/client/src/components/Auth/user-auth-form-login.tsx @@ -1,7 +1,4 @@ -"use client"; - import React, { useState } from 'react'; -import axios from 'axios'; import { useNavigate } from 'react-router-dom'; import { toast } from "sonner"; @@ -13,11 +10,7 @@ import { Label } from "@/components/ui/label"; const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; -interface UserAuthFormProps extends React.HTMLAttributes { - onLoginSuccess?: (username: string) => void; -} - -export function UserAuthForm({ className, onLoginSuccess, ...props }: UserAuthFormProps) { +export function UserAuthForm() { const [isLoading, setIsLoading] = useState(false); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); @@ -28,42 +21,44 @@ export function UserAuthForm({ className, onLoginSuccess, ...props }: UserAuthFo setIsLoading(true); try { - const response = await axios.post(`${backendUrl}/login`, { username, password }, { withCredentials: true }); - if (response.status === 200) { + const response = await fetch(`${backendUrl}/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + credentials: 'include', + body: JSON.stringify({ username, password }), + }); + if (response.ok) { localStorage.setItem('devhub_username', username); - - if (onLoginSuccess) { - onLoginSuccess(username); - } navigate('/home'); - } - } catch (err: any) { - if (err.response && err.response.data && err.response.data.message) { - toast.error(err.response.data.message, { - description: "Please check your details and try again.", - action: { - label: "Try again", - onClick: () => console.log("Try again clicked"), - }, - }); } else { - toast.error("Login failed", { - description: "There was a problem with your request.", + const errorData = await response.json(); + toast.error(errorData.message || "Login failed", { + description: "Please check your details and try again.", action: { label: "Try again", onClick: () => console.log("Try again clicked"), }, }); } - console.error('Login error:', err); + } catch (error) { + toast.error("Login failed", { + description: "There was a problem with your request.", + action: { + label: "Try again", + onClick: () => console.log("Try again clicked"), + }, + }); + console.error('Login error:', error); } finally { setIsLoading(false); } } return ( -
+
diff --git a/client/src/components/Auth/user-auth-form-signup.tsx b/client/src/components/Auth/user-auth-form-signup.tsx index 57dec1e..3bfe919 100644 --- a/client/src/components/Auth/user-auth-form-signup.tsx +++ b/client/src/components/Auth/user-auth-form-signup.tsx @@ -1,7 +1,6 @@ "use client"; import React, { useState, useEffect } from "react"; -import axios from "axios"; import { useNavigate } from "react-router-dom"; import { toast } from "sonner"; @@ -11,7 +10,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -interface UserAuthFormProps extends React.HTMLAttributes {} +interface UserAuthFormProps extends React.HTMLAttributes { } interface PasswordRules { minLength: boolean; containsUpper: boolean; @@ -26,18 +25,15 @@ const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000"; export function UserAuthForm({ className, ...props }: UserAuthFormProps) { const [isLoading, setIsLoading] = useState(false); const [username, setUsername] = useState(""); - const [isUsernameAvailable, setIsUsernameAvailable] = useState< - boolean | null - >(null); + const [isUsernameAvailable, setIsUsernameAvailable] = useState(null); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [usernameError, setUsernameError] = useState(""); - const [passwordRules, setPasswordRules] = useState( - null - ); + const [passwordRules, setPasswordRules] = useState(null); const [isPasswordValid, setIsPasswordValid] = useState(false); - const navigate = useNavigate(); // Hook for navigation + const navigate = useNavigate(); + // Username availability check with debounce useEffect(() => { const checkUsernameAvailability = async () => { if (username) { @@ -56,10 +52,10 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) { return; } setUsernameError(""); - const response = await axios.get(`${backendUrl}/check_username`, { - params: { username }, - }); - setIsUsernameAvailable(response.data.available); + + const response = await fetch(`${backendUrl}/check_username?username=${username}`); + const data = await response.json(); + setIsUsernameAvailable(data.available); } catch (error) { console.error("Error checking username availability:", error); } @@ -76,6 +72,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) { return () => clearTimeout(delayDebounceFn); }, [username]); + // Password validation rules const validatePassword = (password: string): PasswordRules => { return { minLength: password.length >= 6, @@ -107,6 +104,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) { noWhitespace: "Should not contain spaces.", }; + // Form submission handler async function onSubmit(event: React.SyntheticEvent) { event.preventDefault(); setIsLoading(true); @@ -116,7 +114,19 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) { } try { - await axios.post(`${backendUrl}/signup`, { username, email, password }); + const response = await fetch(`${backendUrl}/signup`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, email, password }), + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Signup failed"); + } + toast.success("Signup successful", { description: "You can now log in with your new account.", action: { @@ -124,26 +134,16 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) { onClick: () => navigate("/login"), }, }); - navigate("/login"); // Redirect to login page on successful signup - } catch (err: any) { - if (err.response && err.response.data && err.response.data.message) { - toast.error(err.response.data.message, { - description: "Please check your details and try again.", - action: { - label: "Try again", - onClick: () => console.log("Try again clicked"), - }, - }); - } else { - toast.error("Signup failed", { - description: "There was a problem with your request.", - action: { - label: "Try again", - onClick: () => console.log("Try again clicked"), - }, - }); - } - console.error("Error during signup:", err); + navigate("/login"); + } catch (error) { + toast.error(`Signup failed ${error}`, { + description: "Please check your details and try again.", + action: { + label: "Try again", + onClick: () => console.log("Try again clicked"), + }, + }); + console.error("Error during signup:", error); } finally { setIsLoading(false); } diff --git a/client/src/components/Chat/chat.tsx b/client/src/components/Chat/chat.tsx index af664a5..1044a41 100644 --- a/client/src/components/Chat/chat.tsx +++ b/client/src/components/Chat/chat.tsx @@ -1,21 +1,22 @@ -'use client'; - +import Sidebar from "../Sidebar/Sidebar"; +import MobileSidebar from "../MobileSidebar/MobileSidebar"; import { PlaceholdersAndVanishInput } from '../ui/placeholders-and-vanish-input'; import { useState, useEffect, useRef } from 'react'; import axios from 'axios'; +const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; + interface Message { query: string; response: string; isLoading?: boolean; } -export function PlaceholdersAndVanishInputDemo() { +export default function Chat() { const [messages, setMessages] = useState>([]); const messagesEndRef = useRef(null); - const backendUrl = - import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; + const placeholders = [ 'How to set up a React project with Vite?', @@ -29,27 +30,56 @@ export function PlaceholdersAndVanishInputDemo() { const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; - useEffect(() => { scrollToBottom(); }, [messages]); - const handleChange = () => { - // Additional logic can be added here - }; + const username = localStorage.getItem('devhub_username'); + + useEffect(() => { + const fetchChatHistory = async () => { + if (!username) { + console.error('Username not found in localStorage'); + return; + } + + try { + const result = await axios.get(`${backendUrl}/chat_history`, { + params: { username }, + withCredentials: true + }); + + const chatHistory = + result.data.chat_history.length > 0 + ? result.data.chat_history + : [{ query: 'Hi', response: 'Welcome to DevHub!' }]; + setMessages(chatHistory); + } catch (error) { + console.error('Error fetching chat history:', error); + } + }; + + fetchChatHistory(); + }, [username]); const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); const query = e.currentTarget.querySelector('input')?.value; if (!query) return; + if (!username) { + console.error('Username not found in localStorage'); + return; + } + setMessages((prevMessages) => [ ...prevMessages, { query, response: '', isLoading: true }, ]); try { - const result = await axios.post(`${backendUrl}/chat`, { query }); + const result = await axios.post(`${backendUrl}/chat`, { query, username }, { withCredentials: true }); + setMessages((prevMessages) => { const newMessages = [...prevMessages]; const lastMessage = newMessages[newMessages.length - 1]; @@ -62,50 +92,46 @@ export function PlaceholdersAndVanishInputDemo() { setMessages((prevMessages) => { const newMessages = [...prevMessages]; const lastMessage = newMessages[newMessages.length - 1]; - lastMessage.response = - 'An error occurred while processing your request.'; + lastMessage.response = 'An error occurred while processing your request.'; lastMessage.isLoading = false; return newMessages; }); } }; - return ( -
-

- Start Collaborating -

-
- {messages.map((message, index) => ( -
-
-
+
+ +
+
+ +
+
+
+ {messages.map((message, index) => ( +
+

You:

{message.query}

-
-
-
-
+ +

AI:

{message.isLoading ? (

Loading...

) : (

{message.response}

)} +
-
+ ))} +
- ))} -
-
-
- +
+ +
+
); diff --git a/client/src/components/MobileSidebar/MobileSidebar.tsx b/client/src/components/MobileSidebar/MobileSidebar.tsx index 5bf0474..3ff37c2 100644 --- a/client/src/components/MobileSidebar/MobileSidebar.tsx +++ b/client/src/components/MobileSidebar/MobileSidebar.tsx @@ -3,8 +3,6 @@ import { Home, LineChart, Menu, - Fullscreen, - Shrink } from "lucide-react" import { DropdownMenu, @@ -16,72 +14,16 @@ import { } from "@/components/ui/dropdown-menu" import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" -import { ModeToggle } from "@/components/Theme/mode-toggle" import { useNavigate } from "react-router-dom"; -import { useState, useEffect } from "react" -const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000"; +// const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000"; const MobileSidebar = () => { - const [userImage, setUserImage] = useState(null); - const [isFullscreen, setIsFullscreen] = useState(() => { - // Parse the value from localStorage as a boolean - return localStorage.getItem('isFullscreen') === 'true'; - }); const navigate = useNavigate(); - const handleFullscreenToggle = () => { - if (!isFullscreen) { - document.documentElement.requestFullscreen(); - } else { - document.exitFullscreen(); - } - setIsFullscreen(!isFullscreen); - // Store the fullscreen state as a string ("true"/"false") - localStorage.setItem('isFullscreen', String(!isFullscreen)); - }; + - useEffect(() => { - const handleFullscreenChange = () => { - const fullscreen = document.fullscreenElement !== null; - setIsFullscreen(fullscreen); - localStorage.setItem('isFullscreen', String(fullscreen)); - }; - - document.addEventListener('fullscreenchange', handleFullscreenChange); - return () => { - document.removeEventListener('fullscreenchange', handleFullscreenChange); - }; - }, []); - - useEffect(() => { - const fetchUserData = async () => { - try { - const username = localStorage.getItem('username'); - if (!username) { - throw new Error('Username not found in localStorage'); - } - - const response = await fetch(`${BACKEND_URL}/profile/${username}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const responseData = await response.json(); - setUserImage(responseData.user.image || null); - } catch (error) { - console.error('Error fetching user data', error); - } - }; - - fetchUserData(); - }, []); + return (
@@ -95,7 +37,7 @@ const MobileSidebar = () => {
diff --git a/client/src/components/ui/placeholders-and-vanish-input.tsx b/client/src/components/ui/placeholders-and-vanish-input.tsx index 6b2d907..c22a212 100644 --- a/client/src/components/ui/placeholders-and-vanish-input.tsx +++ b/client/src/components/ui/placeholders-and-vanish-input.tsx @@ -6,11 +6,9 @@ import { cn } from "@/lib/utils"; export function PlaceholdersAndVanishInput({ placeholders, - onChange, onSubmit, }: { placeholders: string[]; - onChange: (e: React.ChangeEvent) => void; onSubmit: (e: React.FormEvent) => void; }) { const [currentPlaceholder, setCurrentPlaceholder] = useState(0); @@ -193,7 +191,6 @@ export function PlaceholdersAndVanishInput({ onChange={(e) => { if (!animating) { setValue(e.target.value); - onChange && onChange(e); } }} onKeyDown={handleKeyDown} diff --git a/client/src/index.css b/client/src/index.css index 3b9cfb7..2c9f4d8 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -17,57 +17,57 @@ body { @layer base { :root { --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; + --foreground: 240 10% 3.9%; --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; + --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; - --radius: 0.5rem; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 10% 3.9%; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; + --radius: 0.5rem } .dark { - --background: 0 0% 9%; - --foreground: 210 40% 98%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 11.2%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 212.7 26.8% 83.9%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; + --chart-5: 340 75% 55% } } diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx index 0e6717d..df21b17 100644 --- a/client/src/pages/Home.tsx +++ b/client/src/pages/Home.tsx @@ -1,36 +1,23 @@ -import { cn } from "@/lib/utils"; -import { PlaceholdersAndVanishInputDemo } from "@/components/Chat/chat"; -import HomeSidebar from "@/components/Sidebar/HomeSidebar"; +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import Chat from '@/components/Chat/chat'; -interface HomeProps { - onLogout: () => void; - username: string; -} +const Home = () => { + const navigate = useNavigate(); -const Home: React.FC = ({ onLogout, username }) => { - - return ( -
- - -
- ); -}; + useEffect(() => { + const username = localStorage.getItem('devhub_username'); + if (!username) { + navigate('/login'); + } + }, [navigate]); -const Dashboard = () => { return ( -
-
- -
+
+
); }; -export default Home; \ No newline at end of file +export default Home; diff --git a/client/src/pages/Landing.tsx b/client/src/pages/Landing.tsx index dfeb372..69fe41d 100644 --- a/client/src/pages/Landing.tsx +++ b/client/src/pages/Landing.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { HoverBorderGradient } from "@/components/ui/hover-border-gradient"; import Footer from '@/components/Footer/Footer'; -import { Navbar } from "@/components/Navbar/Navbar"; +import Navbar from "@/components/Navbar/Navbar"; import { Hero } from '@/components/Hero/Hero'; import { Cover } from '@/components/ui/cover'; diff --git a/client/src/pages/Login.tsx b/client/src/pages/Login.tsx index e69b84b..e51871c 100644 --- a/client/src/pages/Login.tsx +++ b/client/src/pages/Login.tsx @@ -1,14 +1,19 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; +import { useEffect } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; import { cn } from "@/lib/utils"; import { buttonVariants } from "@/components/ui/button"; import { UserAuthForm } from "@/components/Auth/user-auth-form-login"; -interface LoginProps { - onLoginSuccess: (username: string) => void; -} +const Login = () => { + const navigate = useNavigate(); + + useEffect(() => { + const storedUsername = localStorage.getItem('devhub_username'); + if (storedUsername) { + navigate('/home'); + } + }, [navigate]); -const Login: React.FC = ({ onLoginSuccess }) => { return (
= ({ onLoginSuccess }) => { Enter your details below to Login to your account

- onLoginSuccess(username)} /> +

By clicking continue, you agree to our{" "} = ({ onLoginSuccess }) => {

- ); }; diff --git a/client/src/pages/Profile.tsx b/client/src/pages/Profile.tsx index 90465ac..464c5f8 100644 --- a/client/src/pages/Profile.tsx +++ b/client/src/pages/Profile.tsx @@ -4,7 +4,6 @@ import axios from "axios"; import EditProfileForm from "./EditProfileForm"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import HomeSidebar from "@/components/Sidebar/HomeSidebar"; import { Card, CardHeader, CardContent } from "@/components/ui/card"; import { useDispatch, useSelector } from "react-redux"; import { RootState, AppDispatch } from "../lib/store"; @@ -13,11 +12,6 @@ import { FaExternalLinkAlt } from "react-icons/fa"; const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000"; -interface ProfileProps { - onLogout: () => void; - username: string; -} - export interface Tag { value: string; label: string; @@ -28,11 +22,11 @@ interface Project { repoLink: string; tags: Tag[]; title: string; - repo?: string; - link?: string; - language?: string; + repo?: string; + link?: string; + language?: string; stars?: number; - forks?: number; + forks?: number; } interface UserResponse { @@ -60,53 +54,21 @@ interface Language { percentage: string; } -// interface LeetCodeData { -// totalSolved: number; -// totalSubmissions: number; -// totalQuestions: number; -// easySolved: number; -// totalEasy: number; -// mediumSolved: number; -// totalMedium: number; -// hardSolved: number; -// totalHard: number; -// ranking: number; -// contributionPoint: number; -// reputation: number; -// submissionCalendar: string; -// recentSubmissions: { -// title: string; -// titleSlug: string; -// timestamp: string; -// statusDisplay: string; -// lang: string; -// }[]; -// } - -const Profile: React.FC = ({ onLogout, username }) => { - return ( -
- - -
- ); -}; +// const Profile = () => { +// const { username } = useParams<{ username: string }>(); +// return ( + +// ); +// }; interface DashboardProps { - loggedInUsername: string; + username: string; } -const Dashboard: React.FC = ({ loggedInUsername }) => { - const { username } = useParams<{ username: string }>(); +const Profile: React.FC = ({ username }) => { const dispatch = useDispatch(); const friends = useSelector((state: RootState) => state.user.friends); - // const friendStatus = useSelector( - // (state: RootState) => state.user.friendStatus - // ); + const loggedInUsername = useSelector((state: RootState) => state.auth.username); const [profileData, setProfileData] = useState(); const [editing, setEditing] = useState(false); const [githubData, setGithubData] = useState(null); @@ -243,9 +205,9 @@ const Dashboard: React.FC = ({ loggedInUsername }) => { setProfileData((prevData) => prevData ? { - ...prevData, - projects: [...prevData.projects, newProject], - } + ...prevData, + projects: [...prevData.projects, newProject], + } : prevData ); }; @@ -316,261 +278,106 @@ const Dashboard: React.FC = ({ loggedInUsername }) => {

Bio

-

- {profileData.bio} -

+

{profileData.bio || "This user hasn't added a bio yet."}

-

Email

-

- {profileData.email} -

-
- {/*
-

Github

-

- {profileData.githubUsername} -

-
-
-

Leetcode

-

- {profileData.leetcodeUsername} -

-
*/} - - -
-

Top Languages

+

Top Languages

{languages.length > 0 ? ( -
- {languages.map((lang, index) => ( - - {lang.language}: {lang.percentage} - - ))} -
- ) : ( -

Loading languages...

- )} -
- -
- {streakStats ? ( -
+ languages.map((lang) => ( +

+ {lang.language}: {lang.percentage}% +

+ )) ) : ( -

Loading streak stats...

+

No languages data available.

)}
- - {!isOwnProfile && ( - - )} - - {isOwnProfile && ( - - )}
- -
-
-
-

Projects

-
-
-
- {profileData.projects.map((project: Project) => ( - - -
-
-
{project.title}
-
- @{profileData.username} -
+
+ + +

Pinned Projects

+
+ + {pinnedRepos.length > 0 ? ( + pinnedRepos.map((project) => ( +
+

{project.title}

+

{project.description}

+
+ {project.tags.map((tag, index) => ( + + {tag.label} + + ))}
-
- - -

{project.description}

-
-
- ))} -
-
- -
-
-

Friends

- {friends.length > 0 ? ( - friends.map((friend: string) => ( -
- - {friend} - + {project.repoLink && ( + + GitHub Repo + + )}
)) ) : ( -

- No friends yet -

+

No pinned projects available.

)} -
- -
- {leetcodeSvg ? ( -
- ) : ( -

Loading LeetCode data...

- )} -
- - {githubData ? ( -
-

GitHub Profile Summary

- -
-
-

{githubData.public_repos}

-

- Repositories -

-
-
-

{githubData.followers}

-

- Followers -

-
-
-

{githubData.following}

-

- Following -

-
-
-
- ) : ( -

Loading GitHub data...

- )} - {githubStreakSvg ? ( -
- ) : ( -

Loading GitHub streak chart...

- )} + + +
+
+ + +

GitHub Streak

+
+ + {githubStreakSvg ? ( +
+ ) : ( +

No streak data available.

+ )} + + + + +

LeetCode Stats

+
+ + {leetcodeSvg ? ( +
+ ) : ( +

No LeetCode stats available.

+ )} + + + + +

Friend Status

+
+ + {isOwnProfile ? ( +

You can't add yourself as a friend.

+ ) : ( + + )} +
+
)} - -
-

Pinned Repositories:

- {pinnedRepos.length > 0 ? ( -
- {pinnedRepos.map((repo, index) => ( - - - -

- {repo.title} -

-

- {repo.description} -

-
-
- -
- {repo.language} -
- - {repo.stars && ( -
- Star -
- {repo.stars} -
-
- )} -
-
- {repo.forks != null && repo.forks > 0 && ( -
- Fork -
- {repo.forks} -
-
- )} -
-
- - ))} -
- ) : ( -

No pinned repositories found.

- )} -
); diff --git a/server/api/handlers/auth/userauth.py b/server/api/handlers/auth/userauth.py index 466f8a2..ed87170 100644 --- a/server/api/handlers/auth/userauth.py +++ b/server/api/handlers/auth/userauth.py @@ -1,7 +1,8 @@ from flask import request, jsonify, session, redirect, url_for, current_app from flask import Flask, request, jsonify, session as flask_session -from extensions import bcrypt, neo4j_db +from extensions import bcrypt, neo4j_db, users_chat from models import User +from pymongo import MongoClient def signup(): try: @@ -30,6 +31,13 @@ def signup(): with neo4j_db.driver.session() as session: session.run(query, username=data['username'], email=data['email'], password=hashed_password) + # Insert user into MongoDB with an empty chat history array + mongo_user = { + 'username': data['username'], + 'chat_history': [] + } + users_chat.insert_one(mongo_user) + return jsonify({'message': 'User created successfully'}), 201 except Exception as e: diff --git a/server/api/handlers/query/querymodel.py b/server/api/handlers/query/querymodel.py index 498a9dc..b435cb8 100644 --- a/server/api/handlers/query/querymodel.py +++ b/server/api/handlers/query/querymodel.py @@ -4,10 +4,11 @@ from langchain_google_genai import ChatGoogleGenerativeAI from langchain_community.chains.graph_qa.cypher import GraphCypherQAChain from langchain_community.graphs import Neo4jGraph -import sqlite3 -import uuid +from extensions import users_chat +import logging # Load environment variables +load_dotenv() os.environ["GOOGLE_API_KEY"] = os.getenv("GOOGLE_API_KEY") os.environ["NEO4J_URI"] = os.getenv("NEO4J_URI") os.environ["NEO4J_USERNAME"] = os.getenv("NEO4J_USER") @@ -18,43 +19,47 @@ llm = ChatGoogleGenerativeAI(model="gemini-pro") chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, verbose=True) -# Initialize SQLite database connection -def init_db(): - conn = sqlite3.connect('chat.db') - cursor = conn.cursor() - cursor.execute(''' - CREATE TABLE IF NOT EXISTS chats ( - chat_id TEXT PRIMARY KEY, - query TEXT NOT NULL, - response TEXT NOT NULL - ) - ''') - conn.commit() - conn.close() - -init_db() - -def save_chat(chat_id, query, response): - """Save chat message to SQLite database.""" - conn = sqlite3.connect('chat.db') - cursor = conn.cursor() - cursor.execute(''' - INSERT INTO chats (chat_id, query, response) VALUES (?, ?, ?) - ''', (chat_id, query, response)) - conn.commit() - conn.close() - -def get_chat_by_id(chat_id): - """Retrieve chat query and response by chat ID from SQLite database.""" - conn = sqlite3.connect('chat.db') - cursor = conn.cursor() - cursor.execute(''' - SELECT query, response FROM chats WHERE chat_id = ? - ''', (chat_id,)) - result = cursor.fetchone() - conn.close() - return {"query": result[0], "response": result[1]} if result else None +def save_chat(username, query, response): + """Save chat entry to the user's chat history in the database.""" + chat_entry = { + 'query': query, + 'response': response + } + + logging.info(f"Saving chat for user: {username}") + logging.info(f"Chat entry: {chat_entry}") + + result = users_chat.update_one( + {'username': username}, + {'$push': {'chat_history': chat_entry}} + ) + + if result.modified_count == 0: + logging.error(f"Failed to update chat history for user: {username}") + else: + logging.info(f"Successfully updated chat history for user: {username}") + + return chat_entry +def get_chat_history(username): + """Retrieve chat history for a specific user from MongoDB. If empty, initialize with a welcome message.""" + user = users_chat.find_one({'username': username}, {'chat_history': 1}) + + if user and 'chat_history' in user: + # Check if chat history is empty + if not user['chat_history']: + # Initialize chat history with a welcome message + welcome_message = { + 'query': "Hi", + 'response': "Welcome to DevHub!" + } + users_chat.update_one( + {'username': username}, + {'$push': {'chat_history': welcome_message}} + ) + return [welcome_message] # Return the newly added message + + return user['chat_history'] if user else [] def get_graph_response(query): """Get response from graph database using natural language query.""" @@ -65,26 +70,32 @@ def chat(): """Chat route to handle user queries and return responses.""" data = request.json query = data.get("query") + username = data.get("username") # Get username from request body if not query: return jsonify({"error": "No query provided"}), 400 + if not username: + return jsonify({"error": "Username is required"}), 400 # Check for username + try: result = get_graph_response(query) - chat_id = str(uuid.uuid4()) # Generate unique chat ID - save_chat(chat_id, query, result) + chat_id = save_chat(username, query, result) return jsonify({"chat_id": chat_id, "result": result}), 200 except Exception as e: + logging.error(f"Error in chat: {str(e)}") return jsonify({"error": "An error occurred while processing the query"}), 500 -def retrieve_chat(chat_id): - """Retrieve chat response by chat ID.""" +def chat_history(): + """Retrieve chat history for the specified user.""" + username = request.args.get("username") # Get username from query parameters + + if not username: + return jsonify({"error": "Username is required"}), 400 # Check for username + try: - result = get_chat_by_id(chat_id) - if result: - return jsonify({"chat_id": chat_id, "result": result}), 200 - else: - return jsonify({"error": "Chat ID not found"}), 404 + history = get_chat_history(username) + return jsonify({"chat_history": history}), 200 except Exception as e: - return jsonify({"error": "An error occurred while retrieving the chat"}), 500 - + logging.error(f"Error retrieving chat history: {str(e)}") + return jsonify({"error": "An error occurred while retrieving the chat history"}), 500 \ No newline at end of file diff --git a/server/api/urls.py b/server/api/urls.py index 9effb18..5d24bf2 100644 --- a/server/api/urls.py +++ b/server/api/urls.py @@ -2,7 +2,7 @@ from api.handlers.user.profile import get_profile, update_profile, add_project, update_project, delete_project from api.handlers.analyze.githubdata import github_data, top_languages, streak_stats, pinned_repos, streak_chart from api.handlers.analyze.leetcodedata import leetcode_data, leetcode_card -from api.handlers.query.querymodel import chat,retrieve_chat +from api.handlers.query.querymodel import chat,chat_history from api.handlers.user.friends import friends_bp def register_routes(app): @@ -37,8 +37,8 @@ def register_routes(app): app.add_url_rule('/profile//projects/', 'delete_project', delete_project, methods=['DELETE']) # Chat with model routes - app.add_url_rule('/chat','chat',chat, methods=['POST']) - app.add_url_rule('/chat/','retrieve_chat',retrieve_chat, methods=['GET']) + app.add_url_rule('/chat', 'chat', chat, methods=['POST']) + app.add_url_rule('/chat_history', 'chat_history', chat_history, methods=['GET']) # Landing page route app.add_url_rule('/', 'index', index) diff --git a/server/config.py b/server/config.py index c80a10a..a5ab183 100644 --- a/server/config.py +++ b/server/config.py @@ -8,3 +8,4 @@ class Config: NEO4J_URI = os.getenv('NEO4J_URI') NEO4J_USER = os.getenv('NEO4J_USER') NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD') + MONGODB_URI = os.getenv('MONGODB_URI') diff --git a/server/extensions.py b/server/extensions.py index 81e44dd..dda76a1 100644 --- a/server/extensions.py +++ b/server/extensions.py @@ -2,6 +2,7 @@ from flask_cors import CORS from neo4j import GraphDatabase from config import Config +from pymongo import MongoClient bcrypt = Bcrypt() cors = CORS() @@ -18,3 +19,8 @@ def close(self): user=Config.NEO4J_USER, password=Config.NEO4J_PASSWORD ) + +# MongoDB setup +mongo_client = MongoClient(Config.MONGODB_URI) +mongo_db = mongo_client['devhub'] +users_chat = mongo_db['users'] \ No newline at end of file diff --git a/server/requirements.txt b/server/requirements.txt index adc0ecc..6e73322 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -12,4 +12,5 @@ google-generativeai==0.5.4 beautifulsoup4==4.12.3 langchain==0.2.1 langchain-community==0.2.1 -langchain-google-genai==1.0.5 \ No newline at end of file +langchain-google-genai==1.0.5 +pymongo \ No newline at end of file From a68d23ba347778a01ccb7278b5f05e4664b83a9f Mon Sep 17 00:00:00 2001 From: Deepraj Date: Thu, 17 Oct 2024 12:49:48 +0530 Subject: [PATCH 2/3] updated chat section --- client/package-lock.json | 57 +++++++ client/package.json | 2 + client/src/components/Chat/chat.tsx | 156 ++++++++++-------- .../MobileSidebar/MobileSidebar.tsx | 15 +- client/src/components/ui/avatar.tsx | 48 ++++++ client/src/components/ui/scroll-area.tsx | 46 ++++++ client/src/pages/Home.tsx | 2 +- server/api/handlers/query/querymodel.py | 27 ++- 8 files changed, 269 insertions(+), 84 deletions(-) create mode 100644 client/src/components/ui/avatar.tsx create mode 100644 client/src/components/ui/scroll-area.tsx diff --git a/client/package-lock.json b/client/package-lock.json index 8f3151c..ef6c5a4 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -10,11 +10,13 @@ "dependencies": { "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", + "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-navigation-menu": "^1.2.0", + "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", @@ -1147,6 +1149,31 @@ } } }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.1.tgz", + "integrity": "sha512-eoOtThOmxeoizxpX6RiEsQZ2wj5r4+zoeqAwO0cBaFQGjJwIH3dIX0OCxNrCyrrdxG+vBweMETh3VziQG7c1kw==", + "dependencies": { + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz", @@ -1662,6 +1689,36 @@ } } }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.0.tgz", + "integrity": "sha512-q2jMBdsJ9zB7QG6ngQNzNwlvxLQqONyL58QbEGwuyRZZb/ARQwk3uQVbCF7GvQVOtV6EU/pDxAw3zRzJZI3rpQ==", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-select": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.2.tgz", diff --git a/client/package.json b/client/package.json index 0b6ef8b..673510c 100644 --- a/client/package.json +++ b/client/package.json @@ -12,11 +12,13 @@ "dependencies": { "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.2.0", + "@radix-ui/react-avatar": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-navigation-menu": "^1.2.0", + "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", diff --git a/client/src/components/Chat/chat.tsx b/client/src/components/Chat/chat.tsx index 1044a41..ddc52c7 100644 --- a/client/src/components/Chat/chat.tsx +++ b/client/src/components/Chat/chat.tsx @@ -1,22 +1,21 @@ -import Sidebar from "../Sidebar/Sidebar"; -import MobileSidebar from "../MobileSidebar/MobileSidebar"; -import { PlaceholdersAndVanishInput } from '../ui/placeholders-and-vanish-input'; -import { useState, useEffect, useRef } from 'react'; -import axios from 'axios'; +import { useState, useEffect, useRef } from 'react' +import axios from 'axios' +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { PlaceholdersAndVanishInput } from '@/components/ui/placeholders-and-vanish-input' +import Sidebar from "@/components/Sidebar/Sidebar" +import MobileSidebar from "@/components/MobileSidebar/MobileSidebar" const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; interface Message { - query: string; - response: string; - isLoading?: boolean; + query: string + response: string + isLoading?: boolean } export default function Chat() { - const [messages, setMessages] = useState>([]); - const messagesEndRef = useRef(null); - - + const [messages, setMessages] = useState>([]) + const scrollAreaRef = useRef(null) const placeholders = [ 'How to set up a React project with Vite?', @@ -25,107 +24,123 @@ export default function Chat() { 'What is a closure in JavaScript?', 'How to optimize React app performance?', 'Explain event delegation in JavaScript', - ]; + ] const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }; + if (scrollAreaRef.current) { + scrollAreaRef.current.scrollTop = scrollAreaRef.current.scrollHeight + } + } + useEffect(() => { - scrollToBottom(); - }, [messages]); + scrollToBottom() + }, [messages]) - const username = localStorage.getItem('devhub_username'); + const username = typeof window !== 'undefined' ? localStorage.getItem('devhub_username') : null useEffect(() => { const fetchChatHistory = async () => { if (!username) { - console.error('Username not found in localStorage'); - return; + console.error('Username not found in localStorage') + return } try { const result = await axios.get(`${backendUrl}/chat_history`, { params: { username }, withCredentials: true - }); + }) const chatHistory = result.data.chat_history.length > 0 ? result.data.chat_history - : [{ query: 'Hi', response: 'Welcome to DevHub!' }]; - setMessages(chatHistory); + : [{ query: 'Hi', response: 'Welcome to DevHub!' }] + setMessages(chatHistory) } catch (error) { - console.error('Error fetching chat history:', error); + console.error('Error fetching chat history:', error) } - }; + } - fetchChatHistory(); - }, [username]); + fetchChatHistory() + }, [username]) const onSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - const query = e.currentTarget.querySelector('input')?.value; - if (!query) return; + e.preventDefault() + const query = e.currentTarget.querySelector('input')?.value + if (!query) return if (!username) { - console.error('Username not found in localStorage'); - return; + console.error('Username not found in localStorage') + return } setMessages((prevMessages) => [ ...prevMessages, { query, response: '', isLoading: true }, - ]); + ]) try { - const result = await axios.post(`${backendUrl}/chat`, { query, username }, { withCredentials: true }); + const result = await axios.post(`${backendUrl}/chat`, { query, username }, { withCredentials: true }) setMessages((prevMessages) => { - const newMessages = [...prevMessages]; - const lastMessage = newMessages[newMessages.length - 1]; - lastMessage.response = result.data.result; - lastMessage.isLoading = false; - return newMessages; - }); + const newMessages = [...prevMessages] + const lastMessage = newMessages[newMessages.length - 1] + lastMessage.response = result.data.result + lastMessage.isLoading = false + return newMessages + }) } catch (error) { - console.error('Error querying the model:', error); + console.error('Error querying the model:', error) setMessages((prevMessages) => { - const newMessages = [...prevMessages]; - const lastMessage = newMessages[newMessages.length - 1]; - lastMessage.response = 'An error occurred while processing your request.'; - lastMessage.isLoading = false; - return newMessages; - }); + const newMessages = [...prevMessages] + const lastMessage = newMessages[newMessages.length - 1] + lastMessage.response = 'An error occurred while processing your request.' + lastMessage.isLoading = false + return newMessages + }) } - }; + } + return (
-
+
-
-
- {messages.map((message, index) => ( -
- -

You:

-

{message.query}

- +
+
+
+ {messages.map((message, index) => ( +
+
+
+

{message.query}

+
+ +
+
+ + + dh + +
+ {message.isLoading ? ( +

Thinking...

+ ) : ( +

{message.response}

+ )} +
+
+ +
+ ))} +
-

AI:

- {message.isLoading ? ( -

Loading...

- ) : ( -

{message.response}

- )} - -
- ))} -
-
+
+ +
-
- ); -} + ) +} \ No newline at end of file diff --git a/client/src/components/MobileSidebar/MobileSidebar.tsx b/client/src/components/MobileSidebar/MobileSidebar.tsx index 3ff37c2..a3b36b0 100644 --- a/client/src/components/MobileSidebar/MobileSidebar.tsx +++ b/client/src/components/MobileSidebar/MobileSidebar.tsx @@ -16,17 +16,11 @@ import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import { useNavigate } from "react-router-dom"; -// const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000"; - const MobileSidebar = () => { const navigate = useNavigate(); - - - - return ( -
+
@@ -82,7 +74,10 @@ const MobileSidebar = () => { navigate('/settings')}>Settings - Logout + { + localStorage.removeItem('devhub_username'); + navigate('/'); + }}>Logout
diff --git a/client/src/components/ui/avatar.tsx b/client/src/components/ui/avatar.tsx new file mode 100644 index 0000000..991f56e --- /dev/null +++ b/client/src/components/ui/avatar.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/client/src/components/ui/scroll-area.tsx b/client/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..cf253cf --- /dev/null +++ b/client/src/components/ui/scroll-area.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" + +import { cn } from "@/lib/utils" + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)) +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)) +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName + +export { ScrollArea, ScrollBar } diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx index df21b17..b67194a 100644 --- a/client/src/pages/Home.tsx +++ b/client/src/pages/Home.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import Chat from '@/components/Chat/chat'; +import Chat from '@/components/Chat/Chat'; const Home = () => { const navigate = useNavigate(); diff --git a/server/api/handlers/query/querymodel.py b/server/api/handlers/query/querymodel.py index b435cb8..b29e6c7 100644 --- a/server/api/handlers/query/querymodel.py +++ b/server/api/handlers/query/querymodel.py @@ -6,6 +6,7 @@ from langchain_community.graphs import Neo4jGraph from extensions import users_chat import logging +import google.generativeai as genai # Load environment variables load_dotenv() @@ -13,12 +14,33 @@ os.environ["NEO4J_URI"] = os.getenv("NEO4J_URI") os.environ["NEO4J_USERNAME"] = os.getenv("NEO4J_USER") os.environ["NEO4J_PASSWORD"] = os.getenv("NEO4J_PASSWORD") +genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) # Initialize Neo4j graph and language model graph = Neo4jGraph() llm = ChatGoogleGenerativeAI(model="gemini-pro") chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, verbose=True) +# Define model configuration +generation_config = { + "temperature": 0.5, # Adjust randomness + "top_p": 0.95, + "top_k": 50, + "max_output_tokens": 500, # Limit the response length + "response_mime_type": "text/plain", +} + +# Initialize the model +model = genai.GenerativeModel( + model_name="gemini-1.5-flash", + generation_config=generation_config, +) + +# Predefined prompt to guide the chatbot's behavior +pre_prompt = """ +You are a model of Devhub and you are here to help people with their queries. you will be provided a question and a previous model response. your work is to see the response and if the response is not upto mark like i don't know the answer or something like that then see the question and answer it properly. +""" + def save_chat(username, query, response): """Save chat entry to the user's chat history in the database.""" chat_entry = { @@ -80,8 +102,9 @@ def chat(): try: result = get_graph_response(query) - chat_id = save_chat(username, query, result) - return jsonify({"chat_id": chat_id, "result": result}), 200 + response = model.generate_content(pre_prompt+query+result) + chat_id = save_chat(username, query, response.text) + return jsonify({"chat_id": chat_id, "result": response.text}), 200 except Exception as e: logging.error(f"Error in chat: {str(e)}") return jsonify({"error": "An error occurred while processing the query"}), 500 From fa9a3c31a7d0844071022d2094f5f5212e7df71d Mon Sep 17 00:00:00 2001 From: Deepraj Date: Thu, 17 Oct 2024 12:59:40 +0530 Subject: [PATCH 3/3] removing merge conflict --- client/src/components/Sidebar/HomeSidebar.tsx | 130 ++++ client/src/pages/Profile.tsx | 592 ++++++++++++------ 2 files changed, 526 insertions(+), 196 deletions(-) create mode 100644 client/src/components/Sidebar/HomeSidebar.tsx diff --git a/client/src/components/Sidebar/HomeSidebar.tsx b/client/src/components/Sidebar/HomeSidebar.tsx new file mode 100644 index 0000000..b6ed175 --- /dev/null +++ b/client/src/components/Sidebar/HomeSidebar.tsx @@ -0,0 +1,130 @@ +import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Sidebar, SidebarBody, SidebarLink } from '@/components/ui/sidebar'; +import { IconArrowLeft, IconBrandTabler } from '@tabler/icons-react'; +import { Link } from 'react-router-dom'; +import { motion } from 'framer-motion'; + +interface HomeProps { + onLogout: () => void; + username: string; +} + +const HomeSidebar: React.FC = ({ onLogout, username }) => { + const navigate = useNavigate(); + + const handleLogout = async () => { + try { + await onLogout(); + navigate('/'); + } catch (error) { + console.error('Logout failed:', error); + } + }; + + const goToProfile = () => { + navigate(`/u/${username}`); + }; + + const goToHome = () => { + navigate('/home'); + }; + + const links = [ + { + label: 'Dashboard', + href: '#', + icon: ( + + ), + onClick: goToHome, + }, + ]; + + const [open, setOpen] = useState(false); + + return ( + + +
+ {open ? : } +
+ {links.map((link, idx) => ( + + ))} +
+
+
+ + ), + onClick: goToProfile, + }} + /> + + ), + onClick: handleLogout, + }} + /> +
+
+
+ ); +}; + +export const Logo = () => { + return ( + + {/*
*/} +

+ dh +

+ + Devhub + + + ); +}; + +export const LogoIcon = () => { + return ( + +

+ dh +

+ + ); +}; + +export default HomeSidebar; \ No newline at end of file diff --git a/client/src/pages/Profile.tsx b/client/src/pages/Profile.tsx index 464c5f8..fc9bc00 100644 --- a/client/src/pages/Profile.tsx +++ b/client/src/pages/Profile.tsx @@ -1,32 +1,55 @@ -import React, { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; -import axios from "axios"; -import EditProfileForm from "./EditProfileForm"; -import { Button } from "@/components/ui/button"; -import { cn } from "@/lib/utils"; -import { Card, CardHeader, CardContent } from "@/components/ui/card"; -import { useDispatch, useSelector } from "react-redux"; -import { RootState, AppDispatch } from "../lib/store"; -import { setFriends, setFriendStatus } from "../lib/userSlice"; -import { FaExternalLinkAlt } from "react-icons/fa"; - -const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000"; +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import axios from 'axios'; +import EditProfileForm from './EditProfileForm'; +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; +import HomeSidebar from '@/components/Sidebar/HomeSidebar'; +import { Card, CardHeader, CardContent } from '@/components/ui/card'; +import { useDispatch, useSelector } from 'react-redux'; +import { RootState, AppDispatch } from '../lib/store'; +import { setFriends, setFriendStatus } from '../lib/userSlice'; +import { FaExternalLinkAlt, FaStar, FaCodeBranch } from 'react-icons/fa'; +import { Skeleton } from '@/components/ui/skeleton'; + +const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000'; + +interface ProfileProps { + onLogout: () => void; + username: string; +} export interface Tag { value: string; label: string; } +// interface Project { +// description: string; +// repoLink: string; +// tags: Tag[]; +// title: string; +// repo?: string; +// link?: string; +// language?: string; +// stars?: number; +// forks?: number; +// } + interface Project { + owner: string; + repo: string; + link: string; description: string; - repoLink: string; - tags: Tag[]; - title: string; - repo?: string; - link?: string; - language?: string; - stars?: number; - forks?: number; + image: string; + website: string[]; + language: string; + languageColor: string; + stars: number; + forks: number; + title?: string; // Add this if you still need it + tags?: Tag[]; // Add this if you still need it + repoLink?: string; // Add this if you still need it } interface UserResponse { @@ -54,29 +77,67 @@ interface Language { percentage: string; } -// const Profile = () => { -// const { username } = useParams<{ username: string }>(); -// return ( - -// ); -// }; +// interface LeetCodeData { +// totalSolved: number; +// totalSubmissions: number; +// totalQuestions: number; +// easySolved: number; +// totalEasy: number; +// mediumSolved: number; +// totalMedium: number; +// hardSolved: number; +// totalHard: number; +// ranking: number; +// contributionPoint: number; +// reputation: number; +// submissionCalendar: string; +// recentSubmissions: { +// title: string; +// titleSlug: string; +// timestamp: string; +// statusDisplay: string; +// lang: string; +// }[]; +// } + +const Profile: React.FC = ({ onLogout, username }) => { + return ( +
+ + +
+ ); +}; interface DashboardProps { - username: string; + loggedInUsername: string; } -const Profile: React.FC = ({ username }) => { +const Dashboard: React.FC = ({ loggedInUsername }) => { + const { username } = useParams<{ username: string }>(); const dispatch = useDispatch(); const friends = useSelector((state: RootState) => state.user.friends); - const loggedInUsername = useSelector((state: RootState) => state.auth.username); + // const friendStatus = useSelector( + // (state: RootState) => state.user.friendStatus + // ); const [profileData, setProfileData] = useState(); const [editing, setEditing] = useState(false); const [githubData, setGithubData] = useState(null); const [languages, setLanguages] = useState([]); - const [streakStats, setStreakStats] = useState(null); + // const [streakStats, setStreakStats] = useState(null); + const [streakStat, setStreakStat] = useState(null); const [pinnedRepos, setPinnedRepos] = useState([]); const [leetcodeSvg, setLeetcodeSvg] = useState(null); const [githubStreakSvg, setGithubStreakSvg] = useState(null); + const [githubContributionsSvg, setGithubContributionsSvg] = useState< + string | null + >(null); useEffect(() => { const fetchGithubData = async () => { @@ -85,57 +146,54 @@ const Profile: React.FC = ({ username }) => { const githubResponse = await axios.post( `${backendUrl}/analyze/github_data`, { - "github-id": profileData.githubUsername, - } + 'github-id': profileData.githubUsername, + }, ); setGithubData(githubResponse.data); const languagesResponse = await axios.post( `${backendUrl}/analyze/top_languages`, { - "github-id": profileData.githubUsername, - } + 'github-id': profileData.githubUsername, + }, ); setLanguages(languagesResponse.data); - console.log("languagesResponse", languagesResponse); + + const pinnedReposResponse = await axios.post( + `${backendUrl}/analyze/pinned_repos`, + { + 'github-id': profileData.githubUsername, + }, + ); + setPinnedRepos(pinnedReposResponse.data); const streakResponse = await axios.post( - `${backendUrl}/analyze/streak_stats`, + `${backendUrl}/analyze/streak_chart`, { - "github-id": profileData.githubUsername, - } + 'github-id': profileData.githubUsername, + }, + { responseType: 'text' }, ); - setStreakStats(streakResponse.data); + setGithubStreakSvg(streakResponse.data); - const pinnedReposResponse = await axios.post( - `${backendUrl}/analyze/pinned_repos`, + const streakStats = await axios.post( + `${backendUrl}/analyze/streak_stats`, { - "github-id": profileData.githubUsername, - } + 'github-id': profileData.githubUsername, + }, ); - setPinnedRepos(pinnedReposResponse.data); + setStreakStat(streakStats.data); - const fetchGithubStreakChart = async () => { - if (profileData?.githubUsername) { - try { - const githubResponse = await axios.post( - `${backendUrl}/analyze/streak_chart`, - { - "github-id": profileData.githubUsername, - }, - { - responseType: "text", // Get the SVG as text - } - ); - setGithubStreakSvg(githubResponse.data); - } catch (error) { - console.error("Failed to fetch GitHub streak chart:", error); - } - } - }; - fetchGithubStreakChart(); + const contributionsResponse = await axios.post( + `${backendUrl}/analyze/contributions_chart`, + { + 'github-id': profileData.githubUsername, + }, + { responseType: 'text' }, + ); + setGithubContributionsSvg(contributionsResponse.data); } catch (error) { - console.error("Failed to fetch GitHub data:", error); + console.error('Failed to fetch GitHub data:', error); } } }; @@ -150,16 +208,16 @@ const Profile: React.FC = ({ username }) => { const leetcodeResponse = await axios.post( `${backendUrl}/analyze/leetcode_card`, { - "leetcode-id": profileData.leetcodeUsername, + 'leetcode-id': profileData.leetcodeUsername, }, { - responseType: "text", // Important to get the SVG as text - } + responseType: 'text', // Important to get the SVG as text + }, ); setLeetcodeSvg(leetcodeResponse.data); } catch (error) { - console.error("Failed to fetch LeetCode card:", error); + console.error('Failed to fetch LeetCode card:', error); } } }; @@ -175,24 +233,24 @@ const Profile: React.FC = ({ username }) => { `${backendUrl}/profile/${username}`, { params: { logged_in_user: loggedInUsername }, - } + }, ); setProfileData(profileResponse.data); // Fetch friends data const friendsResponse = await axios.get( - `${backendUrl}/profile/${username}/friends` + `${backendUrl}/profile/${username}/friends`, ); dispatch(setFriends(friendsResponse.data.friends)); // Update friend status in Redux dispatch( setFriendStatus( - friendsResponse.data.friends.includes(loggedInUsername) - ) + friendsResponse.data.friends.includes(loggedInUsername), + ), ); } catch (error) { - console.error("Failed to fetch profile or friends data:", error); + console.error('Failed to fetch profile or friends data:', error); } }; @@ -208,7 +266,7 @@ const Profile: React.FC = ({ username }) => { ...prevData, projects: [...prevData.projects, newProject], } - : prevData + : prevData, ); }; @@ -219,16 +277,20 @@ const Profile: React.FC = ({ username }) => { data: { friend_username: loggedInUsername }, }); setProfileData((prev) => (prev ? { ...prev, isFriend: false } : prev)); - dispatch(setFriendStatus({ username: loggedInUsername, isFriend: false })); // Updated to pass an object + dispatch( + setFriendStatus({ username: loggedInUsername, isFriend: false }), + ); // Updated to pass an object } else { await axios.post(`${backendUrl}/profile/${username}/friends`, { friend_username: loggedInUsername, }); setProfileData((prev) => (prev ? { ...prev, isFriend: true } : prev)); - dispatch(setFriendStatus({ username: loggedInUsername, isFriend: true })); // Updated to pass an object + dispatch( + setFriendStatus({ username: loggedInUsername, isFriend: true }), + ); // Updated to pass an object } } catch (error) { - console.error("Failed to update friend status:", error); + console.error('Failed to update friend status:', error); } }; @@ -236,145 +298,283 @@ const Profile: React.FC = ({ username }) => { return
Error: No username provided
; } - if (!profileData) { - return
Loading...
; - } - const isOwnProfile = loggedInUsername === username; - return ( -
-
+
+
{editing ? ( ) : ( -
-
-
+
+ {/* Sidebar */} +
+
{githubData ? ( Avatar ) : ( - Avatar + )} - -
-

{profileData.name}

-

- @{profileData.username} +

+

+ {profileData ? ( + profileData.name + ) : ( + + )} +

+

+ {profileData ? ( + `@${profileData.username}` + ) : ( + + )}

-
-

Bio

-

{profileData.bio || "This user hasn't added a bio yet."}

+ {/* Profile Info Section */} +
+
+

Bio

+ {profileData ? ( +

+ {profileData.bio} +

+ ) : ( + + )} +
+ +
+

Email

+ {profileData ? ( +

+ {profileData.email} +

+ ) : ( + + )} +
+
+ {/* GitHub and LeetCode Links */} +
+ {profileData ? ( + <> + + {' '} + GitHub + + + {' '} + LeetCode + + + ) : ( + <> + + + + )}
-
-

Top Languages

+ +
+

Top Languages

{languages.length > 0 ? ( - languages.map((lang) => ( -

- {lang.language}: {lang.percentage}% -

- )) +
+ {languages.map((lang, index) => ( + + {lang.language}: {lang.percentage} + + ))} +
) : ( -

No languages data available.

+
+ {[1, 2, 3].map((i) => ( + + ))} +
)}
+ {/* Connect Button */} +
-
- - -

Pinned Projects

-
- - {pinnedRepos.length > 0 ? ( - pinnedRepos.map((project) => ( -
-

{project.title}

+ + {/* Main Content Section */} +
+ {/* Friends Section */} +
+

Friends

+ {friends.length > 0 ? ( + friends.map((friend, index) => ( + + )) + ) : ( + <>No friends + )} +
+

Projects

+
+ {profileData ? ( + profileData.projects.map((project, index) => ( + + +

{project.title}

+

+ @{profileData.username} +

+
+

{project.description}

-
- {project.tags.map((tag, index) => ( - - {tag.label} + + + )) + ) : ( + <> + + + + )} +
+ + {/* GitHub Stats */} +
+

GitHub Streak Stats

+ {streakStat ? ( +
+ ) : ( + + )} + +

GitHub Streak

+ {githubStreakSvg ? ( +
+ ) : ( + + )} + +

LeetCode Stats

+ {leetcodeSvg ? ( +
+ ) : ( + + )} +
+ +
+

Pinned Repositories

+ {pinnedRepos.length > 0 ? ( +
+ {pinnedRepos.map((repo, index) => ( + + {/*
*/} + +

+ {repo.repo.trim()} +

+

+ {repo.owner} +

+
+ +

+ {repo.description} +

+
+ {repo.language && ( + + + {repo.language} + + )} + + {repo.stars} - ))} -
- {project.repoLink && ( - - GitHub Repo - - )} -
- )) - ) : ( -

No pinned projects available.

- )} - -
-
-
- - -

GitHub Streak

-
- - {githubStreakSvg ? ( -
- ) : ( -

No streak data available.

- )} - - - - -

LeetCode Stats

-
- - {leetcodeSvg ? ( -
- ) : ( -

No LeetCode stats available.

- )} - - - - -

Friend Status

-
- - {isOwnProfile ? ( -

You can't add yourself as a friend.

- ) : ( - - )} -
-
+ + {repo.forks} + +
+
+ + View Repository{' '} + + + {repo.website && repo.website.length > 0 && ( + + Live Demo + + )} +
+
+
+ ))} +
+ ) : ( + + )} +
)} @@ -383,4 +583,4 @@ const Profile: React.FC = ({ username }) => { ); }; -export default Profile; +export default Profile; \ No newline at end of file