diff --git a/client/package.json b/client/package.json index 6404a4b3656b9d3a9e0eb7d5629ec7081e0c525b..0f16bdf91f639d30d56dff3ca799524cec7d0b3f 100644 --- a/client/package.json +++ b/client/package.json @@ -15,8 +15,11 @@ "@mui/icons-material": "^6.4.1", "@mui/material": "^6.4.1", "axios": "^1.7.9", + "framer-motion": "^12.0.5", + "html2canvas": "^1.4.1", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-router-dom": "^7.1.3" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/client/src/fonts/Action-Man/Action-Man-Bold-Italic.woff2 b/client/public/fonts/Action-Man/Action-Man-Bold-Italic.woff2 similarity index 100% rename from client/src/fonts/Action-Man/Action-Man-Bold-Italic.woff2 rename to client/public/fonts/Action-Man/Action-Man-Bold-Italic.woff2 diff --git a/client/src/fonts/Action-Man/Action-Man-Bold.woff2 b/client/public/fonts/Action-Man/Action-Man-Bold.woff2 similarity index 100% rename from client/src/fonts/Action-Man/Action-Man-Bold.woff2 rename to client/public/fonts/Action-Man/Action-Man-Bold.woff2 diff --git a/client/src/fonts/Action-Man/Action-Man-Italic.woff2 b/client/public/fonts/Action-Man/Action-Man-Italic.woff2 similarity index 100% rename from client/src/fonts/Action-Man/Action-Man-Italic.woff2 rename to client/public/fonts/Action-Man/Action-Man-Italic.woff2 diff --git a/client/src/fonts/Action-Man/Action-Man.woff2 b/client/public/fonts/Action-Man/Action-Man.woff2 similarity index 100% rename from client/src/fonts/Action-Man/Action-Man.woff2 rename to client/public/fonts/Action-Man/Action-Man.woff2 diff --git a/client/src/fonts/Action-Man/Action_Man_Extended-webfont.woff b/client/public/fonts/Action-Man/Action_Man_Extended-webfont.woff similarity index 100% rename from client/src/fonts/Action-Man/Action_Man_Extended-webfont.woff rename to client/public/fonts/Action-Man/Action_Man_Extended-webfont.woff diff --git a/client/src/fonts/Action-Man/Action_Man_Extended_Bold-webfont.woff2 b/client/public/fonts/Action-Man/Action_Man_Extended_Bold-webfont.woff2 similarity index 100% rename from client/src/fonts/Action-Man/Action_Man_Extended_Bold-webfont.woff2 rename to client/public/fonts/Action-Man/Action_Man_Extended_Bold-webfont.woff2 diff --git a/client/src/fonts/Action-Man/Action_Man_Extended_Bold_Italic-webfont.woff b/client/public/fonts/Action-Man/Action_Man_Extended_Bold_Italic-webfont.woff similarity index 100% rename from client/src/fonts/Action-Man/Action_Man_Extended_Bold_Italic-webfont.woff rename to client/public/fonts/Action-Man/Action_Man_Extended_Bold_Italic-webfont.woff diff --git a/client/src/fonts/Action-Man/Action_Man_Extended_Italic-webfont.woff b/client/public/fonts/Action-Man/Action_Man_Extended_Italic-webfont.woff similarity index 100% rename from client/src/fonts/Action-Man/Action_Man_Extended_Italic-webfont.woff rename to client/public/fonts/Action-Man/Action_Man_Extended_Italic-webfont.woff diff --git a/client/src/fonts/Action-Man/Action_Man_Shaded-webfont.woff b/client/public/fonts/Action-Man/Action_Man_Shaded-webfont.woff similarity index 100% rename from client/src/fonts/Action-Man/Action_Man_Shaded-webfont.woff rename to client/public/fonts/Action-Man/Action_Man_Shaded-webfont.woff diff --git a/client/src/fonts/Action-Man/Action_Man_Shaded_Italic-webfont.woff b/client/public/fonts/Action-Man/Action_Man_Shaded_Italic-webfont.woff similarity index 100% rename from client/src/fonts/Action-Man/Action_Man_Shaded_Italic-webfont.woff rename to client/public/fonts/Action-Man/Action_Man_Shaded_Italic-webfont.woff diff --git a/client/public/talky-walky-off.mp3 b/client/public/talky-walky-off.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..80c10c466d75792242dae0274da6984edb3b6df0 Binary files /dev/null and b/client/public/talky-walky-off.mp3 differ diff --git a/client/public/talky-walky-on.mp3 b/client/public/talky-walky-on.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..162622e37d1c72ea3d24062587c5c4cfce82b8e4 Binary files /dev/null and b/client/public/talky-walky-on.mp3 differ diff --git a/client/src/fonts/DigitalStripBB/DigitalStripBB_BoldItal.woff2 b/client/src/fonts/DigitalStripBB/DigitalStripBB_BoldItal.woff2 deleted file mode 100644 index a8d1d84d091cbddda3199044686f5bf715178684..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/DigitalStripBB/DigitalStripBB_BoldItal.woff2 and /dev/null differ diff --git a/client/src/fonts/DigitalStripBB/DigitalStripBB_Ital.woff2 b/client/src/fonts/DigitalStripBB/DigitalStripBB_Ital.woff2 deleted file mode 100644 index af804905890e4ed1f9ba058c20174915e743eb99..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/DigitalStripBB/DigitalStripBB_Ital.woff2 and /dev/null differ diff --git a/client/src/fonts/DigitalStripBB/DigitalStripBB_Reg.woff2 b/client/src/fonts/DigitalStripBB/DigitalStripBB_Reg.woff2 deleted file mode 100644 index 4b3796ea2737bd7337031865de11c0bc4c9d5a0e..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/DigitalStripBB/DigitalStripBB_Reg.woff2 and /dev/null differ diff --git a/client/src/fonts/Karantula/Karantula-Bold.woff2 b/client/src/fonts/Karantula/Karantula-Bold.woff2 deleted file mode 100644 index 0c19156faf8f3bc29ed8cd591920afc8f861b808..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Karantula/Karantula-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/Karantula/Karantula-Italic-Bold.woff2 b/client/src/fonts/Karantula/Karantula-Italic-Bold.woff2 deleted file mode 100644 index 4ed852dbfb5e48464efed63cae53cd61ef657b94..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Karantula/Karantula-Italic-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/Karantula/Karantula-Italic.woff2 b/client/src/fonts/Karantula/Karantula-Italic.woff2 deleted file mode 100644 index 3d6f55dee0e59ec29ec9b4a373a5dacda724694b..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Karantula/Karantula-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/Karantula/Karantula.woff2 b/client/src/fonts/Karantula/Karantula.woff2 deleted file mode 100644 index 9e9c9d37b1420bf6d1d78516d5786e2058f153db..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Karantula/Karantula.woff2 and /dev/null differ diff --git a/client/src/fonts/Komika-Display/Komika-Display.woff b/client/src/fonts/Komika-Display/Komika-Display.woff deleted file mode 100644 index 9765c0aa82a06e85eb4542681a2ddedb20d7ffce..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Display/Komika-Display.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Display/Komika_display_bold-webfont.woff b/client/src/fonts/Komika-Display/Komika_display_bold-webfont.woff deleted file mode 100644 index 2a86cc0a3c397db156537b46d2240840ce5b0502..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Display/Komika_display_bold-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Display/Komika_display_kaps-webfont.woff b/client/src/fonts/Komika-Display/Komika_display_kaps-webfont.woff deleted file mode 100644 index d607dd4e64f3f7146cfaebcb6599294747d3c859..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Display/Komika_display_kaps-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Display/Komika_display_kaps_bold-webfont.woff b/client/src/fonts/Komika-Display/Komika_display_kaps_bold-webfont.woff deleted file mode 100644 index 04302d64f473c063c180ceb7c7fb8967376e9186..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Display/Komika_display_kaps_bold-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Hand/Komika-Hand-Bold-Italic.woff2 b/client/src/fonts/Komika-Hand/Komika-Hand-Bold-Italic.woff2 deleted file mode 100644 index 4d00e7eed6ef125b41ca834e5dca5da7d98b75ca..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Hand/Komika-Hand-Bold-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/Komika-Hand/Komika-Hand-Bold.woff2 b/client/src/fonts/Komika-Hand/Komika-Hand-Bold.woff2 deleted file mode 100644 index 6cf07d240b46c268602c2f68534b41d3b626ef9c..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Hand/Komika-Hand-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/Komika-Hand/Komika-Hand-Italic.woff2 b/client/src/fonts/Komika-Hand/Komika-Hand-Italic.woff2 deleted file mode 100644 index fad3b3f7db445d37fad067996b9c9e010da55d0c..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Hand/Komika-Hand-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/Komika-Hand/Komika-Hand.woff2 b/client/src/fonts/Komika-Hand/Komika-Hand.woff2 deleted file mode 100644 index f4a697cd7533400c909841fbe79268e87de9cfc8..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Hand/Komika-Hand.woff2 and /dev/null differ diff --git a/client/src/fonts/Komika-Hand/Komika_Parch.woff2 b/client/src/fonts/Komika-Hand/Komika_Parch.woff2 deleted file mode 100644 index 0e2481446a7becd69821b263192de98f8f4a33f9..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Hand/Komika_Parch.woff2 and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXKBI-webfont.woff b/client/src/fonts/Komika-Text/KOMTXKBI-webfont.woff deleted file mode 100644 index 408a051cc071bbbcbf998ac055ca1edab4a56334..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXKBI-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTBI-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTBI-webfont.woff deleted file mode 100644 index ce1185b368884e3aa6009d1c4d2dec4360beed2b..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTBI-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTB_-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTB_-webfont.woff deleted file mode 100644 index a3407ce6b4bbf99293d1f68d925ce938148b6a5c..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTB_-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTI_-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTI_-webfont.woff deleted file mode 100644 index f1f999e0edc910b0883e20f3f3365993e541ef2a..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTI_-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTKB-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTKB-webfont.woff deleted file mode 100644 index 9e7599c346deb8c9ffa13be7f68e78099f939ba1..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTKB-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTKI-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTKI-webfont.woff deleted file mode 100644 index 6b156d23e5f50b7598d16c100bda49311074a78a..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTKI-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTK_-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTK_-webfont.woff deleted file mode 100644 index b0d646d54748892d69cb47e70fb06fc281cb01a2..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTK_-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTTI-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTTI-webfont.woff deleted file mode 100644 index 5620b4e071854d27818adfbe208e7b0e79138d31..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTTI-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXTT_-webfont.woff b/client/src/fonts/Komika-Text/KOMTXTT_-webfont.woff deleted file mode 100644 index c4087f02611a4b625913ec9e8574c89ddc76f6f4..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXTT_-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Komika-Text/KOMTXT__-webfont.woff b/client/src/fonts/Komika-Text/KOMTXT__-webfont.woff deleted file mode 100644 index 214a90f28059584ab95fb1e9abe301ea5c54c8d0..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Komika-Text/KOMTXT__-webfont.woff and /dev/null differ diff --git a/client/src/fonts/Manoskope/MANOSKOPE-Bold.woff2 b/client/src/fonts/Manoskope/MANOSKOPE-Bold.woff2 deleted file mode 100644 index 76e2ebabf3cc696a480abd76e8f48faf273600f5..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Manoskope/MANOSKOPE-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/Paete-Round/Paete-Round-Bold-Italic.woff2 b/client/src/fonts/Paete-Round/Paete-Round-Bold-Italic.woff2 deleted file mode 100644 index e2c08fb187738488fa931f12e8e43a3ec594bc39..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Paete-Round/Paete-Round-Bold-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/Paete-Round/Paete-Round-Bold.woff2 b/client/src/fonts/Paete-Round/Paete-Round-Bold.woff2 deleted file mode 100644 index 309a2c0caff1759bd4656d318cb22a368dc620cd..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Paete-Round/Paete-Round-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/Paete-Round/Paete-Round-Italic.woff2 b/client/src/fonts/Paete-Round/Paete-Round-Italic.woff2 deleted file mode 100644 index 5558b83264986de53f589dd8882ebd6aa4ebe49d..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Paete-Round/Paete-Round-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/Paete-Round/Paete-Round.woff2 b/client/src/fonts/Paete-Round/Paete-Round.woff2 deleted file mode 100644 index a42e4132c8b36d4647d461d884f614e1e559d03b..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Paete-Round/Paete-Round.woff2 and /dev/null differ diff --git a/client/src/fonts/Qarmic-Sans/Qarmic-Sans-Abridged.woff2 b/client/src/fonts/Qarmic-Sans/Qarmic-Sans-Abridged.woff2 deleted file mode 100644 index 92776051c312cfee3ba0414beda9a794ff62cd0b..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/Qarmic-Sans/Qarmic-Sans-Abridged.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Bold-Italic.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Bold-Italic.woff2 deleted file mode 100644 index fd78c38ba461af3d204835e0f2b7635d70993752..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Bold-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Bold.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Bold.woff2 deleted file mode 100644 index 172ddf351887d9e4c9a2e0a2caa526d4eaa31db5..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Bold-Italic.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Bold-Italic.woff2 deleted file mode 100644 index 42935fe3900b605ff5207c7d53ed265d0ad937a8..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Bold-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Bold.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Bold.woff2 deleted file mode 100644 index 9ba4edfcfc3ecdb44425cabdda6592130dfeb92b..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Italic.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Italic.woff2 deleted file mode 100644 index cfbeaee58e94061eeaf9e3ce464ea3a7be95253e..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended.woff2 deleted file mode 100644 index 85fd449b3166da2ea7da35d1739fcd197453ff4e..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Extended.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Italic.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Italic.woff2 deleted file mode 100644 index 752a35a6aa080f131641dbf15c0f0b1d1b0290a9..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival.woff2 b/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival.woff2 deleted file mode 100644 index 50adb0537ff5376b090d4605643ee5957ded1ff1..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Arch-Rival/SF-Arch-Rival.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Bold-Italic.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Bold-Italic.woff2 deleted file mode 100644 index 2df36ebe2bb9a9562c07e7e543a16ad4eaab0202..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Bold-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Bold.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Bold.woff2 deleted file mode 100644 index 5d2cb4be454e3031e1a2bd35482875997872754f..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Italic.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Italic.woff2 deleted file mode 100644 index b1ab5e8e14aae791c38d31309054508d5be9271b..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Bold-Italic.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Bold-Italic.woff2 deleted file mode 100644 index 853ccc1db12a07388d52efcaa7fed6a83c205916..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Bold-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Bold.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Bold.woff2 deleted file mode 100644 index 995f444bac9982b148a1fe16fccb6aab3cf91f61..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Italic.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Italic.woff2 deleted file mode 100644 index 7e8e9cb3d8a5fe9994e301fa9383f41c922f5e28..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC.woff2 deleted file mode 100644 index e82f4919ff959cf103ce1d731d19fb7c39aa5326..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand-SC.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand.woff2 b/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand.woff2 deleted file mode 100644 index 4c8632923efd9e4a6127edcec633de7202d58cde..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Cartoonist-Hand/SF-Cartoonist-Hand.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Toontime/SF-Toontime-Blotch-Italic.woff2 b/client/src/fonts/SF-Toontime/SF-Toontime-Blotch-Italic.woff2 deleted file mode 100644 index 92fd94e29457a799f8d8a9421d0b0cd33e08a75e..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Toontime/SF-Toontime-Blotch-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Toontime/SF-Toontime-Blotch.woff2 b/client/src/fonts/SF-Toontime/SF-Toontime-Blotch.woff2 deleted file mode 100644 index f828eabb9994c81d31665c7043a464b330681833..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Toontime/SF-Toontime-Blotch.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Toontime/SF-Toontime-Bold-Italic.woff2 b/client/src/fonts/SF-Toontime/SF-Toontime-Bold-Italic.woff2 deleted file mode 100644 index d28ed904cbc5e3afeebb48c7a6660d13f66fe894..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Toontime/SF-Toontime-Bold-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Toontime/SF-Toontime-Bold.woff2 b/client/src/fonts/SF-Toontime/SF-Toontime-Bold.woff2 deleted file mode 100644 index ca261f71e4d22e5449ab77aa25266c36ca13bc30..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Toontime/SF-Toontime-Bold.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Toontime/SF-Toontime-Italic.woff2 b/client/src/fonts/SF-Toontime/SF-Toontime-Italic.woff2 deleted file mode 100644 index b69bbb0e6099795da58953f27b11badf751b7d55..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Toontime/SF-Toontime-Italic.woff2 and /dev/null differ diff --git a/client/src/fonts/SF-Toontime/SF-Toontime.woff2 b/client/src/fonts/SF-Toontime/SF-Toontime.woff2 deleted file mode 100644 index d3d4d8c384004215748e16957f4ac809e5c9c6e9..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/SF-Toontime/SF-Toontime.woff2 and /dev/null differ diff --git a/client/src/fonts/VTC-Letterer-Pro/VTC-Letterer-Pro.woff2 b/client/src/fonts/VTC-Letterer-Pro/VTC-Letterer-Pro.woff2 deleted file mode 100644 index ec3bb640fabde62e9ec96ca521a6e2f6b003d119..0000000000000000000000000000000000000000 Binary files a/client/src/fonts/VTC-Letterer-Pro/VTC-Letterer-Pro.woff2 and /dev/null differ diff --git a/client/src/index.css b/client/src/index.css index 171963c2afc2618bce4c6c1c77b9516cdd2fbf05..b8ad25016a77be18acdecd111e07b8ae42d42a9f 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -1,4 +1,19 @@ -@import url("https://fonts.googleapis.com/css2?family=Comic+Neue:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap"); +@font-face { + font-family: "Action Man"; + src: url("/fonts/Action-Man/Action-Man.woff2") format("woff2"), + url("/fonts/Action-Man/Action_Man_Extended-webfont.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Action Man"; + src: url("/fonts/Action-Man/Action-Man-Bold.woff2") format("woff2"), + url("/fonts/Action-Man/Action_Man_Extended_Bold-webfont.woff") + format("woff"); + font-weight: bold; + font-style: normal; +} * { margin: 0; @@ -12,12 +27,14 @@ body, min-height: 100vh; background-color: #f5f5f5; overflow: hidden; + font-weight: bold; } :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-family: "Action Man", Inter, system-ui, Avenir, Helvetica, Arial, + sans-serif; line-height: 1.5; - font-weight: 400; + font-weight: bold; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); @@ -28,49 +45,3 @@ body, -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/client/src/layouts/ComicLayout.jsx b/client/src/layouts/ComicLayout.jsx index de1c937d09adb1a3b080f0e4087ef4342e96adea..c58a390233fbdb8290649b22f02578cbaeee1bac 100644 --- a/client/src/layouts/ComicLayout.jsx +++ b/client/src/layouts/ComicLayout.jsx @@ -83,7 +83,7 @@ export function ComicLayout({ segments }) { gap: 4, height: "100%", width: "100%", - px: layouts[0]?.type === "COVER" ? "calc(50% - (90vh * 0.7 * 0.5))" : 0, + px: layouts[0]?.type === "COVER" ? "calc(50% - (90vh * 0.5 * 0.5))" : 0, overflowX: "auto", overflowY: "hidden", "&::-webkit-scrollbar": { diff --git a/client/src/layouts/Panel.jsx b/client/src/layouts/Panel.jsx index 8257155d223c473e11bf86127dd0c39d0012ad8c..9d6f1e123d7fa64e2fc06110649574972522f23c 100644 --- a/client/src/layouts/Panel.jsx +++ b/client/src/layouts/Panel.jsx @@ -1,6 +1,19 @@ -import { Box, CircularProgress, Typography } from "@mui/material"; +import { + Box, + CircularProgress, + Typography, + ThemeProvider, + createTheme, +} from "@mui/material"; import { useEffect, useState } from "react"; +// Créer un thème local en mode clair pour les chips +const lightTheme = createTheme({ + palette: { + mode: "light", + }, +}); + // Component for displaying a single panel export function Panel({ segment, panel, panelIndex }) { const [imageLoaded, setImageLoaded] = useState(false); @@ -101,38 +114,36 @@ export function Panel({ segment, panel, panelIndex }) { justifyContent: "center", flexDirection: "column", gap: 1, - opacity: 0.7, + opacity: 0.5, backgroundColor: "white", zIndex: 1, }} > - - - {!segment.images?.[panelIndex] - ? "Génération en cours..." - : "Chargement de l'image..."} - + )} {/* Texte du segment (uniquement sur le premier panel) */} {panelIndex === 0 && segment.text && ( - - {segment.text} - + + + {segment.text} + + )} )} diff --git a/client/src/layouts/config.js b/client/src/layouts/config.js index d2e208fa83a191ab2a626a3e84ad3a714fef9cb1..3001cc48747650bde84f402111179dc96aef74e3 100644 --- a/client/src/layouts/config.js +++ b/client/src/layouts/config.js @@ -37,62 +37,88 @@ // }, // }; +// Panel size constants +const PANEL_SIZES = { + PORTRAIT: { width: 512, height: 1024 }, + PORTRAIT_MEDIUM: { width: 768, height: 1024 }, + LANDSCAPE: { width: 1024, height: 768 }, + SQUARE: { width: 512, height: 512 }, + SQUARE_LARGE: { width: 1024, height: 1024 }, + PANORAMIC: { width: 1024, height: 512 }, + COVER_SIZE: { width: 512, height: 768 }, +}; + +// Grid span helpers +const GRID = { + FULL_WIDTH: "1 / span 3", + TWO_THIRDS: "1 / span 2", + FULL_HEIGHT: "1 / span 2", + FULL_HEIGHT_FROM_2: "2 / span 2", // Pour les éléments qui commencent à partir de la ligne 2 +}; + export const LAYOUTS = { COVER: { gridCols: 1, gridRows: 1, panels: [ - { width: 512, height: 1024, gridColumn: "1", gridRow: "1" }, // Format portrait + { ...PANEL_SIZES.COVER_SIZE, gridColumn: "1", gridRow: "1" }, // Format portrait ], }, LAYOUT_1: { gridCols: 2, gridRows: 2, panels: [ - { width: 1024, height: 768, gridColumn: "1", gridRow: "1" }, // Landscape top left - { width: 768, height: 1024, gridColumn: "2", gridRow: "1" }, // Portrait top right - { width: 1024, height: 768, gridColumn: "1", gridRow: "2" }, // Landscape middle left - { width: 768, height: 1024, gridColumn: "2", gridRow: "2" }, // Portrait right + { ...PANEL_SIZES.LANDSCAPE, gridColumn: "1", gridRow: "1" }, // Landscape top left + { ...PANEL_SIZES.PORTRAIT_MEDIUM, gridColumn: "2", gridRow: "1" }, // Portrait top right + { ...PANEL_SIZES.LANDSCAPE, gridColumn: "1", gridRow: "2" }, // Landscape middle left + { ...PANEL_SIZES.PORTRAIT_MEDIUM, gridColumn: "2", gridRow: "2" }, // Portrait right ], }, LAYOUT_2: { gridCols: 3, gridRows: 2, panels: [ - { width: 1024, height: 1024, gridColumn: "1 / span 2", gridRow: "1" }, // Large square top left - { width: 512, height: 1024, gridColumn: "3", gridRow: "1" }, // Portrait top right - { width: 1024, height: 768, gridColumn: "1 / span 3", gridRow: "2" }, // Full width landscape bottom + { + ...PANEL_SIZES.SQUARE_LARGE, + gridColumn: GRID.TWO_THIRDS, + gridRow: "1", + }, // Large square top left + { ...PANEL_SIZES.PORTRAIT, gridColumn: "3", gridRow: "1" }, // Portrait top right + { ...PANEL_SIZES.LANDSCAPE, gridColumn: GRID.FULL_WIDTH, gridRow: "2" }, // Full width landscape bottom ], }, LAYOUT_3: { gridCols: 3, gridRows: 2, panels: [ - { width: 1024, height: 768, gridColumn: "1 / span 2", gridRow: "1" }, // Wide landscape top left - { width: 512, height: 1024, gridColumn: "3", gridRow: "1" }, // Portrait top right - { width: 512, height: 1024, gridColumn: "1", gridRow: "2" }, // Portrait bottom left - { width: 1024, height: 768, gridColumn: "2 / span 2", gridRow: "2" }, // Wide landscape bottom right + { ...PANEL_SIZES.LANDSCAPE, gridColumn: GRID.TWO_THIRDS, gridRow: "1" }, // Wide landscape top left + { ...PANEL_SIZES.PORTRAIT, gridColumn: "3", gridRow: "1" }, // Portrait top right + { ...PANEL_SIZES.PORTRAIT, gridColumn: "1", gridRow: "2" }, // Portrait bottom left + { ...PANEL_SIZES.LANDSCAPE, gridColumn: "2 / span 2", gridRow: "2" }, // Wide landscape bottom right ], }, LAYOUT_4: { gridCols: 2, gridRows: 3, panels: [ - { width: 1024, height: 512, gridColumn: "1 / span 2", gridRow: "1" }, // Wide panoramic top - { width: 512, height: 1024, gridColumn: "1", gridRow: "2 / span 2" }, // Tall portrait left - { width: 512, height: 512, gridColumn: "2", gridRow: "2" }, // Square middle right - { width: 512, height: 512, gridColumn: "2", gridRow: "3" }, // Square bottom right + { ...PANEL_SIZES.PANORAMIC, gridColumn: "1 / span 2", gridRow: "1" }, // Wide panoramic top + { + ...PANEL_SIZES.PORTRAIT, + gridColumn: "1", + gridRow: GRID.FULL_HEIGHT_FROM_2, + }, // Tall portrait left + { ...PANEL_SIZES.SQUARE, gridColumn: "2", gridRow: "2" }, // Square middle right + { ...PANEL_SIZES.SQUARE, gridColumn: "2", gridRow: "3" }, // Square bottom right ], }, LAYOUT_5: { gridCols: 3, gridRows: 3, panels: [ - { width: 1024, height: 512, gridColumn: "1 / span 3", gridRow: "1" }, // Wide panoramic top - { width: 512, height: 1024, gridColumn: "1", gridRow: "2 / span 2" }, // Tall portrait left + { ...PANEL_SIZES.PANORAMIC, gridColumn: GRID.FULL_WIDTH, gridRow: "1" }, // Wide panoramic top + { ...PANEL_SIZES.PORTRAIT, gridColumn: "1", gridRow: "2 / span 2" }, // Tall portrait left { - width: 1024, - height: 1024, + ...PANEL_SIZES.SQUARE_LARGE, gridColumn: "2 / span 2", gridRow: "2 / span 2", }, // Large square right @@ -102,10 +128,10 @@ export const LAYOUTS = { gridCols: 3, gridRows: 2, panels: [ - { width: 512, height: 1024, gridColumn: "1", gridRow: "1 / span 2" }, // Tall portrait left - { width: 512, height: 512, gridColumn: "2", gridRow: "1" }, // Square top middle - { width: 512, height: 1024, gridColumn: "3", gridRow: "1 / span 2" }, // Tall portrait right - { width: 512, height: 512, gridColumn: "2", gridRow: "2" }, // Square bottom middle + { ...PANEL_SIZES.PORTRAIT, gridColumn: "1", gridRow: GRID.FULL_HEIGHT }, // Tall portrait left + { ...PANEL_SIZES.SQUARE, gridColumn: "2", gridRow: "1" }, // Square top middle + { ...PANEL_SIZES.PORTRAIT, gridColumn: "3", gridRow: GRID.FULL_HEIGHT }, // Tall portrait right + { ...PANEL_SIZES.SQUARE, gridColumn: "2", gridRow: "2" }, // Square bottom middle ], }, }; diff --git a/client/src/main.jsx b/client/src/main.jsx index 0543c53c114dfb4adfda4aabbc8ff20faf327f16..3bb7e4d402710aa9f47087eb5c370a688c0e973d 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -1,22 +1,21 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import App from "./App.jsx"; -import "./index.css"; -import { ThemeProvider, createTheme } from "@mui/material"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { ThemeProvider } from "@mui/material/styles"; import CssBaseline from "@mui/material/CssBaseline"; - -const theme = createTheme({ - palette: { - mode: "light", - primary: { - main: "#1976d2", - }, - }, -}); +import { theme } from "./theme"; +import { Home } from "./pages/Home"; +import { Game } from "./pages/Game"; +import "./index.css"; ReactDOM.createRoot(document.getElementById("root")).render( - + + + } /> + } /> + + ); diff --git a/client/src/pages/Game.jsx b/client/src/pages/Game.jsx new file mode 100644 index 0000000000000000000000000000000000000000..6737cb444be4526ed0eb404d669d55e8a2455ada --- /dev/null +++ b/client/src/pages/Game.jsx @@ -0,0 +1,25 @@ +import { motion } from "framer-motion"; +import App from "./game/App"; +import { Box } from "@mui/material"; + +export const Game = () => { + return ( + + + + + + ); +}; + +export default Game; diff --git a/client/src/pages/Home.jsx b/client/src/pages/Home.jsx new file mode 100644 index 0000000000000000000000000000000000000000..07cc1bca2cf626a328c11d856bb1051ea034c956 --- /dev/null +++ b/client/src/pages/Home.jsx @@ -0,0 +1,51 @@ +import { Box, Button } from "@mui/material"; +import { motion } from "framer-motion"; +import { useNavigate } from "react-router-dom"; + +export function Home() { + const navigate = useNavigate(); + + return ( + + + + + + + ); +} diff --git a/client/src/App.jsx b/client/src/pages/game/App.jsx similarity index 61% rename from client/src/App.jsx rename to client/src/pages/game/App.jsx index eccb8521df44aec068533862142b5b4a6e65e3e4..bb59cc01f18d1d559994450555abb9c84f8cfd39 100644 --- a/client/src/App.jsx +++ b/client/src/pages/game/App.jsx @@ -6,15 +6,20 @@ import { Box, Typography, LinearProgress, + Chip, + IconButton, + Tooltip, } from "@mui/material"; +import SaveOutlinedIcon from "@mui/icons-material/SaveOutlined"; import RestartAltIcon from "@mui/icons-material/RestartAlt"; import axios from "axios"; -import { ComicLayout } from "./layouts/ComicLayout"; +import { ComicLayout } from "../../layouts/ComicLayout"; import { getNextPanelDimensions, groupSegmentsIntoLayouts, -} from "./layouts/utils"; -import { LAYOUTS } from "./layouts/config"; +} from "../../layouts/utils"; +import { LAYOUTS } from "../../layouts/config"; +import html2canvas from "html2canvas"; // Get API URL from environment or default to localhost in development const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000"; @@ -29,14 +34,29 @@ const api = axios.create({ }, }); -// Function to convert text with ** to bold elements -const formatTextWithBold = (text) => { +// Function to convert text with ** to Chip elements +const formatTextWithBold = (text, isInPanel = false) => { if (!text) return ""; const parts = text.split(/(\*\*.*?\*\*)/g); return parts.map((part, index) => { if (part.startsWith("**") && part.endsWith("**")) { - // Remove the ** and wrap in bold - return {part.slice(2, -2)}; + // Remove the ** and wrap in Chip + return ( + + ); } return part; }); @@ -50,6 +70,7 @@ function App() { const currentImageRequestRef = useRef(null); const pendingImageRequests = useRef(new Set()); // Track pending image requests const audioRef = useRef(new Audio()); + const comicContainerRef = useRef(null); // Start the story on first render useEffect(() => { @@ -149,54 +170,93 @@ function App() { } ); - try { - const result = await api.post( - `${API_URL}/api/generate-image-direct`, - { - prompt: imagePrompts[promptIndex], - width: panelDimensions.width, - height: panelDimensions.height, + let retryCount = 0; + const maxRetries = 3; + let success = false; + + while (retryCount < maxRetries && !success) { + try { + if (retryCount > 0) { + console.log( + `[Image] Retry attempt ${retryCount} for image ${ + promptIndex + 1 + }` + ); } - ); - - console.log(`[Image] Response for image ${promptIndex + 1}:`, { - success: result.data.success, - hasImage: !!result.data.image_base64, - imageLength: result.data.image_base64?.length, - }); - if (result.data.success) { - console.log( - `[Image] Image ${promptIndex + 1} generated successfully` + const result = await api.post( + `${API_URL}/api/generate-image-direct`, + { + prompt: imagePrompts[promptIndex], + width: panelDimensions.width, + height: panelDimensions.height, + } ); - // Mettre à jour les segments locaux - const currentImages = [ - ...(localSegments[segmentIndex].images || []), - ]; - // Remplacer le null à l'index du prompt par la nouvelle image - currentImages[promptIndex] = result.data.image_base64; - - localSegments[segmentIndex] = { - ...localSegments[segmentIndex], - images: currentImages, - }; - console.log("[State] Updating segments with new image:", { - segmentIndex, - imageIndex: promptIndex, - imagesArray: currentImages.map((img) => (img ? "image" : "null")), + + console.log(`[Image] Response for image ${promptIndex + 1}:`, { + success: result.data.success, + hasImage: !!result.data.image_base64, + imageLength: result.data.image_base64?.length, }); - // Mettre à jour l'état avec les segments mis à jour - setStorySegments([...localSegments]); - } else { + + if (result.data.success) { + console.log( + `[Image] Image ${promptIndex + 1} generated successfully` + ); + // Mettre à jour les segments locaux + const currentImages = [ + ...(localSegments[segmentIndex].images || []), + ]; + // Remplacer le null à l'index du prompt par la nouvelle image + currentImages[promptIndex] = result.data.image_base64; + + localSegments[segmentIndex] = { + ...localSegments[segmentIndex], + images: currentImages, + }; + console.log("[State] Updating segments with new image:", { + segmentIndex, + imageIndex: promptIndex, + imagesArray: currentImages.map((img) => + img ? "image" : "null" + ), + }); + // Mettre à jour l'état avec les segments mis à jour + setStorySegments([...localSegments]); + success = true; + } else { + console.error( + `[Image] Generation failed for image ${promptIndex + 1}:`, + result.data.error + ); + retryCount++; + if (retryCount < maxRetries) { + // Attendre un peu avant de réessayer (backoff exponentiel) + await new Promise((resolve) => + setTimeout(resolve, 1000 * Math.pow(2, retryCount)) + ); + } + } + } catch (error) { console.error( - `[Image] Generation failed for image ${promptIndex + 1}:`, - result.data.error + `[Image] Error generating image ${promptIndex + 1}:`, + error ); + retryCount++; + if (retryCount < maxRetries) { + // Attendre un peu avant de réessayer (backoff exponentiel) + await new Promise((resolve) => + setTimeout(resolve, 1000 * Math.pow(2, retryCount)) + ); + } } - } catch (error) { + } + + if (!success) { console.error( - `[Image] Error generating image ${promptIndex + 1}:`, - error + `[Image] Failed to generate image ${ + promptIndex + 1 + } after ${maxRetries} attempts` ); } } @@ -229,7 +289,7 @@ function App() { // 2. Créer le nouveau segment sans images const newSegment = { - text: formatTextWithBold(response.data.story_text), + text: formatTextWithBold(response.data.story_text, true), isChoice: false, isDeath: response.data.is_death, isVictory: response.data.is_victory, @@ -347,6 +407,27 @@ function App() { (segment) => !segment.isChoice ); + const handleSaveAsImage = async () => { + if (comicContainerRef.current) { + try { + const canvas = await html2canvas(comicContainerRef.current, { + scale: 2, // Meilleure qualité + backgroundColor: "#242424", // Même couleur que le fond + logging: false, + }); + + // Convertir en PNG et télécharger + const image = canvas.toDataURL("image/png"); + const link = document.createElement("a"); + link.href = image; + link.download = "my-comic-story.png"; + link.click(); + } catch (error) { + console.error("Error saving image:", error); + } + } + }; + return ( - {/* - - + 0 && - storySegments[storySegments.length - 1].radiationLevel >= 7 - ? "error.light" - : "inherit", + border: "1px solid", + borderColor: "primary.main", + borderRadius: "8px", + backgroundColor: "transparent", + color: "primary.main", + padding: "8px", + "&:hover": { + backgroundColor: "primary.main", + color: "background.paper", }, }} > - - Radiation:{" "} - - {storySegments.length > 0 - ? `${ - storySegments[storySegments.length - 1].radiationLevel - }/10` - : "0/10"} - - - - - - - */} + + + + - {/* {isLoading && } */} + {isLoading && ( + + )} @@ -438,7 +494,7 @@ function App() { sx={{ py: 3, borderColor: "divider", - backgroundColor: "background.paper", + backgroundColor: "background.default", }} > {currentChoices.length > 0 ? ( @@ -447,20 +503,46 @@ function App() { display: "flex", justifyContent: "center", gap: 2, - minHeight: "40px", + minHeight: "100px", }} > - {currentChoices.map((choice) => ( - + + Suggestion {index + 1} + + + ))} ) : storySegments.length > 0 && @@ -475,15 +557,9 @@ function App() { > diff --git a/client/src/theme.js b/client/src/theme.js new file mode 100644 index 0000000000000000000000000000000000000000..950766187cc87bb1b6fcb42ee059240777f4b308 --- /dev/null +++ b/client/src/theme.js @@ -0,0 +1,63 @@ +import { createTheme } from "@mui/material/styles"; + +export const theme = createTheme({ + palette: { + mode: "dark", + }, + typography: { + fontFamily: '"Action Man", "Roboto", "Helvetica", "Arial", sans-serif', + fontWeightRegular: 900, + button: { + fontFamily: '"Action Man", "Roboto", "Helvetica", "Arial", sans-serif', + fontWeight: 900, + }, + body1: { + fontWeight: 900, + }, + body2: { + fontWeight: 900, + }, + caption: { + fontWeight: 900, + }, + h1: { + fontWeight: 900, + }, + h2: { + fontWeight: 900, + }, + h3: { + fontWeight: 900, + }, + h4: { + fontWeight: 900, + }, + h5: { + fontWeight: 900, + }, + h6: { + fontWeight: 900, + }, + }, + components: { + MuiChip: { + styleOverrides: { + root: { + borderRadius: "4px", + backgroundColor: "transparent", + border: "1px solid", + fontWeight: 900, + fontFamily: + '"Action Man", "Roboto", "Helvetica", "Arial", sans-serif', + "& .MuiChip-label": { + padding: "2px 8px", + lineHeight: "1.2", + }, + }, + sizeSmall: { + height: "auto", + }, + }, + }, + }, +}); diff --git a/client/yarn.lock b/client/yarn.lock index 330f32a604c258097e6c20ea7cbbf87c154b2af1..d22f36f55d140252a1415e54a2a07b325dc022fb 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -738,6 +738,11 @@ dependencies: "@babel/types" "^7.20.7" +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + "@types/estree@1.0.6", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" @@ -935,6 +940,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1036,6 +1046,11 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610" + integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== + cosmiconfig@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" @@ -1056,6 +1071,13 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" @@ -1516,6 +1538,15 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +framer-motion@^12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-12.0.5.tgz#cd4f2c04b7c7595c36e4983b55e50ab257b69f4e" + integrity sha512-OgfUHfL+u80uCf6VzkV7j3+H2W9zPMdV+40bug4ftB0C2+0FpfNca2rbH2Fouq2R7//bjw6tJhOwXsrsM4+LKw== + dependencies: + motion-dom "^12.0.0" + motion-utils "^12.0.0" + tslib "^2.4.0" + fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -1666,6 +1697,14 @@ hoist-non-react-statics@^3.3.1: dependencies: react-is "^16.7.0" +html2canvas@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -2014,6 +2053,18 @@ minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +motion-dom@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-12.0.0.tgz#7045c63642eecbcc04c40b4457ebb07b3c2b3d0c" + integrity sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg== + dependencies: + motion-utils "^12.0.0" + +motion-utils@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/motion-utils/-/motion-utils-12.0.0.tgz#fabf79f4f1c818720a1b70f615e2a1768f396ac0" + integrity sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA== + ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -2228,6 +2279,23 @@ react-refresh@^0.14.2: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== +react-router-dom@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.1.3.tgz#2788f7c670aa50275e16bb033b9b04b01a45b6dc" + integrity sha512-qQGTE+77hleBzv9SIUIkGRvuFBQGagW+TQKy53UTZAO/3+YFNBYvRsNIZ1GT17yHbc63FylMOdS+m3oUriF1GA== + dependencies: + react-router "7.1.3" + +react-router@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.1.3.tgz#6c15c28838b799cb3058943e8e8015dbd6c16c7b" + integrity sha512-EezYymLY6Guk/zLQ2vRA8WvdUhWFEj5fcE3RfWihhxXBW7+cd1LsIiA3lmx+KCmneAGQuyBv820o44L2+TtkSA== + dependencies: + "@types/cookie" "^0.6.0" + cookie "^1.0.1" + set-cookie-parser "^2.6.0" + turbo-stream "2.4.0" + react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" @@ -2367,6 +2435,11 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +set-cookie-parser@^2.6.0: + version "2.7.1" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz#3016f150072202dfbe90fadee053573cc89d2943" + integrity sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ== + set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -2541,6 +2614,23 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + +tslib@^2.4.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +turbo-stream@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/turbo-stream/-/turbo-stream-2.4.0.tgz#1e4fca6725e90fa14ac4adb782f2d3759a5695f0" + integrity sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -2618,6 +2708,13 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + vite@^6.0.5: version "6.0.11" resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.11.tgz#224497e93e940b34c3357c9ebf2ec20803091ed8" diff --git a/server/game/game_logic.py b/server/game/game_logic.py index 8b4f3d5af7328ac7cd72e5030c3c78fcc5dc6c43..9ccf19a2c2594cf821f012b108558e5f1938979b 100644 --- a/server/game/game_logic.py +++ b/server/game/game_logic.py @@ -36,7 +36,7 @@ class GameState: # Story output structure class StoryLLMResponse(BaseModel): - story_text: str = Field(description="The next segment of the story. No more than 15 words THIS IS MANDATORY. Use bold formatting (like **this**) ONLY for proper nouns (like **Sarah**, **Vault 15**, **New Eden**) and important locations.") + story_text: str = Field(description="The next segment of the story. No more than 12 words THIS IS MANDATORY. Use bold formatting (like **this**) ONLY for proper nouns (like **Sarah**, **hospital**) and important locations.") choices: List[str] = Field(description="Exactly two possible choices for the player", min_items=2, max_items=2) is_victory: bool = Field(description="Whether this segment ends in Sarah's victory", default=False) radiation_increase: int = Field(description="How much radiation this segment adds (0-3)", ge=0, le=3, default=1) diff --git a/server/game/prompts/image_style_prompt.py b/server/game/prompts/image_style_prompt.py index 00bec9cfb905ba4b4973269d814da8baf1dd8ac5..f7e3ffb8ce8b38b6442024ba1d2d0d205ff2efbe 100644 --- a/server/game/prompts/image_style_prompt.py +++ b/server/game/prompts/image_style_prompt.py @@ -1,2 +1,2 @@ -IMAGE_STYLE_PROMPT = "François Schuiten comic book artist. Moebius style." -IMAGE_NEGATIVE_PROMPT = "Bubbles, text, caption. Do not include bright or clean clothing." \ No newline at end of file +IMAGE_STYLE_PROMPT = "François Schuiten comic book artist. Moebius style. Wastedlands style." +IMAGE_NEGATIVE_PROMPT = "Bubbles, text, caption. Do not include bright or clean clothing. No white or black borders. NO BORDERS." \ No newline at end of file diff --git a/server/game/prompts/system_prompt.py b/server/game/prompts/system_prompt.py index 95fd6144aeec78b67fc6459b4ffaf3efae55285c..c7c6dccea8b9048fe4cbc7c56b6bde20cb3cb26c 100644 --- a/server/game/prompts/system_prompt.py +++ b/server/game/prompts/system_prompt.py @@ -38,6 +38,7 @@ IMPORTANT FORMATTING RULES: * Location names (e.g., **Vault 15**, **New Eden**) * Major historical events (e.g., **Great Collapse**) - Do NOT use bold for common nouns or regular descriptions +- THIS IS MANDATORY FOR THE STORY TO BE CONSISTENT Each response MUST contain: 1. A detailed story segment that: diff --git a/server/pyproject.toml b/server/pyproject.toml index 300aafef59c47840145efec1f7a49fd9a2f37e49..12834e16ade0aab809d4ad1e565bd11a9972662d 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -13,16 +13,8 @@ elevenlabs = "^0.2.26" langchain = "^0.3.15" langchain-mistralai = "^0.2.4" requests = "^2.31.0" -pillow = "^10.2.0" # Pour la génération d'images de test -lorem = "^0.1.1" # Pour la génération de texte aléatoire aiohttp = "^3.9.3" - -[tool.poetry.group.dev.dependencies] -pytest = "^7.4.0" -black = "^23.1.0" -isort = "^5.12.0" - [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/server/server.py b/server/server.py index 3ceeb627eb232f82c248b364f7f499bc131c5a91..c84c6f835e789f646d3917ba9a3e745f35864d72 100644 --- a/server/server.py +++ b/server/server.py @@ -10,13 +10,8 @@ import time import random import asyncio import aiohttp -from lorem.text import TextLorem from contextlib import asynccontextmanager - -lorem = TextLorem(wsep='-', srange=(2,3), words="A B C D".split()) - - # Import local modules if os.getenv("DOCKER_ENV"): from server.game.game_logic import GameState, StoryGenerator, MAX_RADIATION @@ -127,19 +122,6 @@ class DirectImageGenerationRequest(BaseModel): width: int = Field(description="Width of the image to generate") height: int = Field(description="Height of the image to generate") -async def get_test_image(client_id: str, width=1024, height=1024): - """Get a random image from Lorem Picsum""" - # Build the Lorem Picsum URL with blur and grayscale effects - url = f"https://picsum.photos/{width}/{height}?grayscale&blur=2" - - session = await get_client_session(client_id) - async with session.get(url) as response: - if response.status == 200: - image_bytes = await response.read() - return base64.b64encode(image_bytes).decode('utf-8') - else: - raise Exception(f"Failed to fetch image: {response.status}") - @app.get("/api/health") async def health_check(): """Health check endpoint""" @@ -179,17 +161,11 @@ async def chat_endpoint(chat_message: ChatMessage): # Check for radiation death is_death = game_state.radiation_level >= MAX_RADIATION if is_death: - llm_response.story_text += f""" - -MORT PAR RADIATION: Le corps de Sarah ne peut plus supporter ce niveau de radiation ({game_state.radiation_level}/10). -Ses cellules se désagrègent alors qu'elle s'effondre, l'esprit rempli de regrets concernant sa sœur. -Les fournitures médicales qu'elle transportait n'atteindront jamais leur destination. -Sa mission s'arrête ici, une autre victime du tueur invisible des terres désolées.""" llm_response.choices = [] # Pour la mort, on ne garde qu'un seul prompt d'image if len(llm_response.image_prompts) > 1: llm_response.image_prompts = [llm_response.image_prompts[0]] - + # Add segment to history (before victory check to include final state) game_state.add_to_history(llm_response.story_text, previous_choice, llm_response.image_prompts) @@ -199,12 +175,6 @@ Sa mission s'arrête ici, une autre victime du tueur invisible des terres désol victory_chance = (game_state.story_beat - 4) * 0.2 # 20% de chance par step après le 5ème if random.random() < victory_chance: llm_response.is_victory = True - llm_response.story_text = f"""Sarah l'a fait ! Elle a trouvé un bunker sécurisé avec des survivants. - À l'intérieur, elle découvre une communauté organisée qui a réussi à maintenir un semblant de civilisation. - Ils ont même un système de décontamination ! Son niveau de radiation : {game_state.radiation_level}/10. - Elle peut enfin se reposer et peut-être un jour, reconstruire un monde meilleur. - - VICTOIRE !""" llm_response.choices = [] # Pour la victoire, on ne garde qu'un seul prompt d'image if len(llm_response.image_prompts) > 1: @@ -245,136 +215,6 @@ Sa mission s'arrête ici, une autre victime du tueur invisible des terres désol print("Traceback:", traceback.format_exc()) raise HTTPException(status_code=500, detail=str(e)) -@app.post("/api/generate-image") -async def generate_image(request: ImageGenerationRequest): - try: - # Transform story into art prompt - art_prompt = await story_generator.transform_story_to_art_prompt(request.prompt) - - print(f"Generating image with dimensions: {request.width}x{request.height}") - print(f"Using prompt: {art_prompt}") - - # Generate image using Flux client - image_bytes = flux_client.generate_image( - prompt=art_prompt, - width=request.width, - height=request.height - ) - - if image_bytes: - print(f"Received image bytes of length: {len(image_bytes)}") - # Ensure we're getting raw bytes and encoding them properly - if isinstance(image_bytes, str): - print("Warning: image_bytes is a string, converting to bytes") - image_bytes = image_bytes.encode('utf-8') - base64_image = base64.b64encode(image_bytes).decode('utf-8').strip('"') - print(f"Converted to base64 string of length: {len(base64_image)}") - print(f"First 100 chars of base64: {base64_image[:100]}") - return {"success": True, "image_base64": base64_image} - else: - print("No image bytes received from Flux client") - return {"success": False, "error": "Failed to generate image"} - - except Exception as e: - print(f"Error generating image: {str(e)}") - print(f"Error type: {type(e)}") - import traceback - print(f"Traceback: {traceback.format_exc()}") - return {"success": False, "error": str(e)} - -@app.post("/api/test/chat") -async def test_chat_endpoint(request: Request, chat_message: ChatMessage): - """Endpoint de test qui génère des données aléatoires""" - try: - client_id = request.headers.get("x-client-id", "default") - - # Cancel any previous chat request from this client - await cancel_previous_request(client_id, "chat") - - async def generate_chat_response(): - # Générer un texte aléatoire - story_text = f"**Sarah** {lorem.paragraph()}" - - # Générer un niveau de radiation aléatoire qui augmente progressivement - radiation_level = min(10, random.randint(0, 3) + (chat_message.choice_id or 0)) - - # Déterminer si c'est le premier pas - is_first_step = chat_message.message == "restart" - - # Déterminer si c'est le dernier pas (mort ou victoire) - is_last_step = radiation_level >= 30 or ( - not is_first_step and random.random() < 0.1 # 10% de chance de victoire - ) - - # Générer des choix aléatoires sauf si c'est la fin - choices = [] - if not is_last_step: - num_choices = 2 - for i in range(num_choices): - choices.append(Choice( - id=i+1, - text=f"{lorem.sentence() }" - )) - - # Construire la réponse - return StoryResponse( - story_text=story_text, - choices=choices, - radiation_level=radiation_level, - is_victory=is_last_step and radiation_level < 30 - ) - - # Create and store the new request - task = asyncio.create_task(generate_chat_response()) - await store_request(client_id, "chat", task) - - try: - response = await task - return response - except asyncio.CancelledError: - print(f"[INFO] Chat request cancelled for client {client_id}") - raise HTTPException(status_code=409, detail="Request cancelled") - - except Exception as e: - print(f"[ERROR] Error in test_chat_endpoint: {str(e)}") - raise HTTPException(status_code=500, detail=str(e)) - -@app.post("/api/test/generate-image") -async def test_generate_image(request: Request, image_request: ImageGenerationRequest): - """Endpoint de test qui récupère une image aléatoire""" - try: - client_id = request.headers.get("x-client-id", "default") - - print(f"[DEBUG] Client ID: {client_id}") - print(f"[DEBUG] Raw request data: {image_request}") - - # Cancel any previous image request from this client - await cancel_previous_request(client_id, "image") - - # Create and store the new request - task = asyncio.create_task(get_test_image(client_id, image_request.width, image_request.height)) - await store_request(client_id, "image", task) - - try: - image_base64 = await task - return { - "success": True, - "image_base64": image_base64 - } - except asyncio.CancelledError: - print(f"[INFO] Image request cancelled for client {client_id}") - return { - "success": False, - "error": "Request cancelled" - } - - except Exception as e: - print(f"[ERROR] Detailed error in test_generate_image: {str(e)}") - return { - "success": False, - "error": str(e) - } - @app.post("/api/text-to-speech") async def text_to_speech(request: TextToSpeechRequest): """Endpoint pour convertir du texte en audio via ElevenLabs"""