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"}
-
-
-
- }
- onClick={() => handleStoryAction("restart")}
- disabled={isLoading}
- >
- Restart
-
-
-
- */}
+
+
+
+
- {/* {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"""