diff --git a/.gitignore b/.gitignore index 26b002a..1e25c06 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,7 @@ yarn-error.log* # env files (can opt-in for commiting if needed) .env* - +.env.local # vercel .vercel diff --git a/messages/en.json b/messages/en.json index c93d966..7503eb7 100644 --- a/messages/en.json +++ b/messages/en.json @@ -40,7 +40,9 @@ "confident": "Indicates how confident we are in its reliability.", "averageCredibility": "Avg. Source Credibility", "showAllSources": "Show all sources", - "close":"Close" + "close":"Close", + "mediaInputPlaceholder": "Drag and drop media here, or choose a file", + "mediaDropPlaceholder": "Drop media file here" }, "userMenu": { diff --git a/messages/fr.json b/messages/fr.json index 6329383..431e641 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -20,7 +20,7 @@ "help": "Aide", "disclaimer": "Bien que nous nous efforcions d'être précis, certaines réponses de l'IA peuvent varier", "inputPlaceholder": "Commencer à taper un nouvel énoncé...", - "outputOne": "Que souhaitez-vous verifier ajourd'hui?", + "outputOne": "Que souhaitez-vous vérifier aujourd’hui ?", "outputTwo": "Voici mon analyse:", "preferences": "Préférences", "language": "Langue", @@ -40,7 +40,9 @@ "confident": "Comment Veracity sélectionne-t-il les sources?", "averageCredibility": "Crédibilité moy. des sources", "showAllSources": "Voir toutes les sources", - "close": "Fermez" + "close": "Fermez", + "mediaInputPlaceholder": "Glissez-déposez un média ici, ou choisissez un fichier", + "mediaDropPlaceholder": "Déposez le média ici" }, "userMenu": { diff --git a/package-lock.json b/package-lock.json index e203076..ccd5399 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@auth0/nextjs-auth0": "^3.5.0", "chart.js": "^4.4.7", "cookies-next": "^5.0.2", - "next": "15.0.2", + "next": "15.0.7", "next-intl": "^3.25.3", "plotly.js": "^3.0.1", "react": "19.0.0-rc-02c0e824-20241028", @@ -22,7 +22,7 @@ "tabulator-tables": "^6.3.1" }, "devDependencies": { - "@types/node": "^20", + "@types/node": "^20.19.41", "@types/plotly.js": "^2.35.2", "@types/react": "^18", "@types/react-dom": "^18", @@ -744,70 +744,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@kurkle/color": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", @@ -932,9 +868,9 @@ } }, "node_modules/@next/env": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.0.2.tgz", - "integrity": "sha512-c0Zr0ModK5OX7D4ZV8Jt/wqoXtitLNPwUfG9zElCZztdaZyNVnN40rDXVZ/+FGuR4CcNV5AEfM6N8f+Ener7Dg==", + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.0.7.tgz", + "integrity": "sha512-g/v9G2Xmv9T6w/DcRdcdVkLuAHnGt5fcJ3C33PmPrrdtUrwrjXcT4jXasdedSbw+koXa4YeEA3nPgy6q2wmk2A==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -948,9 +884,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.2.tgz", - "integrity": "sha512-GK+8w88z+AFlmt+ondytZo2xpwlfAR8U6CRwXancHImh6EdGfHMIrTSCcx5sOSBei00GyLVL0ioo1JLKTfprgg==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.0.5.tgz", + "integrity": "sha512-BrNm/9BZoV6QEFKFZdgZRyYwhdhxV8GhW+U4D5cdkT4Wefj7YflAUZNx2FWyBPp7utBPCgJXnVbVLhlDoIfKFg==", "cpu": [ "arm64" ], @@ -964,9 +900,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.2.tgz", - "integrity": "sha512-KUpBVxIbjzFiUZhiLIpJiBoelqzQtVZbdNNsehhUn36e2YzKHphnK8eTUW1s/4aPy5kH/UTid8IuVbaOpedhpw==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.0.5.tgz", + "integrity": "sha512-SkpRdqyJLhmU6Ip0dHrZ5mLMQgTU0MlTASRwqCj6NXQJ04eS4QzBgEUUOPX+tsUOQ+KSVMgX/iQaWgQHNMyyCQ==", "cpu": [ "x64" ], @@ -980,9 +916,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.2.tgz", - "integrity": "sha512-9J7TPEcHNAZvwxXRzOtiUvwtTD+fmuY0l7RErf8Yyc7kMpE47MIQakl+3jecmkhOoIyi/Rp+ddq7j4wG6JDskQ==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.0.5.tgz", + "integrity": "sha512-nk+6BAIkIHTeQg+U1uqGpZ8K1KSAbhq80EkSgpgPC6wBmRkEeBitn4yL9C0fUiEPeZ3zN4yrvI635GG/H2QmSQ==", "cpu": [ "arm64" ], @@ -996,9 +932,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.2.tgz", - "integrity": "sha512-BjH4ZSzJIoTTZRh6rG+a/Ry4SW0HlizcPorqNBixBWc3wtQtj4Sn9FnRZe22QqrPnzoaW0ctvSz4FaH4eGKMww==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.0.5.tgz", + "integrity": "sha512-CozywhydLroNNz1AMKdKKVBuRc0UIBG7TlVgXXn51MdZo4sMbfApOlQFUyuAbKJbe67vd39Yib2lVVVDfLTtfw==", "cpu": [ "arm64" ], @@ -1012,9 +948,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.2.tgz", - "integrity": "sha512-i3U2TcHgo26sIhcwX/Rshz6avM6nizrZPvrDVDY1bXcLH1ndjbO8zuC7RoHp0NSK7wjJMPYzm7NYL1ksSKFreA==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.0.5.tgz", + "integrity": "sha512-VWfvl8toyC/5Rn1GgKfiASYgssCsxz4GtwK2cFKmmnyGfoKubFc6DfCI5MzBoe2Q2gzd2CeZDoT1BhuutSiL7A==", "cpu": [ "x64" ], @@ -1028,9 +964,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.2.tgz", - "integrity": "sha512-AMfZfSVOIR8fa+TXlAooByEF4OB00wqnms1sJ1v+iu8ivwvtPvnkwdzzFMpsK5jA2S9oNeeQ04egIWVb4QWmtQ==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.0.5.tgz", + "integrity": "sha512-xCD/V4Z55eFtG2SNyXgG3ciIikcxNe4FgmgcW4xTaEcLY59ZJVLxx4PLve2vDgp7xqvwDD4vvUsJuFMuQ12oGg==", "cpu": [ "x64" ], @@ -1044,9 +980,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.2.tgz", - "integrity": "sha512-JkXysDT0/hEY47O+Hvs8PbZAeiCQVxKfGtr4GUpNAhlG2E0Mkjibuo8ryGD29Qb5a3IOnKYNoZlh/MyKd2Nbww==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.0.5.tgz", + "integrity": "sha512-OmKXP/mUzY+AiDFk9PR3RoM6YfgzNYhtSbfvTUDk3PxoCLKnwTZ8xsFoWX2ph/RFC25QucTeAFepouGGsdBPAg==", "cpu": [ "arm64" ], @@ -1060,9 +996,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.2.tgz", - "integrity": "sha512-foaUL0NqJY/dX0Pi/UcZm5zsmSk5MtP/gxx3xOPyREkMFN+CTjctPfu3QaqrQHinaKdPnMWPJDKt4VjDfTBe/Q==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.0.5.tgz", + "integrity": "sha512-O34P9asvZtdNQ+4sEczSLruYvM7XEQKY/FCwRAeQQnrWW3tol3VEuv2GtnFb1YHsP3lZtagd11UYJqrs0Y0r2A==", "cpu": [ "x64" ], @@ -1664,35 +1600,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "license": "MIT", - "peer": true - }, "node_modules/@types/geojson": { "version": "7946.0.16", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", @@ -1708,13 +1615,6 @@ "@types/geojson": "*" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT", - "peer": true - }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1746,12 +1646,12 @@ } }, "node_modules/@types/node": { - "version": "20.17.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.28.tgz", - "integrity": "sha512-DHlH/fNL6Mho38jTy7/JT7sn2wnXI+wULR6PV4gy4VHLVvnrV/d3pHAMQHhc4gjdLmK2ZiPoMxzp6B3yRajLSQ==", + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/pbf": { @@ -2298,181 +2198,6 @@ "win32" ] }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "license": "Apache-2.0", - "peer": true - }, "node_modules/abs-svg-path": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", @@ -2483,6 +2208,7 @@ "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2518,48 +2244,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2915,39 +2599,6 @@ "node": ">=8" } }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3087,7 +2738,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -3099,16 +2750,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.0" - } - }, "node_modules/clamp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", @@ -3861,13 +3502,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, - "node_modules/electron-to-chromium": { - "version": "1.5.128", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz", - "integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==", - "license": "ISC", - "peer": true - }, "node_modules/element-size": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz", @@ -3898,20 +3532,6 @@ "once": "^1.4.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/es-abstract": { "version": "1.23.9", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", @@ -4026,13 +3646,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "license": "MIT", - "peer": true - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -4159,16 +3772,6 @@ "postcss-modules": "^6.0.0" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4650,6 +4253,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" @@ -4733,6 +4337,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -4788,23 +4393,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause", - "peer": true - }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -5199,13 +4787,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause", - "peer": true - }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -5551,6 +5132,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5702,7 +5284,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz", "integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/import-fresh": { @@ -6316,37 +5898,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/joi": { "version": "17.13.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", @@ -6395,13 +5946,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT", - "peer": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6510,16 +6054,6 @@ "node": ">= 0.8.0" } }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.11.5" - } - }, "node_modules/loader-utils": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", @@ -6593,40 +6127,6 @@ "wrappy": "1" } }, - "node_modules/mapbox-gl": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz", - "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==", - "license": "SEE LICENSE IN LICENSE.txt", - "peer": true, - "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/geojson-types": "^1.0.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^1.5.0", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^1.1.1", - "@mapbox/unitbezier": "^0.0.0", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "csscolorparser": "~1.0.3", - "earcut": "^2.2.2", - "geojson-vt": "^3.2.1", - "gl-matrix": "^3.2.1", - "grid-index": "^1.1.0", - "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", - "potpack": "^1.0.1", - "quickselect": "^2.0.0", - "rw": "^1.3.3", - "supercluster": "^7.1.0", - "tinyqueue": "^2.0.3", - "vt-pbf": "^3.1.1" - }, - "engines": { - "node": ">=6.4.0" - } - }, "node_modules/maplibre-gl": { "version": "4.7.1", "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz", @@ -6738,13 +6238,6 @@ "node": ">=0.10.0" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT", - "peer": true - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6782,29 +6275,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "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==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "peer": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6946,20 +6416,13 @@ "node": ">= 0.6" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT", - "peer": true - }, "node_modules/next": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/next/-/next-15.0.2.tgz", - "integrity": "sha512-rxIWHcAu4gGSDmwsELXacqAPUk+j8dV/A9cDF5fsiCMpkBDYkO2AEaL1dfD+nNmDiU6QMCFN8Q30VEKapT9UHQ==", + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/next/-/next-15.0.7.tgz", + "integrity": "sha512-Vl6fLEuOP1MgtEmDrY51BQr6Bl8oC8vDSHdA10xZWPPZa6e+dOwYNDLWHjvTktNLZkKYySpsW3Yzy4Lo+JORkw==", "license": "MIT", "dependencies": { - "@next/env": "15.0.2", + "@next/env": "15.0.7", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.13", "busboy": "1.6.0", @@ -6971,25 +6434,25 @@ "next": "dist/bin/next" }, "engines": { - "node": ">=18.18.0" + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.0.2", - "@next/swc-darwin-x64": "15.0.2", - "@next/swc-linux-arm64-gnu": "15.0.2", - "@next/swc-linux-arm64-musl": "15.0.2", - "@next/swc-linux-x64-gnu": "15.0.2", - "@next/swc-linux-x64-musl": "15.0.2", - "@next/swc-win32-arm64-msvc": "15.0.2", - "@next/swc-win32-x64-msvc": "15.0.2", + "@next/swc-darwin-arm64": "15.0.5", + "@next/swc-darwin-x64": "15.0.5", + "@next/swc-linux-arm64-gnu": "15.0.5", + "@next/swc-linux-arm64-musl": "15.0.5", + "@next/swc-linux-x64-gnu": "15.0.5", + "@next/swc-linux-x64-musl": "15.0.5", + "@next/swc-win32-arm64-msvc": "15.0.5", + "@next/swc-win32-x64-msvc": "15.0.5", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-02c0e824-20241028", - "react-dom": "^18.2.0 || 19.0.0-rc-02c0e824-20241028", + "react": "^18.2.0 || 19.0.0-rc-66855b96-20241106 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-66855b96-20241106 || ^19.0.0", "sass": "^1.3.0" }, "peerDependenciesMeta": { @@ -7042,13 +6505,6 @@ "license": "MIT", "optional": true }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "license": "MIT", - "peer": true - }, "node_modules/normalize-svg-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz", @@ -7768,16 +7224,6 @@ "performance-now": "^2.1.0" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/react": { "version": "19.0.0-rc-02c0e824-20241028", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0-rc-02c0e824-20241028.tgz", @@ -7874,7 +7320,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -8027,16 +7473,6 @@ "regl-scatter2d": "^3.2.3" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -8257,7 +7693,7 @@ "version": "1.86.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.86.0.tgz", "integrity": "sha512-zV8vGUld/+mP4KbMLJMX7TyGCuUp7hnkOScgCMsWuHtns8CWBoz+vmEhoGMXsaJrbUP8gj+F1dLvVe79sK8UdA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -8286,63 +7722,6 @@ "integrity": "sha512-GysnKjmMSaWcwsKTLzeJO0IhU3EyIiC0ivJKE6yDNLqt3IMxDByx8b6lSNXRNdN+ULUY0WLLjSPaZ0LuU/GnTg==", "license": "MIT" }, - "node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true - }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", @@ -8355,16 +7734,6 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -8600,6 +7969,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", + "optional": true, "engines": { "node": ">=0.10.0" } @@ -8613,17 +7983,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -9085,70 +8444,6 @@ "integrity": "sha512-qFW7kfadtcaISQIibKAIy0f3eeIXUVi8242Vly1iJfMD79kfEGzfczNuPBN/80hDxHzQJXYbmJ8VipI40hQtfA==", "license": "MIT" }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -9429,9 +8724,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unquote": { @@ -9467,37 +8762,6 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.3.2" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/update-diff": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-diff/-/update-diff-1.1.0.tgz", @@ -9550,20 +8814,6 @@ "pbf": "^3.2.1" } }, - "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "license": "MIT", - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/weak-map": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.8.tgz", @@ -9579,87 +8829,6 @@ "get-canvas-context": "^1.0.1" } }, - "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 2142636..6cbb8f7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "tabulator-tables": "^6.3.1" }, "devDependencies": { - "@types/node": "^20", + "@types/node": "^20.19.41", "@types/plotly.js": "^2.35.2", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/src/app/chat/page.module.scss b/src/app/chat/page.module.scss index ebe14f8..32c1531 100644 --- a/src/app/chat/page.module.scss +++ b/src/app/chat/page.module.scss @@ -159,3 +159,497 @@ box-shadow: 2px 2px 10px 0px #0000001A; } } } + +.mediaReliabilityTitle { + font-size: 32px; + font-weight: 700; + margin: 0; +} + +.mediaInfoIcon { + width: 22px; + height: 22px; + border: 1px solid #282828; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 13px; + font-weight: 700; +} + +.mediaReliabilityLink { + border: 0; + background: transparent; + padding: 0; + font-size: 13px; + color: #6d6d6d; + text-decoration: underline; + margin-top: 6px; + cursor: pointer; + font-family: Figtree, sans-serif; +} + +.mediaReliabilityContent { + display: grid; + grid-template-columns: 290px minmax(0, 1fr); + gap: 28px; + align-items: center; +} + +.mediaScoreCircle { + width: 290px; + height: 290px; + border-radius: 50%; + background: conic-gradient( + #15bf4e 0deg, + #15bf4e calc(var(--score) * 3.6deg), + #e8e8e8 calc(var(--score) * 3.6deg), + #e8e8e8 360deg + ); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* fallback if CSS variable is not set yet */ +.mediaReliabilityCard .mediaScoreCircle { + --score: 92; +} + +.mediaScoreInner { + width: 220px; + height: 220px; + border-radius: 50%; + background: #ffffff; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.mediaScoreLabel { + font-size: 20px; + color: #5f5f5f; + margin-bottom: 8px; +} + +.mediaScoreValue { + font-size: 62px; + line-height: 1; + font-weight: 800; + color: #000000; +} + +.mediaVerdictBlock { + min-width: 0; +} + +.mediaVerdictHeadline { + font-size: 34px; + line-height: 1.12; + font-weight: 800; + color: #15bf4e; + margin: 0 0 16px 0; +} + +.mediaVerdictSubtext { + font-size: 18px; + line-height: 1.6; + color: #282828; + margin: 0; +} + +/* ===== Popup ===== */ + +.mediaScorePopupOverlay { + position: fixed; + inset: 0; + background: #00000040; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; +} + +.mediaScorePopup { + position: relative; + max-width: 520px; + width: 100%; + background: #ffffff; + border-radius: 22px; + padding: 28px 30px; + box-shadow: 2px 2px 20px 0px #00000033; + font-family: Figtree, sans-serif; + color: #282828; +} + +.mediaScorePopup h3 { + font-size: 24px; + font-weight: 800; + margin: 0 0 14px 0; +} + +.mediaScorePopup p { + font-size: 15px; + line-height: 1.7; + margin: 0; +} + +.mediaScorePopupClose { + position: absolute; + top: 14px; + right: 16px; + width: 30px; + height: 30px; + border: 0; + border-radius: 50%; + background: #f0f0f0; + color: #282828; + font-size: 22px; + line-height: 1; + cursor: pointer; +} + +/* ===== Responsive ===== */ + +@media screen and (max-width: 1200px) { + .mediaVerificationLayout { + grid-template-columns: 1fr; + } + + .mediaReliabilityContent { + grid-template-columns: 1fr; + } + + .mediaSidePanel { + width: 100%; + } +} + +@media screen and (max-width: 850px) { + .mediaReliabilityTitle { + font-size: 24px; + } + + .mediaScoreCircle { + width: 220px; + height: 220px; + } + + .mediaScoreInner { + width: 165px; + height: 165px; + } + + .mediaScoreValue { + font-size: 42px; + } + + .mediaVerdictHeadline { + font-size: 26px; + } + + .mediaVerdictSubtext { + font-size: 16px; + } +} +/* ===== Uploaded media as user input ===== */ + +.mediaUserMessageRow { + display: flex; + justify-content: flex-end; + align-items: flex-start; + gap: 12px; + width: 100%; + margin: 18px 0 24px; +} + +.mediaUserPreviewBubble { + background: #ffffff; + border-radius: 22px; + padding: 12px; + box-shadow: 2px 2px 10px 0px #0000001a; + max-width: 360px; +} + +.mediaUserPreview { + display: block; + max-width: 100%; + max-height: 280px; + border-radius: 14px; + object-fit: contain; +} + +.mediaUserAvatar { + width: 42px; + height: 42px; + border-radius: 50%; + background: #839092; + color: #ffffff; + font-family: Figtree, sans-serif; + font-size: 16px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* ===== Result + limitations side by side ===== */ + +.mediaResultGrid { + display: grid; + grid-template-columns: minmax(0, 1fr) 360px; + gap: 28px; + align-items: start; + width: 100%; + margin-top: 20px; +} + +.mediaReliabilityCard { + width: 100%; + box-sizing: border-box; + background: #ffffff; + border-radius: 24px; + padding: 22px 24px; + box-shadow: 2px 2px 10px 0px #0000001a; + color: #282828; + font-family: Figtree, sans-serif; +} + +.mediaReliabilityTop { + margin-bottom: 20px; +} + +.mediaReliabilityTitleRow { + display: flex; + align-items: center; + gap: 8px; +} + +.mediaReliabilityTitle { + font-size: 32px; + font-weight: 700; + margin: 0; +} + +.mediaInfoIcon { + width: 22px; + height: 22px; + border: 1px solid #282828; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 13px; + font-weight: 700; +} + +.mediaReliabilityLink { + border: 0; + background: transparent; + padding: 0; + font-size: 13px; + color: #6d6d6d; + text-decoration: underline; + margin-top: 6px; + cursor: pointer; + font-family: Figtree, sans-serif; +} + +.mediaReliabilityContent { + display: grid; + grid-template-columns: 290px minmax(0, 1fr); + gap: 28px; + align-items: center; +} + +.mediaScoreCircle { + width: 290px; + height: 290px; + border-radius: 50%; + background: conic-gradient( + #15bf4e 0deg, + #15bf4e calc(var(--score) * 3.6deg), + #e8e8e8 calc(var(--score) * 3.6deg), + #e8e8e8 360deg + ); + display: flex; + align-items: center; + justify-content: center; +} + +.mediaScoreInner { + width: 220px; + height: 220px; + border-radius: 50%; + background: #ffffff; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.mediaScoreLabel { + font-size: 20px; + color: #5f5f5f; + margin-bottom: 8px; +} + +.mediaScoreValue { + font-size: 62px; + line-height: 1; + font-weight: 800; + color: #000000; +} + +.mediaVerdictHeadline { + font-size: 34px; + line-height: 1.12; + font-weight: 800; + color: #15bf4e; + margin: 0 0 16px 0; +} + +.mediaVerdictSubtext { + font-size: 18px; + line-height: 1.6; + color: #282828; + margin: 0; +} + +/* ===== Right-side limitations panel ===== */ + +.mediaSidePanel { + width: 100%; + box-sizing: border-box; + background: #f8f8f8; + border-radius: 20px; + padding: 20px; + box-shadow: 2px 2px 10px 0px #0000001a; + color: #282828; + font-family: Figtree, sans-serif; +} + +.mediaSideLead { + font-size: 15px; + font-weight: 600; + margin-bottom: 16px; +} + +.mediaSideText { + font-size: 14px; + line-height: 1.6; + margin-bottom: 18px; +} + +.mediaSideLimitations h4 { + font-size: 15px; + font-weight: 700; + margin-bottom: 12px; +} + +.mediaSideLimitations ul { + margin: 0; + padding-left: 18px; +} + +.mediaSideLimitations li { + font-size: 13px; + line-height: 1.6; + margin-bottom: 10px; +} + +/* ===== Responsive ===== */ + +@media screen and (max-width: 1200px) { + .mediaResultGrid { + grid-template-columns: 1fr; + } + + .mediaReliabilityContent { + grid-template-columns: 1fr; + } +} + +@media screen and (max-width: 850px) { + .mediaUserMessageRow { + justify-content: center; + } + + .mediaUserAvatar { + display: none; + } + + .mediaReliabilityTitle { + font-size: 24px; + } + + .mediaScoreCircle { + width: 220px; + height: 220px; + } + + .mediaScoreInner { + width: 165px; + height: 165px; + } + + .mediaScoreValue { + font-size: 42px; + } + + .mediaVerdictHeadline { + font-size: 26px; + } + + .mediaVerdictSubtext { + font-size: 16px; + } +} +/* ===== Media feedback stars ===== */ + +.mediaFeedbackRow { + display: flex; + align-items: center; + gap: 14px; + margin-top: 22px; + padding-top: 16px; + border-top: 1px solid #e8e8e8; + color: #6d6d6d; + font-family: Figtree, sans-serif; + font-size: 15px; + flex-wrap: wrap; +} + +.mediaFeedbackStars { + display: flex; + align-items: center; + gap: 4px; +} + + + +.mediaFeedbackStars button { + border: 0; + background: transparent; + color: #9a9a9a; + font-size: 24px; + line-height: 1; + cursor: pointer; + padding: 0; +} + +.mediaFeedbackStars button:hover, +.mediaFeedbackStarActive { + color: #282828 !important; +} + +.mediaFeedbackThanks { + color: #15bf4e; + font-size: 14px; + font-weight: 600; +} \ No newline at end of file diff --git a/src/app/components/chatWindow/index.tsx b/src/app/components/chatWindow/index.tsx index 4f033dd..afad128 100644 --- a/src/app/components/chatWindow/index.tsx +++ b/src/app/components/chatWindow/index.tsx @@ -1,41 +1,84 @@ "use client" -import styles from "../../chat/page.module.scss"; +import { API_URL } from "@/app/constants"; +import { useAuthApi } from "@/app/hooks/useAuthApi"; +import { FinalAnalysis, Search, Source } from "@/app/types"; +import { useLocale, useTranslations } from "next-intl"; import Image from 'next/image'; -import { useState, useCallback } from 'react'; -import Help from '../help'; -import Input from '../input'; -import HelpWindow from "../helpWindow"; +import Link from "next/link"; +import { redirect } from "next/navigation"; +import { useCallback, useEffect, useState, type CSSProperties } from 'react'; +import styles from "../../chat/page.module.scss"; +import Analysis from "../analysis"; import ChatIn from "../chatBubbles/chatIn"; import ChatOut from "../chatBubbles/chatOut"; -import { useTranslations, useLocale } from "next-intl"; -import Analysis from "../analysis"; +import Help from '../help'; +import HelpWindow from "../helpWindow"; +import Input from '../input'; import SourceWindow from "../sourceWindow"; -import { useAuthApi } from "@/app/hooks/useAuthApi"; -import { redirect } from "next/navigation"; -import { FinalAnalysis, Search, Source } from "@/app/types"; -import Link from "next/link"; -import { API_URL} from "@/app/constants"; + +/* NEW: response shape returned by backend /v1/media/verify */ +type MediaVerificationResult = { + media_type?: string; + p_fake: number; + reliability: number; + reliability_score: number; + verdict: string; + n_frames?: number; + frame_probs?: number[] | null; + explanation: string; +}; export default function ChatWindow() { const t = useTranslations('chatpage'); const { fetchWithAuth, user, error: authError, isLoading: authLoading } = useAuthApi(); - /*UI window states*/ + + /* UI window states */ const [helpIsOpen, setHelpIsOpen] = useState(false); const [sourceWindow, setSourceWindow] = useState(1); - /*input state*/ + + /* NEW: popup explaining media reliability score */ + const [mediaScoreInfoOpen, setMediaScoreInfoOpen] = useState(false); + + /* input state */ const [claim, setClaim] = useState(""); const [claimId, setClaimId] = useState(null); const [claimIsSent, setClaimIsSent] = useState(false); + /* Language */ const locale = useLocale(); - /*verification states*/ + + /* verification states */ const [finalAnalysis, setFinalAnalysis] = useState(null); const [isLoadingSources, setIsLoadingSources] = useState(false); const [sources, setSources] = useState([]); const [searchesUsed, setSearchesUsed] = useState([]); const [error, setError] = useState(null); + /* NEW: media verification loading state */ + const [isVerifyingMedia, setIsVerifyingMedia] = useState(false); + + /* NEW: media verification result shown inside the chat */ + const [mediaResult, setMediaResult] = useState(null); + const [submittedMediaName, setSubmittedMediaName] = useState(null); + const [submittedMediaPreview, setSubmittedMediaPreview] = useState(null); + const [submittedMediaType, setSubmittedMediaType] = useState(null); + + const showMediaSubmission = Boolean(submittedMediaPreview); + const showMediaResult = Boolean(mediaResult); + const showMediaVerification = Boolean(submittedMediaPreview || isVerifyingMedia || mediaResult); + /* NEW: frontend-only feedback for media result */ + const [mediaFeedbackRating, setMediaFeedbackRating] = useState(null); + const [mediaFeedbackSubmitted, setMediaFeedbackSubmitted] = useState(false); + /* NEW: cleanup browser preview URL when media preview changes/unmounts */ + useEffect(() => { + return () => { + if (submittedMediaPreview) { + URL.revokeObjectURL(submittedMediaPreview); + } + }; + }, [submittedMediaPreview]); + const fetchSources = async (analysisId: string) => { try { setIsLoadingSources(true); @@ -80,14 +123,15 @@ export default function ChatWindow() { if (!searchesResponse.ok) { throw new Error(`Failed to fetch search ID's: ${await searchesResponse.text()}`); } + const searchData = await searchesResponse.json(); setSearchesUsed(searchData); } catch (err) { console.error('Error fetching sources:', err); setError(err instanceof Error ? err.message : 'Failed to load sources'); - } + } }; - + const handleAnalysisComplete = async (data: { type: 'analysis_complete'; content: { @@ -100,14 +144,15 @@ export default function ChatWindow() { const analysisResponse = await fetchWithAuth( `${API_URL}/v1/analysis/${data.content.analysis_id}` ); - + if (!analysisResponse.ok) { throw new Error(`Failed to fetch final analysis: ${await analysisResponse.text()}`); } - + const analysisData = await analysisResponse.json(); setFinalAnalysis(analysisData); - setClaimId(analysisData.id); + setClaimId(analysisData.id); + await fetchSources(data.content.analysis_id); await fetchSearches(data.content.analysis_id); } catch (err) { @@ -121,24 +166,34 @@ export default function ChatWindow() { const verifyClaim = useCallback(async () => { let eventSource: EventSource | null = null; - let language = '' + let language = ''; if (locale == 'en') { language = 'english'; - } else if (locale == 'fr'){ - language = 'french' + } else if (locale == 'fr') { + language = 'french'; } try { setClaimIsSent(true); + + /* NEW: clear media result when user starts text verification */ + setMediaResult(null); + setSubmittedMediaName(null); + setSubmittedMediaPreview(null); + setSubmittedMediaType(null); + setFinalAnalysis(null); setSources([]); setSearchesUsed([]); setError(null); + setMediaFeedbackRating(null); + setMediaFeedbackSubmitted(false); + const claimResponse = await fetchWithAuth(`${API_URL}/v1/claims/`, { method: 'POST', - headers: { + headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, @@ -148,7 +203,7 @@ export default function ChatWindow() { language: language }) }); - + if (!claimResponse.ok) { throw new Error(`Failed to create claim: ${await claimResponse.text()}`); } @@ -157,60 +212,60 @@ export default function ChatWindow() { fetchWithAuth(`${API_URL}/v1/claims/${claimData.id}/embedding`, { method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', } }) - .then(() => console.log('Embedding update completed successfully')) - .catch(err => console.error('Embedding generation failed:', err)); + .then(() => console.log('Embedding update completed successfully')) + .catch(err => console.error('Embedding generation failed:', err)); const tokenResponse = await fetch('/api/auth/token'); if (!tokenResponse.ok) { throw new Error('Failed to get authentication token'); } + const { accessToken } = await tokenResponse.json(); - + const streamUrl = `${API_URL}/v1/analysis/claim/${claimData.id}/stream`; const urlWithToken = new URL(streamUrl); urlWithToken.searchParams.append('access_token', accessToken); eventSource = new EventSource(urlWithToken.toString(), { withCredentials: true }); - + eventSource.onopen = () => { console.log('EventSource connection established'); }; - + eventSource.onmessage = async (event) => { if (event.data === '[DONE]') { eventSource?.close(); return; } - + try { const data = JSON.parse(event.data); - + if (data.type === 'error') { throw new Error(data.content); } - - + if (data.type === 'analysis_complete' && data.content?.analysis_id) { await handleAnalysisComplete(data, eventSource); } } catch (err: unknown) { - console.error('Error handling stream data:', err); + console.log('Error handling stream data:', err); if (err instanceof Error) { setError(err.message); } - eventSource?.close(); - } - }; - - eventSource.onerror = (err) => { - console.error('EventSource error:', err); + else { + setError('Sorry, we couldn’t complete the analysis. Please try again.');} + eventSource?.close();}}; + + eventSource.onerror = () => { + console.log('EventSource error'); let errorMessage = 'Connection to analysis stream failed. Please try again.'; - + switch (eventSource?.readyState) { case EventSource.CONNECTING: errorMessage = 'Connection failed. Please check your internet connection.'; @@ -219,23 +274,24 @@ export default function ChatWindow() { errorMessage = 'Connection closed unexpectedly. Please try again.'; break; } - + setError(errorMessage); eventSource?.close(); }; - + return () => { if (eventSource && eventSource.readyState !== EventSource.CLOSED) { eventSource.close(); } }; - + } catch (err) { - console.error('Verification error:', err); + console.log('Verification error:', err); setError(err instanceof Error ? err.message : 'Error verifying claim'); eventSource?.close(); } - }, [locale, + }, [ + locale, fetchWithAuth, claim, handleAnalysisComplete, @@ -244,50 +300,307 @@ export default function ChatWindow() { setSources, setSearchesUsed, setError + ]); + /* NEW: sends selected image/video/GIF to backend /v1/media/verify */ + const verifyMedia = useCallback( + async (file: File) => { + try { + setIsVerifyingMedia(true); + setError(null); - ]); + /* NEW: show selected media preview and clear old media result */ + setSubmittedMediaName(file.name); + setSubmittedMediaPreview(URL.createObjectURL(file)); + setSubmittedMediaType(file.type); + setMediaResult(null); + + /* NEW: clear text-analysis UI so old text results do not mix with media results */ + setClaim(""); + setClaimId(null); + setClaimIsSent(false); + setFinalAnalysis(null); + setSources([]); + setSearchesUsed([]); + + /* + NEW: FormData sends the file to the backend. + The image/video is not stored by the frontend. + */ + const formData = new FormData(); + formData.append("file", file); + + console.log("Calling media endpoint:", `${API_URL}/v1/media/verify`); + + const response = await fetchWithAuth(`${API_URL}/v1/media/verify`, { + method: "POST", + headers: { + Accept: "application/json", + }, + body: formData, + }); + + if (!response.ok) { + throw new Error(await response.text()); + } + + const data = await response.json(); + + console.log("OpenFake media result:", data); + + /* NEW: save backend response so it renders in the chat */ + setMediaResult(data); + } catch (err) { + console.log("Media verification error:", err); + setError( + err instanceof Error + ? err.message + : "Could not verify this media." + ); + } finally { + setIsVerifyingMedia(false); + } + }, + [fetchWithAuth] + ); /*check auth0 user, send back to homepage if user is not logged in*/ if (authLoading) return
Loading...
; if (authError) return
Authentication error: {authError.message}
; if (!user) redirect('/'); - + return (
-
-
-

{t('title')}

-
- me - {t('learnMore')} -
+
+
+

{t('title')}

+
+ me + + {t('learnMore')} + +
+
+ +
+ {helpIsOpen === true ? : ""} +
+ + + {claimIsSent === true ? : <>} + + {claimIsSent && !finalAnalysis ? : ""} + + {finalAnalysis && finalAnalysis.analysis_text ? + <> + + + + : <>} + + {/* NEW: uploaded media shown as the user's submitted input on the right */} +{showMediaSubmission ? ( +
+
+ {submittedMediaType?.startsWith("video/") ? ( +
-
- {helpIsOpen === true ? :""} -
- - {claimIsSent === true ? : <>} - {claimIsSent && !finalAnalysis ? : ""} - {finalAnalysis && finalAnalysis.analysis_text ? - <> - - - :<>} - {error?

{error}

: ""} + +
SA
+
+) : null} + +{/* NEW: loading state while OpenFake is analyzing the media */} +{isVerifyingMedia ? : ""} + +{/* NEW: reliability score and limitations shown together after result is ready */} +{showMediaResult && mediaResult ? ( +
+
+
+
+

Reliability score

+ i +
+ +
+ +
+
+
+ Reliability + + {mediaResult.reliability_score}% + +
+
+ +
+

+ {mediaResult.verdict === "Likely real" + ? "This media is likely reliable," + : mediaResult.verdict === "Likely fake" + ? "This media may be manipulated," + : "This media should be reviewed carefully,"} +

+ +

+ {mediaResult.explanation} +

+
+
+ {/* NEW: media feedback row */} +
+ How convincing is this media analysis? + +
+ {[1, 2, 3, 4, 5].map((star) => ( + + ))} +
+ + {mediaFeedbackSubmitted ? ( + Thanks! + ) : null} +
-
- - -
-

{t('disclaimer')}

-
- + +
+) : null} + {error ?

{error}

: ""} +
+ + +
+ + +
+ +

{t('disclaimer')}

+ + + {/* NEW: hide the normal Sources panel during media verification so the + reliability card and limitations panel can sit side by side */} + {!showMediaVerification ? ( + + ) : null} + + {/* NEW: popup explaining how media reliability score is computed */} + {mediaScoreInfoOpen ? ( +
setMediaScoreInfoOpen(false)} + > +
event.stopPropagation()} + > + + +

How the score is computed

+ +

+ We use a Swin Transformer V2 model fine-tuned to distinguish real + photographs from AI-generated images. For videos and GIFs, we sample 5 + frames evenly across the duration and average the model's + confidence. The score shown is the model's estimated probability + that the content was generated by AI. +

+
+
+ ) : null} + ); } \ No newline at end of file diff --git a/src/app/components/input/index.tsx b/src/app/components/input/index.tsx index 4b908ea..126c054 100644 --- a/src/app/components/input/index.tsx +++ b/src/app/components/input/index.tsx @@ -1,28 +1,141 @@ +"use client"; import { useTranslations } from "next-intl"; +import Image from "next/image"; +import type { DragEvent, FormEvent } from "react"; +import { useEffect, useRef, useState } from "react"; import styles from "./input.module.scss"; -import Image from 'next/image'; -import { useEffect, useState, useRef } from "react"; +/* NEW: local input mode for switching between text and media */ +type VerificationMode = "text" | "media"; type Props = { setClaim: (arg0: string) => void; verifyClaim: () => void; claim: string; + + /* NEW: optional media backend handler from ChatWindow */ + verifyMedia?: (file: File) => void; + isVerifyingMedia?: boolean; }; -export default function Input({setClaim, verifyClaim, claim}: Props) { - const t = useTranslations('chatpage'); +/* NEW: media icon for the mode selector */ +function MediaIcon() { + return ( + + ); +} + +export default function Input({ + setClaim, + verifyClaim, + claim, + verifyMedia, + isVerifyingMedia = false, +}: Props) { + const t = useTranslations("chatpage"); const [inputText, setInputText] = useState(""); + + /* NEW: keep mode local so ChatWindow does not need mode props */ + const [mode, setMode] = useState("text"); + + /* NEW: local selected file state */ + const [selectedFile, setSelectedFile] = useState(null); + /* NEW: highlights media input while dragging a file */ + const [isDraggingMedia, setIsDraggingMedia] = useState(false); + const formRef = useRef(null); + /* NEW: prevents the same text claim from being verified repeatedly */ + const lastVerifiedClaimRef = useRef(""); + /* NEW: only allow image/video files */ +const isSupportedMediaFile = (file: File) => { + return file.type.startsWith("image/") || file.type.startsWith("video/"); +}; - const handleChange = (newText: string) => { - setInputText(newText) +/* NEW: allow drag-over behavior */ +const handleMediaDragOver = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDraggingMedia(true); +}; + +/* NEW: remove drag highlight */ +const handleMediaDragLeave = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDraggingMedia(false); +}; + +/* NEW: select dropped media file */ +const handleMediaDrop = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDraggingMedia(false); + + const droppedFile = e.dataTransfer.files?.[0]; + + if (!droppedFile) return; + + if (!isSupportedMediaFile(droppedFile)) { + console.log("Unsupported media file:", droppedFile.type); + return; } - const handleSubmit = (e: React.ChangeEvent) => { + + setSelectedFile(droppedFile); +}; + const handleChange = (newText: string) => { + setInputText(newText); + }; + + const handleSubmit = (e: FormEvent) => { e.preventDefault(); - setClaim(inputText); - setInputText(""); - } + + if (mode === "text") { + const trimmedText = inputText.trim(); + + if (!trimmedText) return; + + setClaim(trimmedText); + setInputText(""); + return; + } + + /* NEW: media submit */ + if (mode === "media" && selectedFile) { + if (verifyMedia) { + verifyMedia(selectedFile); + } else { + console.log( + "Media selected, but verifyMedia is not connected yet:", + selectedFile.name + ); + } + } + }; + useEffect(() => { // Access URL parameters using window.location.search const queryParams = new URLSearchParams(window.location.search); @@ -30,6 +143,9 @@ export default function Input({setClaim, verifyClaim, claim}: Props) { if (q) { const decodedQuery = decodeURIComponent(q); + + /* NEW: URL query should always submit as text */ + setMode("text"); setInputText(decodedQuery); // Automatically trigger form submission @@ -38,18 +154,95 @@ export default function Input({setClaim, verifyClaim, claim}: Props) { }, 100); } }, []); + useEffect(() => { - if (claim !== '') { - verifyClaim(); - } - }, [claim]); + /* Existing behavior, but only in text mode. + The ref prevents repeated verification of the same claim. */ + if ( + mode === "text" && + claim !== "" && + lastVerifiedClaimRef.current !== claim + ) { + lastVerifiedClaimRef.current = claim; + verifyClaim(); + } +}, [claim, mode, verifyClaim]); return (
- handleChange(e.target.value)} value={inputText} /> - -
+ {/* NEW: icon mode selector */} +
+ + + +
+ + {mode === "text" ? ( + handleChange(e.target.value)} + value={inputText} + /> + ) : ( + /* NEW: styled file picker */ + + )} + + + ); } \ No newline at end of file diff --git a/src/app/components/input/input.module.scss b/src/app/components/input/input.module.scss index e72ae3a..766bb7c 100644 --- a/src/app/components/input/input.module.scss +++ b/src/app/components/input/input.module.scss @@ -1,31 +1,100 @@ -.inputWrapper{ - width:80%; - border-radius: 20px; - margin-right:5px; - .input{ - width: 80%; - padding-left: 12px; - height: 40px; - background: #f0f0f0; - border:0; - border-radius: 20px 0px 0px 20px; - } - .submit{ - width: 50px; - height: 40px; - position: relative; - top:3px; - background: #f0f0f0; - border:0; - border-radius: 0px 20px 20px 0px; - cursor: pointer; - } - @media screen and (max-width: 850px) { - margin-left:15%; - .input{ - width:70%; - font-size: 14px; - - } - } +.inputWrapper { + width: 80%; + margin-right: 5px; + display: flex; + align-items: center; + justify-content: center; + height: 40px; +} + +.modeToggle { + height: 40px; + display: flex; + align-items: center; + background: #f0f0f0; + border-radius: 20px 0 0 20px; + padding-left: 6px; + padding-right: 4px; + gap: 4px; + flex-shrink: 0; +} + +.modeButton { + width: 34px; + height: 28px; + border: 0; + border-radius: 16px; + background: transparent; + color: #7a7a7a; + font-family: Figtree, sans-serif; + font-size: 13px; + font-weight: 600; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.modeButtonActive { + background: #ffffff; + color: #282828; + box-shadow: 0 1px 4px 0 #0000001a; +} + +.input { + flex: 1; + height: 40px; + background: #f0f0f0; + border: 0; + border-radius: 0; + color: #282828; + font-family: Figtree, sans-serif; + font-size: 16px; + padding-left: 12px; + min-width: 0; +} + +.fileInputLabel { + flex: 1; + height: 40px; + background: #f0f0f0; + border: 0; + color: #777777; + font-family: Figtree, sans-serif; + font-size: 16px; + display: flex; + align-items: center; + cursor: pointer; + overflow: hidden; + min-width: 0; +} + +.fileInputText { + padding-left: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.hiddenFileInput { + display: none; +} + +.submit { + width: 50px; + height: 40px; + background: #f0f0f0; + color: #1683ff; + border: 0; + border-radius: 0 20px 20px 0; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.submit:disabled { + cursor: not-allowed; + opacity: 0.45; } \ No newline at end of file diff --git a/src/app/constants.ts b/src/app/constants.ts index 9cd8658..e8d6395 100644 --- a/src/app/constants.ts +++ b/src/app/constants.ts @@ -1 +1 @@ -export const API_URL = 'https://api.veri-fact.ai' +export const API_URL = process.env.NEXT_PUBLIC_API_URL || "https://api.veri-fact.ai"; \ No newline at end of file diff --git a/src/app/hooks/useAuthApi.ts b/src/app/hooks/useAuthApi.ts index 9a84cf8..0258801 100644 --- a/src/app/hooks/useAuthApi.ts +++ b/src/app/hooks/useAuthApi.ts @@ -1,7 +1,7 @@ "use client" import { useUser } from '@auth0/nextjs-auth0/client'; import { useRouter } from 'next/navigation'; -import { useState, useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; export function useAuthApi() { const { user, error: userError, isLoading } = useUser(); @@ -31,47 +31,59 @@ export function useAuthApi() { }, [user, accessToken, fetchToken]); - const fetchWithAuth = useCallback(async (url: string, options = {}, retry = true) => { + const fetchWithAuth = useCallback( + async (url: string, options: RequestInit = {}, retry = true) => { if (!user && !isLoading) { - console.log("no user?") - router.push('/api/auth/login'); - throw new Error('Not authenticated'); + console.log("no user?"); + router.push("/api/auth/login"); + throw new Error("Not authenticated"); } if (isLoading) { console.log("Waiting for user to load..."); - await new Promise(resolve => setTimeout(resolve, 100)); // Wait a bit + await new Promise((resolve) => setTimeout(resolve, 100)); } let token = accessToken; + if (!token) { token = await fetchToken(); } + const headers = new Headers(options.headers); + + headers.set("Authorization", `Bearer ${token}`); + + const isFormData = options.body instanceof FormData; + + if (isFormData) { + headers.delete("Content-Type"); + } else if (!headers.has("Content-Type")) { + headers.set("Content-Type", "application/json"); + } + const response = await fetch(url, { ...options, - credentials: 'include', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json', - }, + credentials: "include", + headers, }); if (!response.ok) { try { if (response.status === 401 && retry) { - // Token might be expired, try to fetch a new one token = await fetchToken(); - return fetchWithAuth(url, options, false); // Retry with new token + return fetchWithAuth(url, options, false); } } catch { - router.push('/api/auth/login'); - throw new Error('Authentication expired, please log in again'); + router.push("/api/auth/login"); + throw new Error("Authentication expired, please log in again"); } } return response; - }, [user, router, accessToken, fetchToken]); + }, + [user, isLoading, router, accessToken, fetchToken] +); return { fetchWithAuth, fetchToken, user, isLoading, error: userError }; } \ No newline at end of file