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 |
},
|