update
Browse files- client/src/layouts/Panel.jsx +22 -33
- client/src/pages/game/App.jsx +36 -1
- client/src/theme.js +7 -2
client/src/layouts/Panel.jsx
CHANGED
|
@@ -1,19 +1,6 @@
|
|
| 1 |
-
import {
|
| 2 |
-
Box,
|
| 3 |
-
CircularProgress,
|
| 4 |
-
Typography,
|
| 5 |
-
ThemeProvider,
|
| 6 |
-
createTheme,
|
| 7 |
-
} from "@mui/material";
|
| 8 |
import { useEffect, useState } from "react";
|
| 9 |
|
| 10 |
-
// Créer un thème local en mode clair pour les chips
|
| 11 |
-
const lightTheme = createTheme({
|
| 12 |
-
palette: {
|
| 13 |
-
mode: "light",
|
| 14 |
-
},
|
| 15 |
-
});
|
| 16 |
-
|
| 17 |
// Component for displaying a single panel
|
| 18 |
export function Panel({ segment, panel, panelIndex }) {
|
| 19 |
const [imageLoaded, setImageLoaded] = useState(false);
|
|
@@ -125,25 +112,27 @@ export function Panel({ segment, panel, panelIndex }) {
|
|
| 125 |
|
| 126 |
{/* Texte du segment (uniquement sur le premier panel) */}
|
| 127 |
{panelIndex === 0 && segment.text && (
|
| 128 |
-
<
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
| 147 |
)}
|
| 148 |
</>
|
| 149 |
)}
|
|
|
|
| 1 |
+
import { Box, CircularProgress, Typography } from "@mui/material";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import { useEffect, useState } from "react";
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
// Component for displaying a single panel
|
| 5 |
export function Panel({ segment, panel, panelIndex }) {
|
| 6 |
const [imageLoaded, setImageLoaded] = useState(false);
|
|
|
|
| 112 |
|
| 113 |
{/* Texte du segment (uniquement sur le premier panel) */}
|
| 114 |
{panelIndex === 0 && segment.text && (
|
| 115 |
+
<Box
|
| 116 |
+
sx={{
|
| 117 |
+
position: "absolute",
|
| 118 |
+
bottom: "20px",
|
| 119 |
+
left: "20px",
|
| 120 |
+
right: "20px",
|
| 121 |
+
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
| 122 |
+
fontSize: ".9rem",
|
| 123 |
+
padding: "10px",
|
| 124 |
+
borderRadius: "8px",
|
| 125 |
+
boxShadow: "0 -2px 4px rgba(0,0,0,0.1)",
|
| 126 |
+
zIndex: 2,
|
| 127 |
+
color: "black",
|
| 128 |
+
"& .MuiChip-root": {
|
| 129 |
+
color: "black",
|
| 130 |
+
borderColor: "black",
|
| 131 |
+
},
|
| 132 |
+
}}
|
| 133 |
+
>
|
| 134 |
+
{segment.text}
|
| 135 |
+
</Box>
|
| 136 |
)}
|
| 137 |
</>
|
| 138 |
)}
|
client/src/pages/game/App.jsx
CHANGED
|
@@ -275,6 +275,38 @@ function App() {
|
|
| 275 |
}
|
| 276 |
};
|
| 277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
const handleStoryAction = async (action, choiceId = null) => {
|
| 279 |
setIsLoading(true);
|
| 280 |
try {
|
|
@@ -323,7 +355,10 @@ function App() {
|
|
| 323 |
// 5. Désactiver le loading car l'histoire est affichée
|
| 324 |
setIsLoading(false);
|
| 325 |
|
| 326 |
-
// 6.
|
|
|
|
|
|
|
|
|
|
| 327 |
if (
|
| 328 |
response.data.image_prompts &&
|
| 329 |
response.data.image_prompts.length > 0
|
|
|
|
| 275 |
}
|
| 276 |
};
|
| 277 |
|
| 278 |
+
// Fonction pour jouer l'audio
|
| 279 |
+
const playAudio = async (text) => {
|
| 280 |
+
try {
|
| 281 |
+
// Nettoyer le texte des balises markdown et des chips
|
| 282 |
+
const cleanText = text.replace(/\*\*(.*?)\*\*/g, "$1");
|
| 283 |
+
|
| 284 |
+
// Appeler l'API text-to-speech
|
| 285 |
+
const response = await api.post(`${API_URL}/api/text-to-speech`, {
|
| 286 |
+
text: cleanText,
|
| 287 |
+
});
|
| 288 |
+
|
| 289 |
+
if (response.data.success) {
|
| 290 |
+
// Créer un Blob à partir du base64
|
| 291 |
+
const audioBlob = await fetch(
|
| 292 |
+
`data:audio/mpeg;base64,${response.data.audio_base64}`
|
| 293 |
+
).then((r) => r.blob());
|
| 294 |
+
const audioUrl = URL.createObjectURL(audioBlob);
|
| 295 |
+
|
| 296 |
+
// Mettre à jour la source de l'audio
|
| 297 |
+
audioRef.current.src = audioUrl;
|
| 298 |
+
audioRef.current.play();
|
| 299 |
+
|
| 300 |
+
// Nettoyer l'URL quand l'audio est terminé
|
| 301 |
+
audioRef.current.onended = () => {
|
| 302 |
+
URL.revokeObjectURL(audioUrl);
|
| 303 |
+
};
|
| 304 |
+
}
|
| 305 |
+
} catch (error) {
|
| 306 |
+
console.error("Error playing audio:", error);
|
| 307 |
+
}
|
| 308 |
+
};
|
| 309 |
+
|
| 310 |
const handleStoryAction = async (action, choiceId = null) => {
|
| 311 |
setIsLoading(true);
|
| 312 |
try {
|
|
|
|
| 355 |
// 5. Désactiver le loading car l'histoire est affichée
|
| 356 |
setIsLoading(false);
|
| 357 |
|
| 358 |
+
// 6. Jouer l'audio du nouveau segment
|
| 359 |
+
await playAudio(response.data.story_text);
|
| 360 |
+
|
| 361 |
+
// 7. Générer les images en parallèle
|
| 362 |
if (
|
| 363 |
response.data.image_prompts &&
|
| 364 |
response.data.image_prompts.length > 0
|
client/src/theme.js
CHANGED
|
@@ -42,7 +42,7 @@ export const theme = createTheme({
|
|
| 42 |
components: {
|
| 43 |
MuiChip: {
|
| 44 |
styleOverrides: {
|
| 45 |
-
root: {
|
| 46 |
borderRadius: "4px",
|
| 47 |
backgroundColor: "transparent",
|
| 48 |
border: "1px solid",
|
|
@@ -53,7 +53,12 @@ export const theme = createTheme({
|
|
| 53 |
padding: "2px 8px",
|
| 54 |
lineHeight: "1.2",
|
| 55 |
},
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
sizeSmall: {
|
| 58 |
height: "auto",
|
| 59 |
},
|
|
|
|
| 42 |
components: {
|
| 43 |
MuiChip: {
|
| 44 |
styleOverrides: {
|
| 45 |
+
root: ({ theme }) => ({
|
| 46 |
borderRadius: "4px",
|
| 47 |
backgroundColor: "transparent",
|
| 48 |
border: "1px solid",
|
|
|
|
| 53 |
padding: "2px 8px",
|
| 54 |
lineHeight: "1.2",
|
| 55 |
},
|
| 56 |
+
...(theme.palette.mode === "dark" && {
|
| 57 |
+
backgroundColor: "transparent !important",
|
| 58 |
+
color: "inherit !important",
|
| 59 |
+
borderColor: "currentColor !important",
|
| 60 |
+
}),
|
| 61 |
+
}),
|
| 62 |
sizeSmall: {
|
| 63 |
height: "auto",
|
| 64 |
},
|