diff --git a/package-lock.json b/package-lock.json index c5098011..6d564da9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "bcryptjs": "^2.4.3", "blurhash": "^2.0.5", "date-fns": "^3.6.0", + "dompurify": "^3.3.1", "framer-motion": "^11.2.10", "html2canvas": "^1.4.1", "html5-qrcode": "^2.3.8", @@ -52,6 +53,7 @@ "react-loader-spinner": "^6.1.6", "react-loading-indicators": "^0.2.3", "react-loading-skeleton": "^3.4.0", + "react-markdown": "^10.1.0", "react-oauth": "^0.0.1", "react-qr-barcode-scanner": "^2.1.8", "react-quill": "^2.0.0", @@ -3061,13 +3063,30 @@ "@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==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, + "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==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -3076,6 +3095,15 @@ "@types/node": "*" } }, + "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==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -3102,6 +3130,21 @@ "@types/istanbul-lib-report": "*" } }, + "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==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.14.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz", @@ -3175,6 +3218,19 @@ "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "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==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3196,8 +3252,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/@vercel/analytics": { "version": "1.3.1", @@ -3868,6 +3923,16 @@ "@babel/core": "^7.0.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "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", @@ -4289,6 +4354,16 @@ } ] }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/cfb": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", @@ -4309,6 +4384,46 @@ "node": ">=10" } }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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", @@ -4518,6 +4633,16 @@ "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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", @@ -4791,6 +4916,19 @@ "node": ">=0.10.0" } }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -4916,6 +5054,15 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-element-overflow": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/detect-element-overflow/-/detect-element-overflow-1.4.2.tgz", @@ -4941,6 +5088,19 @@ "node": ">=8" } }, + "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==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/diff-sequences": { "version": "28.1.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", @@ -4987,6 +5147,15 @@ "csstype": "^3.0.2" } }, + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/dot-prop": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", @@ -5569,6 +5738,16 @@ "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==", + "license": "MIT", + "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", @@ -6407,6 +6586,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "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": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^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==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -6439,6 +6658,16 @@ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" }, + "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==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", @@ -6664,6 +6893,12 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -6690,6 +6925,30 @@ "node": ">= 12" } }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -6844,6 +7103,16 @@ "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==", + "license": "MIT", + "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", @@ -6906,6 +7175,16 @@ "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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-in-browser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", @@ -7005,6 +7284,18 @@ "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==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -8670,6 +8961,16 @@ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" }, + "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==", + "license": "MIT", + "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", @@ -8792,6 +9093,159 @@ "node": ">= 0.4" } }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "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/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==", + "license": "MIT", + "dependencies": { + "@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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "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/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==", + "license": "MIT", + "dependencies": { + "@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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@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": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "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-classify-character": "^2.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/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==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mem": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", @@ -8825,26 +9279,468 @@ "node": ">= 8" } }, - "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/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "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" - } + "@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.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "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", @@ -10287,6 +11183,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^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==", + "license": "MIT" + }, "node_modules/parse-github-url": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", @@ -10673,6 +11594,16 @@ "react-is": "^16.13.1" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -11205,6 +12136,33 @@ "react": ">=16.8.0" } }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.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-oauth": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/react-oauth/-/react-oauth-0.0.1.tgz", @@ -11655,6 +12613,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "license": "MIT", + "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.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "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/remote-git-tags": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remote-git-tags/-/remote-git-tags-3.0.0.tgz", @@ -12168,6 +13159,16 @@ "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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spawn-please": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-2.0.2.tgz", @@ -12383,6 +13384,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "license": "MIT", + "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", @@ -12444,6 +13459,24 @@ "node": ">=0.10.0" } }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/styled-components": { "version": "6.1.12", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz", @@ -12623,6 +13656,16 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, + "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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", @@ -12634,6 +13677,16 @@ "node": ">=0.10.0" } }, + "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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-custom-error": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz", @@ -12806,6 +13859,25 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "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/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -12842,6 +13914,74 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -13021,6 +14161,34 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.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==", + "license": "MIT", + "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.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "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.19", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", @@ -13576,6 +14744,16 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index ebe1627f..e5e79dc7 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "bcryptjs": "^2.4.3", "blurhash": "^2.0.5", "date-fns": "^3.6.0", + "dompurify": "^3.3.1", "framer-motion": "^11.2.10", "html2canvas": "^1.4.1", "html5-qrcode": "^2.3.8", @@ -54,6 +55,7 @@ "react-loader-spinner": "^6.1.6", "react-loading-indicators": "^0.2.3", "react-loading-skeleton": "^3.4.0", + "react-markdown": "^10.1.0", "react-oauth": "^0.0.1", "react-qr-barcode-scanner": "^2.1.8", "react-quill": "^2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85b5a747..f4a9830e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: date-fns: specifier: ^3.6.0 version: 3.6.0 + dompurify: + specifier: ^3.3.1 + version: 3.3.1 framer-motion: specifier: ^11.2.10 version: 11.2.11(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -140,6 +143,9 @@ importers: react-loading-skeleton: specifier: ^3.4.0 version: 3.4.0(react@18.3.1) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@18.3.3)(react@18.3.1) react-oauth: specifier: ^0.0.1 version: 0.0.1 @@ -1118,12 +1124,21 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} @@ -1136,6 +1151,12 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@20.14.6': resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} @@ -1169,6 +1190,15 @@ packages: '@types/stylis@4.2.5': resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -1420,6 +1450,9 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1516,6 +1549,9 @@ packages: caniuse-lite@1.0.30001636: resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + cfb@1.2.2: resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} engines: {node: '>=0.8'} @@ -1536,6 +1572,18 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1616,6 +1664,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -1718,6 +1769,9 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -1759,6 +1813,10 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-element-overflow@1.4.2: resolution: {integrity: sha512-4m6cVOtvm/GJLjo7WFkPfwXoEIIbM7GQwIh4WEa4g7IsNi1YzwUsGL5ApNLrrHL29bHeNeQ+/iZhw+YHqgE2Fw==} @@ -1770,6 +1828,9 @@ packages: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff-sequences@28.1.1: resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -1792,6 +1853,9 @@ packages: dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dompurify@3.3.1: + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} + dot-prop@6.0.1: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} @@ -1951,6 +2015,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2295,6 +2362,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -2309,6 +2382,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + html2canvas@1.4.1: resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} engines: {node: '>=8.0.0'} @@ -2398,6 +2474,9 @@ packages: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -2406,6 +2485,12 @@ packages: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-arguments@1.2.0: resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} engines: {node: '>= 0.4'} @@ -2451,6 +2536,9 @@ packages: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2474,6 +2562,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-in-browser@1.1.3: resolution: {integrity: sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==} @@ -2512,6 +2603,10 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -2862,6 +2957,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2916,6 +3014,30 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mem@8.1.1: resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} engines: {node: '>=10'} @@ -2930,6 +3052,69 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.7: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} @@ -3257,6 +3442,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-github-url@1.0.2: resolution: {integrity: sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==} engines: {node: '>=0.10.0'} @@ -3373,6 +3561,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -3538,6 +3729,12 @@ packages: peerDependencies: react: '>=16.8.0' + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-oauth@0.0.1: resolution: {integrity: sha512-vbzUrZzKQLILhFuvyelvpyOh9coi7/h6ZkhOEG89SYamviWaXKM3PKBiXMbLsRxf0QBV8CMvyZhtvmo0xL5Q0g==} @@ -3693,6 +3890,12 @@ packages: resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} engines: {node: '>=12'} + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + remote-git-tags@3.0.0: resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} engines: {node: '>=8'} @@ -3887,6 +4090,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spawn-please@2.0.2: resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} engines: {node: '>=14'} @@ -3955,6 +4161,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3987,6 +4196,12 @@ packages: resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} engines: {node: '>=0.10.0'} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + styled-components@6.1.12: resolution: {integrity: sha512-n/O4PzRPhbYI0k1vKKayfti3C/IGcPf+DqcrOB7O/ab9x4u/zjqraneT5N45+sIe87cxrCApXM8Bna7NYxwoTA==} engines: {node: '>= 16'} @@ -4061,10 +4276,16 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + trim-repeated@1.0.0: resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} engines: {node: '>=0.10.0'} + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-custom-error@3.3.1: resolution: {integrity: sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==} engines: {node: '>=14.0.0'} @@ -4133,6 +4354,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unique-filename@2.0.1: resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -4153,6 +4377,21 @@ packages: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -4207,6 +4446,12 @@ packages: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite@5.3.1: resolution: {integrity: sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4365,6 +4610,9 @@ packages: youtube-player@5.5.2: resolution: {integrity: sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@ampproject/remapping@2.3.0': @@ -5391,12 +5639,24 @@ snapshots: dependencies: '@babel/types': 7.24.7 + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.5 + '@types/estree@1.0.5': {} '@types/graceful-fs@4.1.9': dependencies: '@types/node': 20.14.6 + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/http-cache-semantics@4.0.4': {} '@types/istanbul-lib-coverage@2.0.6': {} @@ -5409,6 +5669,12 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/node@20.14.6': dependencies: undici-types: 5.26.5 @@ -5442,6 +5708,13 @@ snapshots: '@types/stylis@4.2.5': {} + '@types/trusted-types@2.0.7': + optional: true + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.32': @@ -5738,6 +6011,8 @@ snapshots: babel-plugin-jest-hoist: 28.1.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) + bail@2.0.2: {} + balanced-match@1.0.2: {} base64-arraybuffer@1.0.2: {} @@ -5873,6 +6148,8 @@ snapshots: caniuse-lite@1.0.30001636: {} + ccount@2.0.1: {} + cfb@1.2.2: dependencies: adler-32: 1.3.1 @@ -5893,6 +6170,14 @@ snapshots: char-regex@1.0.2: {} + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -5965,6 +6250,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} + commander@10.0.1: {} commander@11.1.0: {} @@ -6065,6 +6352,10 @@ snapshots: decamelize@1.2.0: {} + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -6104,12 +6395,18 @@ snapshots: delegates@1.0.0: {} + dequal@2.0.3: {} + detect-element-overflow@1.4.2: {} detect-libc@2.0.3: {} detect-newline@3.1.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + diff-sequences@28.1.1: {} dijkstrajs@1.0.3: {} @@ -6131,6 +6428,10 @@ snapshots: '@babel/runtime': 7.24.7 csstype: 3.1.3 + dompurify@3.3.1: + optionalDependencies: + '@types/trusted-types': 2.0.7 + dot-prop@6.0.1: dependencies: is-obj: 2.0.0 @@ -6405,6 +6706,8 @@ snapshots: estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + esutils@2.0.3: {} eventemitter3@2.0.3: {} @@ -6779,6 +7082,30 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.5 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -6793,6 +7120,8 @@ snapshots: html-escaper@2.0.2: {} + html-url-attributes@3.0.1: {} + html2canvas@1.4.1: dependencies: css-line-break: 2.1.0 @@ -6874,6 +7203,8 @@ snapshots: ini@4.1.3: {} + inline-style-parser@0.2.7: {} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 @@ -6885,6 +7216,13 @@ snapshots: jsbn: 1.1.0 sprintf-js: 1.1.3 + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-arguments@1.2.0: dependencies: call-bound: 1.0.4 @@ -6932,6 +7270,8 @@ snapshots: dependencies: has-tostringtag: 1.0.2 + is-decimal@2.0.1: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.0.2: @@ -6950,6 +7290,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + is-in-browser@1.1.3: {} is-installed-globally@0.4.0: @@ -6975,6 +7317,8 @@ snapshots: is-path-inside@3.0.3: {} + is-plain-obj@4.1.0: {} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -7513,6 +7857,8 @@ snapshots: lodash@4.17.21: {} + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -7593,6 +7939,95 @@ snapshots: math-intrinsics@1.1.0: {} + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mem@8.1.1: dependencies: map-age-cleaner: 0.1.3 @@ -7604,6 +8039,139 @@ snapshots: merge2@1.4.1: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.3.5 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.7: dependencies: braces: 3.0.3 @@ -7988,6 +8556,16 @@ snapshots: dependencies: callsites: 3.1.0 + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse-github-url@1.0.2: {} parse-json@5.2.0: @@ -8080,6 +8658,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + property-information@7.1.0: {} + proto-list@1.2.4: {} proxy-from-env@1.1.0: {} @@ -8263,6 +8843,24 @@ snapshots: dependencies: react: 18.3.1 + react-markdown@10.1.0(@types/react@18.3.3)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 18.3.3 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-oauth@0.0.1: {} react-onclickoutside@6.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -8470,6 +9068,23 @@ snapshots: dependencies: rc: 1.2.8 + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + remote-git-tags@3.0.0: {} require-directory@2.1.1: {} @@ -8670,6 +9285,8 @@ snapshots: source-map@0.6.1: {} + space-separated-tokens@2.0.2: {} + spawn-please@2.0.2: dependencies: cross-spawn: 7.0.3 @@ -8763,6 +9380,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -8785,6 +9407,14 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@emotion/is-prop-valid': 1.2.2 @@ -8864,10 +9494,14 @@ snapshots: tr46@0.0.3: {} + trim-lines@3.0.1: {} + trim-repeated@1.0.0: dependencies: escape-string-regexp: 1.0.5 + trough@2.2.0: {} + ts-custom-error@3.3.1: {} tslib@2.6.2: {} @@ -8943,6 +9577,16 @@ snapshots: undici-types@5.26.5: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + unique-filename@2.0.1: dependencies: unique-slug: 3.0.0 @@ -8963,6 +9607,29 @@ snapshots: dependencies: crypto-random-string: 4.0.0 + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universalify@2.0.1: {} untildify@4.0.0: {} @@ -9023,6 +9690,16 @@ snapshots: validate-npm-package-name@5.0.1: {} + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite@5.3.1(@types/node@20.14.6)(sass@1.77.6): dependencies: esbuild: 0.21.5 @@ -9204,3 +9881,5 @@ snapshots: sister: 3.0.2 transitivePeerDependencies: - supports-color + + zwitch@2.0.4: {} diff --git a/src/App.jsx b/src/App.jsx index 4684d98b..7fae46d7 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -16,6 +16,10 @@ import FullBlog from "./pages/Blog/FullBlog"; // state import AuthContext from "./context/AuthContext"; import EventStats from "./features/Modals/Event/EventStats/EventStats"; + +// Chatbot +import Chatbot from "./components/Chatbot/Chatbot"; + import { EventsView, NewForm, @@ -97,8 +101,12 @@ const AuthLayout = () => ( function App() { const authCtx = useContext(AuthContext); console.log(authCtx.user.access); + return (
+ {/* Global Chatbot Component */} + + }> }> @@ -138,8 +146,8 @@ function App() { {(authCtx.user.access === "ADMIN" || authCtx.user.access === "SENIOR_EXECUTIVE_CREATIVE") && ( - } /> - )} + } /> + )} {/* Certificates Route */} {authCtx.user.access === "ADMIN" && ( diff --git a/src/components/Chatbot/Chatbot.jsx b/src/components/Chatbot/Chatbot.jsx new file mode 100644 index 00000000..daf2cb8a --- /dev/null +++ b/src/components/Chatbot/Chatbot.jsx @@ -0,0 +1,525 @@ +/** + * @fileoverview FED Chatbot Component + * @module components/Chatbot + * @description AI-powered chatbot for FED KIIT website with seamless navigation + */ + +import { useState, useRef, useEffect, useContext } from 'react'; +import { useNavigate, useLocation } from 'react-router-dom'; +import ReactMarkdown from 'react-markdown'; +import DOMPurify from 'dompurify'; +import styles from './Chatbot.module.scss'; +import { IoCloseOutline, IoSend, IoMic, IoMicOff } from 'react-icons/io5'; +import { BiSolidMessageSquareDetail } from 'react-icons/bi'; +import { IoSparkles } from 'react-icons/io5'; +import { FiLogIn } from 'react-icons/fi'; +import { chatbotService } from '../../services/chatbot'; +import AuthContext from '../../context/AuthContext'; +import FedLogo from '../../assets/images/FedLogo.png'; + +const Chatbot = () => { + const chatbotName = import.meta.env.VITE_CHATBOT_NAME || 'AskFED'; + const navigate = useNavigate(); + const location = useLocation(); + const authCtx = useContext(AuthContext); + + // Get user's first name for personalized greeting + const userName = authCtx.isLoggedIn ? authCtx.user?.name?.split(' ')[0] : null; + + // Generate personalized greeting message + const getGreetingMessage = () => { + if (userName) { + return `Hi **${userName}**! I'm **${chatbotName}**, your personal assistant for FED KIIT. 🚀 I'm here to help you with anything related to FED!`; + } + return `Hello! I'm **${chatbotName}**, your personal assistant for FED KIIT. 🚀 I'm here to help you with anything related to FED!`; + }; + + const [messages, setMessages] = useState([ + { + id: 1, + text: getGreetingMessage(), + isUser: false, + timestamp: new Date(), + } + ]); + const [userInput, setUserInput] = useState(''); + const [isTyping, setIsTyping] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [isListening, setIsListening] = useState(false); + const [showAuthPrompt, setShowAuthPrompt] = useState(false); + const [conversationHistory, setConversationHistory] = useState([]); // Track conversation for context + const [isWaitingForEmailContent, setIsWaitingForEmailContent] = useState(false); // Email flow state + const messagesEndRef = useRef(null); + const chatboxRef = useRef(null); + const recognitionRef = useRef(null); + + // Update greeting when user logs in/out + useEffect(() => { + setMessages(prev => { + if (prev.length === 1 && !prev[0].isUser) { + return [{ + ...prev[0], + text: getGreetingMessage() + }]; + } + return prev; + }); + }, [authCtx.isLoggedIn, userName]); + + // Suggested prompts + const suggestedPrompts = [ + "What is FED?", + "Who is the president?", + "Tell me about FED events", + "Show me FED blogs" + ]; + + // Navigation patterns - FIXED: /Blog not /Blogs + const NAVIGATION_PATTERNS = { + '[NAV:/Team]': '/Team', + '[NAV:/Events]': '/Events', + '[NAV:/Blog]': '/Blog', + '[NAV:/Blogs]': '/Blog', // Handle both for backwards compatibility + '[NAV:/pastEvents]': '/pastEvents', + }; + + // Auto-scroll to bottom + const scrollToBottom = () => { + if (chatboxRef.current) { + chatboxRef.current.scrollTop = chatboxRef.current.scrollHeight; + } + }; + + useEffect(() => { + scrollToBottom(); + }, [messages, isTyping, isOpen]); // Added isOpen to scroll to bottom when chatbot opens + + // Toggle chatbot + const toggleChatbot = () => { + setIsOpen(!isOpen); + }; + + /** + * Process navigation hints from AI response + * Navigates the page seamlessly without closing chatbot + */ + const processNavigation = (responseText) => { + let cleanedText = responseText; + let navigationPath = null; + + // Check for navigation patterns + for (const [pattern, path] of Object.entries(NAVIGATION_PATTERNS)) { + if (responseText.includes(pattern)) { + cleanedText = responseText.replace(pattern, '').trim(); + navigationPath = path; + break; + } + } + + // Perform navigation if path found and not already on that page + if (navigationPath && location.pathname !== navigationPath) { + setTimeout(() => { + navigate(navigationPath); + }, 500); + } + + return cleanedText; + }; + + // Handle login button click - FIXED: Minimize chatbot when clicking sign in + const handleLoginClick = () => { + sessionStorage.setItem('prevPage', window.location.pathname); + setIsOpen(false); // Minimize chatbot + navigate('/login'); + }; + + // Build conversation history for context + const buildConversationHistory = () => { + // Get last 6 messages (3 exchanges) for context + const recentMessages = messages.slice(-6); + return recentMessages.map(msg => ({ + role: msg.isUser ? 'user' : 'model', + content: msg.text + })); + }; + + // Send message handler + const sendMessage = async (messageText = null) => { + const textToSend = messageText || userInput; + if (!textToSend?.trim()) return; + + // Add user message + const userMessage = { + id: messages.length + 1, + text: textToSend, + isUser: true, + timestamp: new Date(), + }; + + setMessages(prev => [...prev, userMessage]); + setUserInput(''); + setIsTyping(true); + setShowAuthPrompt(false); + + try { + // Check if we're waiting for email content + if (isWaitingForEmailContent) { + // Send the email with user's content UNCHANGED + const emailResult = await chatbotService.sendEmail( + textToSend, // Send exactly what user typed - no modifications + userName || 'Anonymous', + authCtx.user?.email + ); + + setIsWaitingForEmailContent(false); + + const emailResponse = { + id: messages.length + 2, + text: emailResult.success + ? '✅ Your message has been sent to FED! The team will get back to you soon. 📧' + : '❌ Sorry, there was an error sending your email. Please try again later.', + isUser: false, + timestamp: new Date(), + }; + setMessages(prev => [...prev, emailResponse]); + setIsTyping(false); + return; + } + + // Check if user wants to send an email + const emailIntentKeywords = ['send email', 'send mail', 'email fed', 'contact fed', 'message fed', 'reach out', 'send a message']; + const lowerText = textToSend.toLowerCase(); + const wantsToSendEmail = emailIntentKeywords.some(keyword => lowerText.includes(keyword)); + + if (wantsToSendEmail) { + setIsWaitingForEmailContent(true); + const promptMessage = { + id: messages.length + 2, + text: '📧 Sure! Please type your message in the next chat. I will send it exactly as you write it to fedkiit@gmail.com.\n\n**Note:** Your message will be sent exactly as you type it - no changes will be made.', + isUser: false, + timestamp: new Date(), + }; + setMessages(prev => [...prev, promptMessage]); + setIsTyping(false); + return; + } + + // Normal chatbot flow - Build conversation history for context + const history = buildConversationHistory(); + + // Call backend chatbot API with conversation history + const response = await chatbotService.sendMessage(textToSend, history); + + // Handle auth required response + if (response.requiresAuth) { + setShowAuthPrompt(true); + const authMessage = { + id: messages.length + 2, + text: response.message || '🔐 Please sign in to access this feature.', + isUser: false, + timestamp: new Date(), + isAuthPrompt: true + }; + setMessages(prev => [...prev, authMessage]); + } else { + // Get the raw response + let rawResponse = response.success ? response.response : 'Sorry, I encountered an error. Please try again.'; + + // Check for EMAIL_TRIGGER tag in AI response + const emailTriggerPattern = /\[EMAIL_TRIGGER\]/gi; + const hasEmailTrigger = emailTriggerPattern.test(rawResponse); + + if (hasEmailTrigger) { + // Remove the trigger tag from displayed message + rawResponse = rawResponse.replace(/\[EMAIL_TRIGGER\]/gi, '').trim(); + // Set email mode - next user message goes directly to email + setIsWaitingForEmailContent(true); + console.log('[Chatbot] Email trigger detected - next message will be sent as email'); + } + + // Process navigation hints and clean the response + const cleanedResponse = processNavigation(rawResponse); + + const botResponse = { + id: messages.length + 2, + text: cleanedResponse, + isUser: false, + timestamp: new Date(), + }; + setMessages(prev => [...prev, botResponse]); + } + } catch (error) { + console.error('Error sending message:', error); + setIsWaitingForEmailContent(false); // Reset email state on error + const errorResponse = { + id: messages.length + 2, + text: 'Sorry, I\'m having trouble connecting. Please try again later.', + isUser: false, + timestamp: new Date(), + }; + setMessages(prev => [...prev, errorResponse]); + } finally { + setIsTyping(false); + } + }; + + // Handle Enter key + const handleKeyPress = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }; + + // Voice recognition + const startVoiceRecognition = () => { + if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) { + alert('Voice recognition is not supported in your browser. Please use Chrome or Edge.'); + return; + } + + const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; + const recognition = new SpeechRecognition(); + + recognition.continuous = false; + recognition.interimResults = false; + recognition.lang = 'en-US'; + + recognition.onstart = () => { + setIsListening(true); + }; + + recognition.onresult = (event) => { + const transcript = event.results[0][0].transcript; + setUserInput(transcript); + }; + + recognition.onerror = (event) => { + console.error('Speech recognition error:', event.error); + setIsListening(false); + }; + + recognition.onend = () => { + setIsListening(false); + }; + + recognitionRef.current = recognition; + recognition.start(); + }; + + const stopVoiceRecognition = () => { + if (recognitionRef.current) { + recognitionRef.current.stop(); + setIsListening(false); + } + }; + + const toggleVoiceRecognition = () => { + if (isListening) { + stopVoiceRecognition(); + } else { + startVoiceRecognition(); + } + }; + + // Clean and sanitize message text - remove broken HTML from AI + const cleanMessage = (text) => { + // Remove any broken HTML the AI might have generated + let cleanText = text + // Remove HTML anchor tags completely + .replace(/]*>/gi, '') + .replace(/<\/a>/gi, '') + // Remove broken HTML attribute fragments + .replace(/"\s*target="_blank"\s*rel="noopener\s*noreferrer"\s*style="[^"]*">/gi, '') + .replace(/"\s*target="_blank"\s*rel="noopener\s*noreferrer">/gi, '') + .replace(/"\s*target="_blank">/gi, '') + .replace(/style="[^"]*">/gi, '') + .replace(/rel="[^"]*">/gi, '') + .replace(/"\s*>/g, '') + // Remove AI-generated mailto markdown links: [email](mailto:email) -> just email + .replace(/\[([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})\]\(mailto:[^)]+\)/gi, '$1') + // Remove AI-generated Gmail compose links: [email](https://mail.google.com/...) -> just email + .replace(/\[([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})\]\(https:\/\/mail\.google\.com[^)]+\)/gi, '$1') + // Convert standalone @fedkiit to Instagram link (NOT inside URLs like medium.com/@fedkiit) + .replace(/(? { + return ( + + {children} + + ); + }; + + return ( + <> + {/* Toggle Button */} + {!isOpen && ( + + )} + + {/* Backdrop Overlay */} + {isOpen && ( +
+ )} + + {/* Chatbot Container */} + {isOpen && ( +
+ {/* Header - FIXED: Removed username from here */} +
+
+
+ FED Logo +
+
+
+

{chatbotName}

+

+ AI Assistant +

+
+
+ +
+ + {/* Messages Area */} +
+ {messages.map((message) => ( +
+ {!message.isUser && ( +
+ Bot +
+ )} +
+
+ {children} + }} + > + {cleanMessage(message.text)} + + {/* Show login button for auth prompts */} + {message.isAuthPrompt && !authCtx.isLoggedIn && ( + + )} +
+
+
+ ))} + + {/* Typing Indicator */} + {isTyping && ( +
+
+ Bot +
+
+ + + +
+
+ )} + + {/* Show suggested prompts only for first message */} + {messages.length === 1 && !isTyping && ( +
+

Quick actions:

+
+ {suggestedPrompts.map((prompt, index) => ( + + ))} +
+
+ )} + +
+
+ + {/* Input Area */} +
+ setUserInput(e.target.value)} + onKeyPress={handleKeyPress} + placeholder={authCtx.isLoggedIn ? "Ask about your events, certificates..." : "Ask me anything about FED..."} + className={styles.messageInput} + /> + + +
+
+ )} + + ); +}; + +export default Chatbot; diff --git a/src/components/Chatbot/Chatbot.module.scss b/src/components/Chatbot/Chatbot.module.scss new file mode 100644 index 00000000..a78f8408 --- /dev/null +++ b/src/components/Chatbot/Chatbot.module.scss @@ -0,0 +1,700 @@ +/** + * FED Chatbot Styles + * Self-contained SCSS module for the chatbot component + */ + +// ============================================ +// VARIABLES (Self-contained) +// ============================================ + +// Colors +$page-color: #1C1C1C; +$dark-bg: #141414; +$light-text: #ffffff; +$border-subtle: rgba(255, 255, 255, 0.1); +$glass-bg: rgba(28, 28, 28, 0.95); +$glass-blur: 10px; + +// Gradients +$primary-gradient: linear-gradient(260deg, rgba(255, 190, 11, 0.84) -29.7%, rgba(244, 43, 3, 0.84) 128.34%); + +// Spacing +$spacing-sm: 8px; +$spacing-md: 16px; +$spacing-lg: 20px; +$spacing-xl: 24px; + +// Transitions +$transition-fast: 0.2s ease; + +// Z-indexes (lower than modals which use 50-1000) +$z-toggle: 40; +$z-container: 45; + +// Breakpoints +$desktop: 1024px; +$tablet: 768px; +$mobile: 480px; + +// Sizes +$toggle-button-size: 72px; +$border-radius-medium: 16px; + +// ============================================ +// ANIMATIONS +// ============================================ + +@keyframes bounce { + + 0%, + 100% { + transform: translateY(0); + } + + 50% { + transform: translateY(-8px); + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes pulse { + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.5; + } +} + +@keyframes typing { + + 0%, + 100% { + transform: translateY(0); + opacity: 0.4; + } + + 50% { + transform: translateY(-4px); + opacity: 1; + } +} + +// ============================================ +// TOGGLE BUTTON +// ============================================ + +.chatbotToggle { + position: fixed; + bottom: $spacing-xl; + right: $spacing-xl; + width: $toggle-button-size; + height: $toggle-button-size; + border-radius: 50%; + background: $primary-gradient; + border: none; + color: $light-text; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + box-shadow: 0 8px 24px rgba(244, 43, 3, 0.4); + z-index: $z-toggle; + transition: all $transition-fast; + animation: bounce 2s infinite; + backdrop-filter: blur($glass-blur); + + &:hover { + transform: scale(1.1); + animation: none; + box-shadow: 0 12px 32px rgba(244, 43, 3, 0.5); + } + + &:active { + transform: scale(0.95); + } +} + +.pulseRing { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + height: 100%; + border-radius: 50%; + border: 3px solid rgba(255, 190, 11, 0.6); + animation: pulseRing 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +@keyframes pulseRing { + 0% { + transform: translate(-50%, -50%) scale(1); + opacity: 1; + } + + 100% { + transform: translate(-50%, -50%) scale(1.5); + opacity: 0; + } +} + +// ============================================ +// BACKDROP +// ============================================ + +.backdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.6); + z-index: 42; + animation: fadeIn 0.3s ease; +} + +// ============================================ +// MAIN CONTAINER +// ============================================ + +.chatbotContainer { + position: fixed; + bottom: 20px; + right: 20px; + width: min(400px, calc(100vw - 40px)); + height: calc(100vh - 120px); + background-color: $glass-bg; + border-radius: 24px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); + display: flex; + flex-direction: column; + z-index: $z-container; + border: 1px solid rgba(255, 255, 255, 0.1); + backdrop-filter: blur($glass-blur); + animation: slideInUp 0.3s cubic-bezier(0.4, 0, 0.2, 1); + overflow: hidden; +} + +@keyframes slideInUp { + from { + transform: translateY(20px); + opacity: 0; + } + + to { + transform: translateY(0); + opacity: 1; + } +} + +// ============================================ +// HEADER +// ============================================ + +.chatbotHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px $spacing-sm; + background: linear-gradient(135deg, rgba(28, 28, 28, 0.95) 0%, rgba(20, 20, 20, 0.95) 100%); + border-radius: 24px 24px 0 0; + border-bottom: 1px solid $border-subtle; + min-height: 42px; + backdrop-filter: blur(12px); +} + +.headerContent { + display: flex; + align-items: center; + gap: $spacing-sm; +} + +.avatarContainer { + position: relative; +} + +.avatar { + height: 42px; + width: 42px; + border-radius: 50%; +} + +.statusIndicator { + position: absolute; + bottom: 2px; + right: 2px; + width: 12px; + height: 12px; + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + border-radius: 50%; + border: 2px solid $dark-bg; + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +.headerText { + display: flex; + flex-direction: column; + gap: 2px; +} + +.title { + color: $light-text; + font-family: 'Open Sans', sans-serif; + font-size: 1.25rem; + font-weight: 600; + margin: 0; + letter-spacing: 0.3px; +} + +.subtitle { + display: flex; + align-items: center; + gap: 4px; + color: rgba(255, 255, 255, 0.6); + font-size: 0.8rem; + margin: 0; +} + +.userBadge { + color: rgba(255, 190, 11, 0.9); + font-weight: 500; +} + +.closeButton { + background: none; + border: none; + color: rgba(255, 255, 255, 0.7); + cursor: pointer; + padding: 8px; + border-radius: 8px; + transition: all $transition-fast; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.95); + } + + &:active { + transform: scale(0.95); + } +} + +// ============================================ +// MESSAGES AREA +// ============================================ + +.messagesArea { + flex: 1; + padding: $spacing-lg; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + gap: $spacing-md; + scrollbar-width: thin; + scrollbar-color: rgba(255, 255, 255, 0.15) rgba(255, 255, 255, 0.03); + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.03); + border-radius: 10px; + } + + &::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.15); + border-radius: 10px; + + &:hover { + background: rgba(255, 255, 255, 0.25); + } + } +} + +// ============================================ +// MESSAGE BUBBLES +// ============================================ + +.messageWrapper { + display: flex; + gap: 10px; + animation: messageSlide 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +@keyframes messageSlide { + from { + transform: translateY(10px); + opacity: 0; + } + + to { + transform: translateY(0); + opacity: 1; + } +} + +.userWrapper { + justify-content: flex-end; +} + +.botWrapper { + justify-content: flex-start; +} + +.messageAvatar { + flex-shrink: 0; + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + + img { + width: 100%; + height: 100%; + object-fit: contain; + } +} + +.messageContent { + display: flex; + flex-direction: column; + gap: 4px; + max-width: 75%; +} + +.message { + padding: 12px 16px; + border-radius: 16px; + word-wrap: break-word; + font-size: 0.9rem; + line-height: 1.5; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + cursor: default; + + &:hover { + transform: translateY(-2px) scale(1.01); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); + } +} + +.userMessage { + background: linear-gradient(135deg, #f0f0f0 0%, #e8e8e8 100%); + color: #1a1a1a; + border-bottom-right-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + + &:hover { + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); + } +} + +.botMessage { + background: $primary-gradient; + color: $light-text; + border-bottom-left-radius: 4px; + box-shadow: 0 2px 8px rgba(244, 43, 3, 0.2); + + &:hover { + box-shadow: 0 8px 24px rgba(255, 140, 40, 0.35); + } + + // Style bullet lists inside bot messages + ul { + margin: 8px 0; + padding-left: 20px; + list-style-position: outside; + } + + li { + margin: 6px 0; + padding-left: 4px; + line-height: 1.5; + + &::marker { + color: $light-text; + } + } +} + +.loginButton { + display: flex; // Changed from inline-flex to make button on separate line + width: fit-content; + align-items: center; + gap: 8px; + margin-top: 12px; + padding: 10px 18px; + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 10px; + color: $light-text; + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + transition: all $transition-fast; + + &:hover { + background: rgba(255, 255, 255, 0.25); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + } + + &:active { + transform: translateY(0); + } +} + +// ============================================ +// TYPING INDICATOR +// ============================================ + +.typingIndicator { + display: flex; + align-items: center; + gap: 6px; + padding: 12px 16px; + background: $primary-gradient; + border-radius: 16px; + border-bottom-left-radius: 4px; + max-width: 70px; + box-shadow: 0 2px 8px rgba(244, 43, 3, 0.2); + + span { + width: 8px; + height: 8px; + background-color: $light-text; + border-radius: 50%; + animation: typing 1.4s infinite ease-in-out; + + &:nth-child(2) { + animation-delay: 0.2s; + } + + &:nth-child(3) { + animation-delay: 0.4s; + } + } +} + +// ============================================ +// SUGGESTED PROMPTS +// ============================================ + +.suggestedPrompts { + margin-top: $spacing-md; + animation: fadeIn 0.5s ease-in-out; +} + +.promptsLabel { + margin: 0 0 $spacing-sm 0; + font-size: 0.75rem; + color: rgba(255, 255, 255, 0.5); + font-weight: 500; + letter-spacing: 0.5px; +} + +.promptsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 8px; +} + +.promptButton { + padding: 10px 14px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + color: rgba(255, 255, 255, 0.8); + font-size: 0.8rem; + cursor: pointer; + transition: all $transition-fast; + text-align: left; + + &:hover { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 190, 11, 0.5); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(255, 190, 11, 0.2); + } + + &:active { + transform: translateY(0); + } +} + +// ============================================ +// INPUT AREA +// ============================================ + +.inputArea { + display: flex; + align-items: center; // Vertically align input and buttons + padding: 10px $spacing-md; // Reduced vertical padding + background: linear-gradient(135deg, rgba(28, 28, 28, 0.95) 0%, rgba(20, 20, 20, 0.95) 100%); + border-radius: 0 0 24px 24px; + border-top: 1px solid $border-subtle; + gap: $spacing-sm; + backdrop-filter: blur(12px); +} + +.messageInput { + flex: 1; + height: 44px; // Match mic and send button height + padding: 0 18px; + border: 1px solid $border-subtle; + border-radius: 14px; + background: rgba(255, 255, 255, 0.08); + color: $light-text; + font-size: 0.9rem; + font-family: 'Open Sans', sans-serif; + transition: all $transition-fast; + + &::placeholder { + color: rgba(255, 255, 255, 0.4); + } + + &:focus { + outline: none; + background: rgba(255, 255, 255, 0.12); + border-color: rgba(255, 190, 11, 0.5); + box-shadow: 0 0 0 3px rgba(255, 190, 11, 0.1); + } +} + +.voiceButton { + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 12px; + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: $light-text; + transition: all $transition-fast; + + &:hover { + background: rgba(255, 255, 255, 0.15); + transform: translateY(-2px); + } + + &:active { + transform: translateY(0); + } + + &.listening { + background: $primary-gradient; + border-color: transparent; + animation: pulse 1.5s ease-in-out infinite; + box-shadow: 0 0 20px rgba(255, 140, 40, 0.5); + } +} + +.sendButton { + background: $primary-gradient; + border: none; + border-radius: 12px; + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: $light-text; + transition: all $transition-fast; + box-shadow: 0 4px 12px rgba(244, 43, 3, 0.3); + + &:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(244, 43, 3, 0.4); + } + + &:active:not(:disabled) { + transform: translateY(0); + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } +} + +// ============================================ +// RESPONSIVE DESIGN +// ============================================ + +@media (max-width: $desktop) { + .chatbotContainer { + height: 65vh; + } +} + +@media (max-width: $tablet) { + .chatbotContainer { + width: calc(100% - 40px); + height: 70vh; + } + + .promptsGrid { + grid-template-columns: 1fr; + } +} + +@media (max-width: $mobile) { + .chatbotContainer { + width: calc(100% - 20px); + height: 80vh; + bottom: $spacing-sm; + right: $spacing-sm; + border-radius: $border-radius-medium; + } + + .chatbotToggle { + bottom: $spacing-md; + right: $spacing-md; + width: 64px; + height: 64px; + } + + .message { + font-size: 0.85rem; + } + + .messageContent { + max-width: 82%; + } + + .chatbotHeader { + padding: $spacing-sm $spacing-md; + min-height: 70px; + } + + .avatar { + height: 40px; + width: 40px; + } + + .title { + font-size: 1.1rem; + } +} \ No newline at end of file diff --git a/src/components/Chatbot/index.jsx b/src/components/Chatbot/index.jsx new file mode 100644 index 00000000..d2526960 --- /dev/null +++ b/src/components/Chatbot/index.jsx @@ -0,0 +1,6 @@ +/** + * @fileoverview Chatbot Component Exports + * @module components/Chatbot + */ + +export { default } from './Chatbot'; diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx index db3084a7..33a4532a 100644 --- a/src/context/AuthContext.jsx +++ b/src/context/AuthContext.jsx @@ -14,7 +14,7 @@ const AuthContext = React.createContext({ college: "", contactNo: "", year: "", - extra:{ + extra: { github: "", linkedin: "", designation: "", @@ -27,10 +27,10 @@ const AuthContext = React.createContext({ }, target: null, isAdmin: false, - login: async (token) => {}, - logout: () => {}, - settarget: () => {}, - update: () => {}, + login: async (token) => { }, + logout: () => { }, + settarget: () => { }, + update: () => { }, eventData: null, memberData: null, croppedImageFile: null, @@ -86,7 +86,19 @@ export const AuthContextProvider = (props) => { setTarget(t); }; - const logoutHandler = useCallback(() => { + const logoutHandler = useCallback(async () => { + // Call backend to clear auth cookie + try { + const baseURL = import.meta.env.VITE_API_URL || "http://localhost:5000"; + await fetch(`${baseURL}/api/auth/logout`, { + method: 'POST', + credentials: 'include' // Important: send cookies with request + }); + } catch (error) { + console.error('Logout API error:', error); + } + + // Clear local state setToken(null); setUserIsLoggedIn(false); localStorage.removeItem("token"); @@ -127,9 +139,9 @@ export const AuthContextProvider = (props) => { college: college, contactNo: contactNo, year: year, - extra:{ + extra: { github: github, - linkedin:linkedin, + linkedin: linkedin, designation: designation, }, access: access, @@ -181,9 +193,9 @@ export const AuthContextProvider = (props) => { college: college, contactNo: contactNo, year: year, - extra:{ + extra: { github: github, - linkedin:linkedin, + linkedin: linkedin, designation: designation, }, access: access, diff --git a/src/features/ChatBot/ChatBot.jsx b/src/features/ChatBot/ChatBot.jsx deleted file mode 100644 index 03f67726..00000000 --- a/src/features/ChatBot/ChatBot.jsx +++ /dev/null @@ -1,395 +0,0 @@ -import { useRef, useState, useEffect } from "react"; -import { useLocation } from "react-router-dom"; -import styles from "./styles/ChatBot.module.scss"; -import { BsSend, BsFillMicFill, BsFillMicMuteFill } from "react-icons/bs"; -import { FaVolumeUp, FaVolumeMute } from "react-icons/fa"; -import { IoCloseOutline, IoCopyOutline } from "react-icons/io5"; -import { BiSolidMessageSquareDetail } from "react-icons/bi"; -import { apiBot } from "../../services"; -import { Alert } from "../../microInteraction"; - -export default function ChatBot() { - const name = "FedRick"; - const [messages, setMessages] = useState([]); - const [userInput, setUserInput] = useState(""); - const [isThinking, setIsThinking] = useState(false); - const [isTyping, setIsTyping] = useState(false); - const [initialMsg, setInitialMsg] = useState( - `Hello! I am ${name}, your personal assistant for FED. How can I help you today?` - ); - const [isActive, setIsActive] = useState(false); - const [isVisible, setIsVisible] = useState(false); - const [isEventOngoing, setIsEventOngoing] = useState(true); - const chatboxRef = useRef(null); - const location = useLocation(); - const isPixel_AI_Hack = location.pathname.includes("/Pixel_AI_Hack"); - const [alert, setAlert] = useState(null); - const [isSpeaking, setIsSpeaking] = useState(false); - - const [isRecording, setIsRecording] = useState(false); - const [speechRecognition, setSpeechRecognition] = useState(null); - const [lastSpeechTime, setLastSpeechTime] = useState(Date.now()); - const speechRecognitionDelay = 2000; - - //Setting Alerts - useEffect(() => { - if (alert) { - const { type, message, position, duration } = alert; - Alert({ type, message, position, duration }); - setAlert(null); // Reset alert after displaying it - } - }, [alert]); - - // Setting the chatbot Connection with server - useEffect(() => { - const fetchInitialMessage = async () => { - setTimeout(async () => { - try { - const response = await apiBot.post("/chat", { - input: "Connection Request", - }); - - if (response.status === 200 || response.status === 201) { - // console.log( - // "Successfully connected to chatbot", - // response.data.message - // ); - } else { - // console.log("Error:", response.data.message); - } - } catch (error) { - console.error("Error:", error); - } - setIsThinking(false); - scrollToBottom(); - }, 1500); - }; - - fetchInitialMessage(); - }, []); - - // Typing effect for bot messages - const typeMessage = async (message) => { - let typedMessage = ""; - setIsTyping(true); // Start typing - for (let i = 0; i < message.length; i++) { - typedMessage += message[i]; - setMessages((prevMessages) => [ - ...prevMessages.slice(0, -1), - { bot: typedMessage, isTyping: true }, - ]); - scrollToBottom(); - await new Promise((resolve) => setTimeout(resolve, 20)); - setIsThinking(false); - } - setIsTyping(false); // Typing completed - setMessages((prevMessages) => [ - ...prevMessages.slice(0, -1), - { bot: typedMessage, isTyping: false }, // Mark the message as fully typed - ]); - scrollToBottom(); - - if (isSpeaking) { - speakMessage(typedMessage); - } - }; - - // Sending user message to the server - const sendMessage = async () => { - if (!userInput) return; - - if (isSpeaking) { - window.speechSynthesis.cancel(); - setIsSpeaking(false); - } - - const userMessage = { user: userInput }; - setMessages((prevMessages) => [...prevMessages, userMessage]); - setUserInput(""); - - setTimeout(() => { - setIsThinking(true); - }, 600); - - setTimeout(async () => { - try { - const response = await apiBot.post("/chat", { input: userInput }); - - if (response.status === 200 || response.status === 201) { - setMessages((prevMessages) => [ - ...prevMessages, - { bot: "", isTyping: true }, - ]); - await typeMessage(response.data.response); - } else { - setMessages((prevMessages) => [ - ...prevMessages, - { bot: "Error: Unable to connect to the server.", isTyping: false }, - ]); - // console.log("Error:", response.data.message); - } - } catch (error) { - setMessages((prevMessages) => [ - ...prevMessages, - { bot: "Error: Unable to connect to the server.", isTyping: false }, - ]); - console.error("Error:", error); - } - - scrollToBottom(); - }, 4000); // Total delay considering thinking and typing phases - }; - - // Handle Enter key press - const onHandleKey = (e) => { - if (e.key === "Enter") { - sendMessage(); - } - }; - - const scrollToBottom = () => { - if (chatboxRef.current) { - chatboxRef.current.scrollTop = chatboxRef.current.scrollHeight; - } - }; - - useEffect(() => { - if (isEventOngoing) { - if (isVisible) { - document.body.classList.add(styles.lockScroll); - } else { - document.body.classList.remove(styles.lockScroll); - } - } - - return () => { - document.body.classList.remove(styles.lockScroll); - }; - }, [isVisible, isEventOngoing]); - - const closePopup = () => { - setIsVisible(false); - }; - - useEffect(() => { - scrollToBottom(); - }, [messages]); - - const handleClick = () => { - setIsActive(!isActive); - setIsVisible(!isVisible); - }; - - const botToggle = isActive ? styles.hidden : ""; - const chatBotOpen = isActive ? "" : styles.hidden; - - // Copy text to clipboard - const copyToClipboard = (text) => { - navigator.clipboard - .writeText(text) - .then(() => { - setAlert({ - type: "success", - message: "Copied to clipboard!", - position: "bottom-left", - duration: 2000, - }); - }) - .catch((err) => { - setAlert({ - type: "error", - message: "Failed to copy to clipboard!", - position: "bottom-left", - duration: 2000, - }); - }); - }; - - // Speak message - const speakMessage = (text) => { - if (isSpeaking) { - window.speechSynthesis.cancel(); // Stop current speech - setIsSpeaking(false); // Toggle to non-speaking state - } else { - const utterance = new SpeechSynthesisUtterance(text); - utterance.onend = () => { - setIsSpeaking(false); // Reset speaking state after speech ends - }; - window.speechSynthesis.speak(utterance); - setIsSpeaking(true); // Set to speaking state - } - }; - - // Speech recognition - useEffect(() => { - // console.log("Speech recognition effect"); - if ("webkitSpeechRecognition" in window) { - const recognition = new window.webkitSpeechRecognition(); - recognition.lang = "en-US"; - recognition.interimResults = false; - recognition.onresult = (event) => { - const transcript = event.results[0][0].transcript; - setUserInput(transcript); - setLastSpeechTime(Date.now()); // Reset the timer when a result is received - }; - recognition.onerror = (event) => { - console.error("Speech recognition error", event); - setIsRecording(false); - }; - setSpeechRecognition(recognition); - } else { - console.warn("Speech recognition not supported"); - } - }, [isRecording]); - - // Handle Mic Button Click - const startRecording = () => { - if (speechRecognition) { - speechRecognition.start(); - setIsRecording(true); - } - }; - - const stopRecording = () => { - if (speechRecognition) { - speechRecognition.stop(); - setIsRecording(false); - } - }; - - // Auto Send msges after a delay - useEffect(() => { - const handleSendMessage = () => { - if (Date.now() - lastSpeechTime >= speechRecognitionDelay) { - sendMessage(); - } - }; - - const timer = setTimeout(handleSendMessage, speechRecognitionDelay); - setIsRecording(false); - return () => clearTimeout(timer); - }, [lastSpeechTime]); - - return ( - <> - - -
-
-
-
- logo -

{name}

-
- - - -
-
-
-
-
- {initialMsg} -
- {messages.map((message, index) => ( -
- {message.user || message.bot} - {!message.user && - !message.isTyping && ( // Show icons only for fully typed bot messages -
- - -
- )} -
- ))} - -
- {isRecording && ( -
- {name} is listening... -
- )} - {isThinking && ( -
- {name} is thinking... -
- )} -
-
-
- -
- setUserInput(e.target.value)} - placeholder="Ask something..." - onKeyDown={onHandleKey} - /> - - -
-
-
- - - ); -} diff --git a/src/features/ChatBot/index.jsx b/src/features/ChatBot/index.jsx deleted file mode 100644 index b28b5e8f..00000000 --- a/src/features/ChatBot/index.jsx +++ /dev/null @@ -1 +0,0 @@ -export { default as ChatBot } from './ChatBot'; \ No newline at end of file diff --git a/src/features/ChatBot/styles/ChatBot.module.scss b/src/features/ChatBot/styles/ChatBot.module.scss deleted file mode 100644 index 311387a9..00000000 --- a/src/features/ChatBot/styles/ChatBot.module.scss +++ /dev/null @@ -1,443 +0,0 @@ -@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"); - -@keyframes bounce { - 0%, - 100% { - transform: translateY(-25%); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: translateY(0); - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } -} - -.popup { - z-index: 50; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - justify-content: center; - align-items: center; - background-color: rgba(0, 0, 0, 0.6); - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out; -} - -.fadeIn { - opacity: 1; - visibility: visible; - transition: opacity 1s ease-in; -} - -.chatbottoggle { - position: fixed; - bottom: 30px; - right: 35px; - outline: none; - border: none; - height: 70px; - width: 70px; - z-index: 50; - display: flex; - cursor: pointer; - align-items: center; - justify-content: center; - border-radius: 50%; - background: var(--primary); - box-shadow: 0 3px 4px 2px #06060690; - transition: all 0.2s ease; - animation: fade-in 2s; - backdrop-filter: blur(20px); - animation: bounce 1s infinite; -} - -.chatbottoggle:hover { - transition: 0.2s linear; - background: linear-gradient( - 260deg, - rgba(255, 190, 11, 0.9) -29.7%, - rgba(244, 43, 3, 0.9) 128.34% - ); -} - -.chatbottoggle span { - margin-top: 5px; - font-size: 3rem; - color: rgb(255, 255, 255); - z-index: 50; -} - -.hidden { - display: none; -} - -.chatcontainer { - width: 340px; - height: fit-content; - margin: 0; - position: fixed; - z-index: 50; - border: 1px solid #6e6c6a; - border-radius: 25px; - font-family: "Poppins", sans-serif; - margin: 0 20px; - animation: fade-in 1s; - backdrop-filter: blur(20px); -} - -header { - height: 4rem; - display: flex; - align-items: center; - justify-content: space-between; - background-color: #1c1c1c98; - border-radius: 25px 25px 0px 0px; -} - -.logo { - display: flex; - align-items: center; - justify-content: center; - margin-left: 15px; -} - -.logo img { - height: 2.35rem; - margin-right: 0.5rem; -} - -.Pixel_AI_HackBackGround{ - transition: .2s linear; - background: linear-gradient( to left, - rgba(17, 44, 60, 0.8), - rgba(226, 8, 76, 0.8), - rgba(255, 211, 137, 0.8), - rgba(180, 223, 255, 0.8) - ); -} -.Pixel_AI_HackBackGround:hover{ - transition: .2s linear; - background: none; -} -.close { - margin-right: 15px; - margin-top: 5px; - font-size: 2rem; - cursor: pointer; - color: white; - &.Pixel_AI_HackBackGround { - background: none; - } - -} - -.close:hover { - color: #d14206; - transition: 0.2s linear; -} - -.fed { - font-weight: 650; - text-align: center; - font-size: 1.5rem; - line-height: 36px; -} - -hr { - margin-bottom: 5px; - border: 1px solid #6e6c6a; -} - -.chatbox { - padding: 10px; - padding-bottom: 0; - height: 70vh; - overflow-y: auto; - color: black; - scrollbar-width: none; - display: flex; - justify-content: space-between; - gap: 0; - align-items: center; - flex-direction: column; - overflow-x: hidden; -} - -.messageBox { - display: flex; - align-items: flex-start; - justify-content: flex-start; - flex-direction: column; - width: 100%; -} - -.lockScroll { - overflow: hidden; -} - -.usermessage, -.botmessage { - margin-top: 10px; - font-size: 0.95rem; - padding: 0.5rem; - border-radius: 10px; - max-width: 80%; - position: relative; - animation: fade-in 2s; - padding-left: 1rem; - padding-right: 1rem; - word-wrap: break-word; - white-space: pre-wrap; -} - -.usermessage { - text-align: left; - background-color: #e0e0e0; - align-self: flex-end; - color: #000; -} - -.usermessage:after { - content: ""; - position: absolute; - top: -10px; - left: 99%; - width: 0px; - height: 0px; - border-bottom: 20px solid #e0e0e0; - border-right: 20px solid transparent; - transform: translateX(-50%) translateY(50%) rotate(87deg); -} - -.botmessage { - text-align: left; - background: linear-gradient( - 260deg, - rgba(255, 190, 11, 0.84) -29.7%, - rgba(244, 43, 3, 0.84) 128.34% - ); - color: #fff; - &.omegaBackGround { - background: linear-gradient( - 260deg, - rgba(22, 175, 143, 0.84) -29.7%, - rgba(1, 113, 227, 0.84) 128.34% - ); - } -} - -.botmessage:after { - content: ""; - position: absolute; - top: -20px; - left: 2%; - width: 0px; - height: 0px; - color: black; - border-bottom: 20px solid #d14206; - border-right: 20px solid transparent; - transform: translateX(-50%) translateY(50%) rotate(315deg); -} -.omegaBackGround:after { - border-bottom: 20px solid #0d6fac; - border-right: 20px solid transparent; - transform: translateX(-50%) translateY(50%) rotate(315deg); -} - -.inputArea { - height: 3rem; - display: flex; - align-items: center; - justify-content: center; - color: black; - border-radius: 0px 0px 25px 25px; - margin-bottom: 10px; - - input { - border: none; - height: 2.5rem; - width: 250px; - padding: 0.5rem; - padding-left: 1rem; - color: black; - border-radius: 20px 0px 0px 20px; - } -} - - - -input:focus { - outline: none; -} - -.sendMessage { - border: none; - height: 2.5rem; - width: 50px; - margin-top: 0rem; - margin-bottom: 0px; - font-size: 1.15rem; - font-weight: 600; - color: black; - background-color: white; - border-top-right-radius: 20px; - border-bottom-right-radius: 20px; - cursor: pointer; -} - -.sendIcon { - color: #ff8a00; - margin-top: 0.3rem; - &.omegaBackGround { - color: #0d8bd8; - background: white; - } - &.omegaBackGround:hover { - background: #fff; - } -} - -.micButton { - margin-bottom: 10px; - padding-bottom: 5px; - height: 40px; - background-color: white; - display: flex; - justify-content: center; - align-items: center; -} - -.chatbox::-webkit-scrollbar { - width: 0px; -} - -.thinking { - height: 25px; - padding-bottom: 20px; -} - -.thinkingIndicator { - margin-left: 2px; - font-style: italic; - color: #fff; -} - -.botmessage { - position: relative; - padding-right: 40px; // Ensure enough space for icons -} - -.messageActions { - display: none; // Hide by default - justify-content: flex-end; // Align to the right - align-items: center; - position: absolute; - bottom: -3px; - right: 10px; // Position icons to the right - transform: translateY(-50%); - width: auto; // Adjust width as necessary - gap: 5px; -} - -.messageActions button { - background: none; - border: none; - cursor: pointer; - color: #fff; - margin: -5px 0px 1px 0; -} - -.botmessage:hover .messageActions { - display: flex; - opacity: 2; -} - -.omegaBackGround { - background-color: #38ccff; - background: linear-gradient( - 260deg, - rgba(22, 175, 143, 0.84) -29.7%, - rgba(1, 113, 227, 0.839) 128.34% - ); -} - -.omegaBackGround:hover { - transition: 0.2s linear; - background: linear-gradient( - 260deg, - rgba(22, 175, 143, 0.9) -29.7%, - rgba(1, 113, 227, 0.9) 128.34% - ); -} - -@keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@media (width >= 480px) { - .chatcontainer { - right: 0; - } -} - -@media (max-width: 1024px) { - .chatcontainer { - height: auto; - } - .chatbox { - height: 50vh; - } - .micButton { - margin-bottom: 5px; - padding-bottom: 5px; - height: 40px; - background-color: white; - display: flex; - justify-content: center; - align-items: center; - } -} - -// Add media query for screens smaller than 768px -@media (max-width: 768px) { - .chatcontainer { - height: auto; - } - .chatbox { - height: 70vh; - } - .micButton { - margin-bottom: 5px; - padding-bottom: 5px; - height: 40px; - background-color: white; - display: flex; - justify-content: center; - align-items: center; - } -} - -// Add media query for screens smaller than 480px -@media (max-width: 480px) { - .chatcontainer { - height: auto; - margin-right: 15px; - } - .chatbottoggle { - z-index: 1; - } - - .chatbox { - height: 60vh; - } -} diff --git a/src/features/index.jsx b/src/features/index.jsx index 88cc15aa..d6762590 100644 --- a/src/features/index.jsx +++ b/src/features/index.jsx @@ -1,2 +1 @@ -export * from './ChatBot'; export * from './Modals'; diff --git a/src/pages/Blog/Blog.jsx b/src/pages/Blog/Blog.jsx index 44344b4d..76585227 100644 --- a/src/pages/Blog/Blog.jsx +++ b/src/pages/Blog/Blog.jsx @@ -2,7 +2,6 @@ import React, { useEffect, useState, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { Search } from "lucide-react"; import styles from "./styles/Blog.module.scss"; -import { ChatBot } from "../../features"; import { api } from "../../services"; import { ComponentLoading } from "../../microInteraction"; import BlogCard from "../../components/BlogCard/BlogCard"; @@ -91,7 +90,7 @@ const Blog = () => { ? JSON.parse(blog.author) : blog.author; if (authorObj.department) uniqueDepts.add(authorObj.department); - } catch {} + } catch { } }); setDepartments(["All", ...Array.from(uniqueDepts)]); } else { @@ -151,7 +150,7 @@ const Blog = () => { ? JSON.parse(blog.author) : blog.author; authorName = authorObj?.name || ""; - } catch {} + } catch { } const authorMatch = authorName .toLowerCase() .includes(searchQuery.toLowerCase()); @@ -199,9 +198,8 @@ const Blog = () => { {departments.map((dept, i) => (
  • { setSelectedDepartment(dept); setShowFilter(false); @@ -223,9 +221,8 @@ const Blog = () => { <> {filteredBlogs.length > 0 ? (
    {filteredBlogs.map((blog) => (
    @@ -319,8 +316,6 @@ const Blog = () => { )}
    - -
    ); }; diff --git a/src/pages/Blog/FullBlog.jsx b/src/pages/Blog/FullBlog.jsx index d1ca05ce..175695e7 100644 --- a/src/pages/Blog/FullBlog.jsx +++ b/src/pages/Blog/FullBlog.jsx @@ -3,7 +3,6 @@ import { useNavigate } from "react-router-dom"; import { Search, Filter } from "lucide-react"; import styles from "./styles/FullBlog.module.scss"; import BlogCard from "../../components/BlogCard/BlogCard"; -import { ChatBot } from "../../features"; import { api } from "../../services"; import { ComponentLoading } from "../../microInteraction"; @@ -37,7 +36,7 @@ const FullBlog = () => { try { const authorObj = typeof blog.author === 'string' ? JSON.parse(blog.author) : blog.author; authorName = authorObj?.name || ''; - } catch {} + } catch { } const authorMatch = authorName.toLowerCase().includes(searchQuery.toLowerCase()); return titleMatch || descMatch || authorMatch; }) @@ -47,61 +46,61 @@ const FullBlog = () => { const recentBlogs = filteredBlogs.slice(0, 1); useEffect(() => { - const fetchBlogs = async () => { - try { - setIsLoading(true); - const response = await api.get('/api/blog/getBlog'); - if (response.status === 200) { - const blogData = response.data.blogs || []; - let processedBlogs = []; - - if (typeof blogData === 'string') { - try { - processedBlogs = JSON.parse(blogData); - } catch { - processedBlogs = []; + const fetchBlogs = async () => { + try { + setIsLoading(true); + const response = await api.get('/api/blog/getBlog'); + if (response.status === 200) { + const blogData = response.data.blogs || []; + let processedBlogs = []; + + if (typeof blogData === 'string') { + try { + processedBlogs = JSON.parse(blogData); + } catch { + processedBlogs = []; + } + } else if (Array.isArray(blogData)) { + processedBlogs = blogData.map((blog) => ({ + id: blog.id || blog._id, + title: blog.title || blog.blogTitle || 'Untitled Blog', + desc: blog.desc || blog.metaDescription || blog.blogContent || '', + image: blog.image || blog.blogImage || 'https://via.placeholder.com/400x200', + date: blog.date || blog.blogDate || new Date().toISOString(), + author: blog.author || blog.blogAuthor || '{"name":"Unknown","department":"N/A"}', + blogLink: blog.blogLink || blog.mediumLink || '', + visibility: blog.visibility === 'private' ? 'private' : 'public', + approval: blog.approval === false ? false : true, + summary: blog.summary || blog.metaDescription || '', + likes: blog.likes || 0, + comments: blog.comments || [], + readTime: blog.readTime || Math.ceil(Math.random() * 15) + 3, + })); } - } else if (Array.isArray(blogData)) { - processedBlogs = blogData.map((blog) => ({ - id: blog.id || blog._id, - title: blog.title || blog.blogTitle || 'Untitled Blog', - desc: blog.desc || blog.metaDescription || blog.blogContent || '', - image: blog.image || blog.blogImage || 'https://via.placeholder.com/400x200', - date: blog.date || blog.blogDate || new Date().toISOString(), - author: blog.author || blog.blogAuthor || '{"name":"Unknown","department":"N/A"}', - blogLink: blog.blogLink || blog.mediumLink || '', - visibility: blog.visibility === 'private' ? 'private' : 'public', - approval: blog.approval === false ? false : true, - summary: blog.summary || blog.metaDescription || '', - likes: blog.likes || 0, - comments: blog.comments || [], - readTime: blog.readTime || Math.ceil(Math.random() * 15) + 3, - })); - } - setBlogs(processedBlogs); - - // ✅ Extract departments dynamically - const uniqueDepts = new Set(); - processedBlogs.forEach((blog) => { - try { - const authorObj = typeof blog.author === 'string' ? JSON.parse(blog.author) : blog.author; - if (authorObj.department) uniqueDepts.add(authorObj.department); - } catch {} - }); - setDepartments(["All", ...Array.from(uniqueDepts)]); - } else { - setError('Failed to fetch blogs'); + setBlogs(processedBlogs); + + // ✅ Extract departments dynamically + const uniqueDepts = new Set(); + processedBlogs.forEach((blog) => { + try { + const authorObj = typeof blog.author === 'string' ? JSON.parse(blog.author) : blog.author; + if (authorObj.department) uniqueDepts.add(authorObj.department); + } catch { } + }); + setDepartments(["All", ...Array.from(uniqueDepts)]); + } else { + setError('Failed to fetch blogs'); + } + } catch { + setError('An error occurred while fetching blogs'); + } finally { + setIsLoading(false); } - } catch { - setError('An error occurred while fetching blogs'); - } finally { - setIsLoading(false); - } - }; + }; - fetchBlogs(); -}, []); + fetchBlogs(); + }, []); @@ -125,40 +124,39 @@ const FullBlog = () => {
    -
    - - setSearchQuery(e.target.value)} - className={styles.searchInput} - /> -
    - - {showFilter && ( -
      - {departments.map((dept, i) => ( -
    • { - setSelectedDepartment(dept); - setShowFilter(false); - }} - > - {dept} -
    • - ))} -
    - )} -
    -
    -
    +
    + + setSearchQuery(e.target.value)} + className={styles.searchInput} + /> +
    + + {showFilter && ( +
      + {departments.map((dept, i) => ( +
    • { + setSelectedDepartment(dept); + setShowFilter(false); + }} + > + {dept} +
    • + ))} +
    + )} +
    +
    +
    {/* Blog Sections */} @@ -169,10 +167,10 @@ const FullBlog = () => {

    RECENTLY ADDED

    {filteredBlogs.length > 0 ? ( - + ) : (

    No recent blogs available.

    @@ -211,8 +209,6 @@ const FullBlog = () => {
    - -
  • ); }; diff --git a/src/pages/Event/Event.jsx b/src/pages/Event/Event.jsx index 6c1accbf..251c68a9 100644 --- a/src/pages/Event/Event.jsx +++ b/src/pages/Event/Event.jsx @@ -4,7 +4,6 @@ import { api } from "../../services"; import style from "./styles/Event.module.scss"; import AuthContext from "../../context/AuthContext"; import { EventCard } from "../../components"; -import { ChatBot } from "../../features"; import FormData from "../../data/FormData.json"; import ring from "../../assets/images/ring.svg"; import { MdKeyboardArrowRight } from "react-icons/md"; @@ -184,13 +183,12 @@ const Event = () => { // Slice the public pastEvents array to show only the first 4 events const displayedPastEvents = pastEvents - .filter((event)=>event.info.isPublic) - .slice(0, 4); + .filter((event) => event.info.isPublic) + .slice(0, 4); return ( <> - - + {isOpen && ( )} @@ -225,7 +223,7 @@ const Event = () => { )}
    - + <> {ongoingEvents.length > 0 && (
    @@ -235,15 +233,15 @@ const Event = () => { style={{ marginBottom: "-1rem" }} > ring - + Ongoing Events
    - +
    ) : (
    )} - {!isRegisteredInRelatedEvents && parentEventCount==0 && + {!isRegisteredInRelatedEvents && parentEventCount == 0 && authCtx.isLoggedIn && authCtx.user.access === "USER" && (
    diff --git a/src/pages/Event/PastEvent.jsx b/src/pages/Event/PastEvent.jsx index 79d6014f..7cb9ed9e 100644 --- a/src/pages/Event/PastEvent.jsx +++ b/src/pages/Event/PastEvent.jsx @@ -2,7 +2,6 @@ import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import style from "./styles/PastEvent.module.scss"; -import { ChatBot } from "../../features"; import { EventCard } from "../../components"; import { api } from "../../services"; import FormData from "../../data/FormData.json"; @@ -32,7 +31,7 @@ const PastEvent = () => { message: "Sorry for the inconvenience, we are having issues fetching our Events", }); console.error("Error fetching events:", response.data.message); - + const sortedPastEvents = events .filter((event) => event.info.isEventPast) .sort((a, b) => new Date(b.info.eventDate) - new Date(a.info.eventDate)); @@ -71,7 +70,6 @@ const PastEvent = () => { return ( <> -
    @@ -104,18 +102,18 @@ const PastEvent = () => {
    {pastEvents.map((event, index) => ( event.info.isPublic ? ( -
    - -
    +
    + +
    ) : null ))}
    diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx index 8349c3b3..164b6430 100644 --- a/src/pages/Home/Home.jsx +++ b/src/pages/Home/Home.jsx @@ -1,7 +1,7 @@ /* eslint-disable no-unused-vars */ import React from 'react'; import { Hero, About, Sponser, Feedback, Contact } from "../../sections"; -import { ChatBot, LiveEventPopup } from "../../features"; +import { LiveEventPopup } from "../../features"; const Home = () => { @@ -11,7 +11,6 @@ const Home = () => { <> -
    diff --git a/src/pages/LiveEvents/Pixel_AI_Hack/Pixel_AI_Hack.jsx b/src/pages/LiveEvents/Pixel_AI_Hack/Pixel_AI_Hack.jsx index 569d0ef0..f0873fb2 100644 --- a/src/pages/LiveEvents/Pixel_AI_Hack/Pixel_AI_Hack.jsx +++ b/src/pages/LiveEvents/Pixel_AI_Hack/Pixel_AI_Hack.jsx @@ -5,10 +5,9 @@ import { motion } from "framer-motion"; import { Element } from "react-scroll"; import { useInView } from "react-intersection-observer"; import styles from "./styles/Pixel_AI_Hack.module.scss"; -import Hero from "../../../sections/LiveEvents/Pixel_AI_Hack/Hero/Hero.jsx"; +import Hero from "../../../sections/LiveEvents/Pixel_AI_Hack/Hero/Hero.jsx"; import Sponsor from "../../../sections/LiveEvents/Pixel_AI_Hack/Sponsor/Sponsor.jsx"; import Attend from "../../../sections/LiveEvents/Pixel_AI_Hack/Attend/Attend.jsx"; -import ChatBot from "../../../features/ChatBot/ChatBot.jsx"; import Accordion from "../../../components/LiveEvents/Accordian/Accordian.jsx"; import Faqs from "../../../sections/LiveEvents/Pixel_AI_Hack/Faqs/Faqs.jsx"; import LiveInsights from "../../../sections/LiveEvents/Pixel_AI_Hack/LiveInsights/LiveInsights.jsx"; @@ -120,7 +119,7 @@ function Pixel_AI_Hack() { eventName={eventName} /> - -
    - - -
    ); } diff --git a/src/pages/Social/Social.jsx b/src/pages/Social/Social.jsx index dfd5a2ad..a911da14 100644 --- a/src/pages/Social/Social.jsx +++ b/src/pages/Social/Social.jsx @@ -4,7 +4,6 @@ import linkedinlogo from "../../assets/images/SocialMedia/linkedinLogo.svg"; import instalogo from "../../assets/images/SocialMedia/instaLogo.svg"; import styles from "./styles/Social.module.scss"; import { ComponentLoading } from "../../microInteraction"; -import { ChatBot } from "../../features"; const Social = () => { useEffect(() => { @@ -14,9 +13,8 @@ const Social = () => { return ( - +
    -

    @@ -75,7 +73,7 @@ const Social = () => {

    - + ); }; diff --git a/src/pages/Team/Team.jsx b/src/pages/Team/Team.jsx index 796677d8..5616fe73 100644 --- a/src/pages/Team/Team.jsx +++ b/src/pages/Team/Team.jsx @@ -6,7 +6,6 @@ import { TeamCard } from "../../components"; import { FaRegArrowAltCircleRight } from "react-icons/fa"; import useWindowWidth from "../../utils/hooks/useWindowWidth"; import { ComponentLoading } from "../../microInteraction"; -import { ChatBot } from "../../features"; const Team = () => { useEffect(() => { @@ -113,6 +112,7 @@ const Team = () => { ) .join(" "); + // Ensure consistent capitalization for role names switch (role.toLowerCase()) { case "pr and finance": role = "PR And Finance"; @@ -206,7 +206,6 @@ const Team = () => { return (
    -

    Meet Our{" "} @@ -270,22 +269,22 @@ const Team = () => { {error &&
    {error.message}
    } - Board Of - - Directors - - - } - members={directorsAndAbove} -/> + title={ + + Board Of + + Directors + + + } + members={directorsAndAbove} + />
    diff --git a/src/pages/Team/styles/Team.module.scss b/src/pages/Team/styles/Team.module.scss index 2989482d..818b81ac 100644 --- a/src/pages/Team/styles/Team.module.scss +++ b/src/pages/Team/styles/Team.module.scss @@ -1,4 +1,3 @@ - .Team h2 { text-align: center; font-size: 50px; @@ -9,7 +8,7 @@ color: #FF8A00; } -.para p{ +.para p { padding: 30px 70px; z-index: 1; text-align: center; @@ -20,7 +19,7 @@ margin-bottom: 15px; } -.error{ +.error { padding: 30px 70px; z-index: 1; text-align: center; @@ -48,9 +47,8 @@ font-weight: 800; } -strong { +.Team strong { font-weight: 800; - } .teamTitle { @@ -94,7 +92,7 @@ strong { } -.alumniBut{ +.alumniBut { display: flex; align-items: center; justify-content: flex-end; @@ -106,7 +104,7 @@ strong { } -.alumniBut a{ +.alumniBut a { background: var(--primary); background-clip: text; color: transparent; @@ -114,7 +112,7 @@ strong { } -.alumniBut .ulhover{ +.alumniBut .ulhover { margin-right: 10px; font-weight: 700; padding: 5px; @@ -128,7 +126,7 @@ strong { } -.alumniBut .ulhover:after { +.alumniBut .ulhover:after { background: none repeat scroll 0 0 transparent; bottom: 0; content: ""; @@ -140,21 +138,23 @@ strong { transition: width 0.3s ease 0s, left 0.3s ease 0s; width: 0; } -.alumniBut .ulhover:hover:after { - width: 100%; - left: 0; + +.alumniBut .ulhover:hover:after { + width: 100%; + left: 0; } //* Outer dark box */ .FICwrapper { - background: #0d0d0d; /* same as TEAM page background */ + background: #0d0d0d; + /* same as TEAM page background */ padding: 50px; border-radius: 20px; max-width: 1150px; min-height: 220px; margin: 30px auto; box-shadow: 0 0 40px rgba(0, 0, 0, 0.4); - + } /* Grid layout */ @@ -166,10 +166,11 @@ strong { } /* Left Image */ - .image img { +.image img { display: flex; - justify-content: center; /* centers horizontally */ - align-items: center; + justify-content: center; + /* centers horizontally */ + align-items: center; width: 180px; height: 180px; border-radius: 0%; @@ -186,7 +187,8 @@ strong { /* Text Block */ .textBlock { - text-align: left; /* ensures all text aligns left */ + text-align: left; + /* ensures all text aligns left */ } /* Name */ @@ -254,7 +256,7 @@ strong { .FICsection .image { margin-bottom: 20px; - margin:0 auto; + margin: 0 auto; } .FICsection .image img { @@ -273,7 +275,7 @@ strong { text-align: center; } - .role{ + .role { text-align: center; align-items: center; } @@ -294,7 +296,7 @@ strong { } - .error{ + .error { padding: 30px 70px; z-index: 1; text-align: center; @@ -313,7 +315,7 @@ strong { } - .para p{ + .para p { font-size: 1rem; padding: 30px 30px; line-height: auto; @@ -333,18 +335,19 @@ strong { padding: 0; } - h3{ + .teamSection h3 { text-align: center; font-size: 1.5rem; } - .para p{ + .para p { margin-bottom: 20px; } .Team h2 { font-size: 40px; } + .circle { left: -30%; background-color: rgba(255, 92, 0, 0.59); @@ -354,7 +357,7 @@ strong { margin-left: 0px; } - .error{ + .error { padding: 30px 70px; z-index: 1; text-align: center; @@ -364,24 +367,25 @@ strong { margin-bottom: 10px; } - .lastRowCentered{ + .lastRowCentered { margin-top: 1rem; } - + .alumniBut { margin-right: 10px; padding-right: 15px; } - .alumniBut a{ + + .alumniBut a { font-size: 20px; } } -@media (max-width: 430px){ - .lastRowCentered{ +@media (max-width: 430px) { + .lastRowCentered { margin-bottom: 0; } } @@ -391,7 +395,8 @@ strong { grid-template-columns: repeat(2, 1fr); gap: 5vw; } - .lastRowCentered{ + + .lastRowCentered { margin-bottom: 0; } -} +} \ No newline at end of file diff --git a/src/services/chatbot/chatbotService.js b/src/services/chatbot/chatbotService.js new file mode 100644 index 00000000..1544a531 --- /dev/null +++ b/src/services/chatbot/chatbotService.js @@ -0,0 +1,94 @@ +/** + * @fileoverview Chatbot Service for FED Frontend + * @module services/chatbot/chatbotService + * @description API client for chatbot backend communication + */ + +import { api } from '../index'; + +/** + * Chatbot API Service + * Communicates with FED Backend chatbot endpoints + */ +const chatbotService = { + /** + * Send a message to the chatbot and get AI response + * @param {string} message - User's message + * @param {Array} conversationHistory - Optional conversation history for context + * @returns {Promise} Response object + */ + sendMessage: async (message, conversationHistory = []) => { + try { + const response = await api.post('/api/chatbot/message', { + message, + conversationHistory + }, { + withCredentials: true // Send cookies for optional auth + }); + + return response.data; + } catch (error) { + console.error('[Chatbot Service] Error:', error); + + // Handle auth required response + if (error.response?.data?.requiresAuth) { + return { + success: false, + requiresAuth: true, + message: error.response.data.message + }; + } + + return { + success: false, + response: 'Sorry, I encountered an error connecting to the server. Please try again.', + error: error.message + }; + } + }, + + /** + * Check chatbot service health + * @returns {Promise} Health status + */ + checkHealth: async () => { + try { + const response = await api.get('/api/chatbot/health'); + return response.data; + } catch (error) { + console.error('[Chatbot Service] Health check failed:', error); + return { + status: 'unhealthy', + error: error.message + }; + } + }, + + /** + * Send email through chatbot to FED + * @param {string} content - Email content + * @param {string} senderName - Optional sender name + * @param {string} senderEmail - Optional sender email + * @returns {Promise} Response object + */ + sendEmail: async (content, senderName = null, senderEmail = null) => { + try { + const response = await api.post('/api/chatbot/send-email', { + content, + senderName, + senderEmail + }, { + withCredentials: true + }); + return response.data; + } catch (error) { + console.error('[Chatbot Service] Email error:', error); + return { + success: false, + message: 'Failed to send email. Please try again.' + }; + } + } +}; + +export default chatbotService; diff --git a/src/services/chatbot/index.js b/src/services/chatbot/index.js new file mode 100644 index 00000000..a306b4c3 --- /dev/null +++ b/src/services/chatbot/index.js @@ -0,0 +1,6 @@ +/** + * @fileoverview Chatbot Service Exports + * @module services/chatbot + */ + +export { default as chatbotService } from './chatbotService'; diff --git a/src/services/client/axiosBotClient.js b/src/services/client/axiosBotClient.js deleted file mode 100644 index 307abf87..00000000 --- a/src/services/client/axiosBotClient.js +++ /dev/null @@ -1,13 +0,0 @@ -import axios from "axios"; - -const baseURL = import.meta.env.VITE_CHATBOT_API_URL || "http://localhost:5000"; -let headers = { - 'Content-Type': 'application/json', -}; - -const axiosClient = axios.create({ - baseURL: baseURL, - headers, -}); - -export default axiosClient; \ No newline at end of file diff --git a/src/services/client/axiosClient.js b/src/services/client/axiosClient.js index 6673fccd..dfde5df3 100644 --- a/src/services/client/axiosClient.js +++ b/src/services/client/axiosClient.js @@ -4,6 +4,7 @@ const baseURL = import.meta.env.VITE_API_URL || "http://localhost:5000"; const axiosClient = axios.create({ baseURL: baseURL, + withCredentials: true // Send cookies with requests for authentication }); export default axiosClient; \ No newline at end of file diff --git a/src/services/index.jsx b/src/services/index.jsx index 4d00e5cb..5a282e73 100644 --- a/src/services/index.jsx +++ b/src/services/index.jsx @@ -1,4 +1,4 @@ // Api services -export {default as api} from './client/axiosClient'; -export {default as apiBot} from './client/axiosBotClient'; \ No newline at end of file +export { default as api } from './client/axiosClient'; +// apiBot removed - chatbot now uses main api client \ No newline at end of file