update
Browse files- client/src/components/BlinkingText.jsx +37 -0
- client/src/components/BookPages.jsx +34 -0
- client/src/components/StyledText.jsx +31 -0
- client/src/pages/Game.jsx +7 -0
- client/src/pages/Home.jsx +152 -44
- client/src/pages/Tutorial.jsx +164 -65
- server/core/prompts/system.py +14 -2
- server/core/prompts/text_prompts.py +1 -0
- server/services/mistral_client.py +1 -1
client/src/components/BlinkingText.jsx
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Typography } from "@mui/material";
|
2 |
+
import { keyframes } from "@mui/system";
|
3 |
+
import { styled } from "@mui/material/styles";
|
4 |
+
|
5 |
+
const blink = keyframes`
|
6 |
+
0% { opacity: 1; }
|
7 |
+
49% { opacity: 1; }
|
8 |
+
50% { opacity: 0; }
|
9 |
+
99% { opacity: 0; }
|
10 |
+
100% { opacity: 1; }
|
11 |
+
`;
|
12 |
+
|
13 |
+
const BlinkingTypography = styled(Typography)`
|
14 |
+
animation: ${blink} 1.2s infinite steps(1);
|
15 |
+
font-size: 1rem;
|
16 |
+
font-weight: bold;
|
17 |
+
letter-spacing: 1px;
|
18 |
+
text-transform: uppercase;
|
19 |
+
color: white;
|
20 |
+
text-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
|
21 |
+
display: flex;
|
22 |
+
flex-direction: column;
|
23 |
+
align-items: center;
|
24 |
+
line-height: 1.2;
|
25 |
+
`;
|
26 |
+
|
27 |
+
export const BlinkingText = ({ children, ...props }) => {
|
28 |
+
const [firstWord, secondWord] = children.split(" ");
|
29 |
+
return (
|
30 |
+
<BlinkingTypography {...props}>
|
31 |
+
<span>{firstWord}</span>
|
32 |
+
<span>{secondWord}</span>
|
33 |
+
</BlinkingTypography>
|
34 |
+
);
|
35 |
+
};
|
36 |
+
|
37 |
+
export default BlinkingText;
|
client/src/components/BookPages.jsx
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Box } from "@mui/material";
|
2 |
+
|
3 |
+
const PAGE_COUNT = 4;
|
4 |
+
|
5 |
+
export const BookPages = () => {
|
6 |
+
return (
|
7 |
+
<>
|
8 |
+
{/* Pages */}
|
9 |
+
{[...Array(PAGE_COUNT)].map((_, index) => {
|
10 |
+
const offset = PAGE_COUNT - index;
|
11 |
+
const verticalOffset = offset * 1;
|
12 |
+
return (
|
13 |
+
<Box
|
14 |
+
key={index}
|
15 |
+
sx={{
|
16 |
+
position: "absolute",
|
17 |
+
top: `${verticalOffset}px`,
|
18 |
+
bottom: `${verticalOffset}px`,
|
19 |
+
left: `${20 + offset}px`, // Ajout d'une marge de base de 20px
|
20 |
+
right: `${-offset}px`,
|
21 |
+
background: "#888",
|
22 |
+
borderRadius: "4px",
|
23 |
+
boxShadow: "2px 4px 12px rgba(0,0,0,0.15)",
|
24 |
+
border: "1px solid rgba(0,0,0,0.1)",
|
25 |
+
zIndex: 0,
|
26 |
+
}}
|
27 |
+
/>
|
28 |
+
);
|
29 |
+
})}
|
30 |
+
</>
|
31 |
+
);
|
32 |
+
};
|
33 |
+
|
34 |
+
export default BookPages;
|
client/src/components/StyledText.jsx
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Typography } from "@mui/material";
|
2 |
+
|
3 |
+
/**
|
4 |
+
* A component that renders text with styled words (bold, italic, etc.)
|
5 |
+
* It automatically handles spacing between words and styled segments
|
6 |
+
*/
|
7 |
+
export const StyledText = ({
|
8 |
+
text,
|
9 |
+
variant = "body1",
|
10 |
+
component,
|
11 |
+
...props
|
12 |
+
}) => {
|
13 |
+
// Split the text into segments, preserving spaces
|
14 |
+
const segments = text.split(/(<strong>.*?<\/strong>)/).filter(Boolean);
|
15 |
+
|
16 |
+
return (
|
17 |
+
<Typography variant={variant} component={component} {...props}>
|
18 |
+
{segments.map((segment, index) => {
|
19 |
+
if (segment.startsWith("<strong>")) {
|
20 |
+
// Extract the text between <strong> tags and wrap it in <strong>
|
21 |
+
const content = segment.replace(/<\/?strong>/g, "");
|
22 |
+
return <strong key={index}>{content}</strong>;
|
23 |
+
}
|
24 |
+
// Return regular text segments as is
|
25 |
+
return segment;
|
26 |
+
})}
|
27 |
+
</Typography>
|
28 |
+
);
|
29 |
+
};
|
30 |
+
|
31 |
+
export default StyledText;
|
client/src/pages/Game.jsx
CHANGED
@@ -365,6 +365,13 @@ export function Game() {
|
|
365 |
left: 0,
|
366 |
right: 0,
|
367 |
zIndex: 1000,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
368 |
}}
|
369 |
/>
|
370 |
)}
|
|
|
365 |
left: 0,
|
366 |
right: 0,
|
367 |
zIndex: 1000,
|
368 |
+
height: 8,
|
369 |
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
370 |
+
"& .MuiLinearProgress-bar": {
|
371 |
+
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
372 |
+
backgroundImage:
|
373 |
+
"linear-gradient(90deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.8) 50%, rgba(255,255,255,0.2) 100%)",
|
374 |
+
},
|
375 |
}}
|
376 |
/>
|
377 |
)}
|
client/src/pages/Home.jsx
CHANGED
@@ -2,6 +2,8 @@ import { Box, Button, Typography } from "@mui/material";
|
|
2 |
import { motion } from "framer-motion";
|
3 |
import { useNavigate } from "react-router-dom";
|
4 |
import { usePageSound } from "../hooks/usePageSound";
|
|
|
|
|
5 |
|
6 |
export function Home() {
|
7 |
const navigate = useNavigate();
|
@@ -38,60 +40,166 @@ export function Home() {
|
|
38 |
width: "auto",
|
39 |
}}
|
40 |
>
|
|
|
41 |
<Box
|
42 |
-
component="img"
|
43 |
-
src="/book.webp"
|
44 |
-
alt="Book cover"
|
45 |
sx={{
|
|
|
46 |
height: "100%",
|
47 |
width: "auto",
|
48 |
-
|
49 |
-
borderRadius: "4px",
|
50 |
-
}}
|
51 |
-
/>
|
52 |
-
<Box
|
53 |
-
sx={{
|
54 |
-
position: "absolute",
|
55 |
-
top: "75%",
|
56 |
-
left: "50%",
|
57 |
-
transform: "translate(-50%, -50%)",
|
58 |
-
textAlign: "center",
|
59 |
-
color: "white",
|
60 |
-
textShadow: "2px 2px 4px rgba(0,0,0,0.15)",
|
61 |
}}
|
62 |
>
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
66 |
sx={{
|
67 |
-
|
68 |
-
|
|
|
|
|
69 |
}}
|
70 |
>
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
>
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
</Box>
|
96 |
</Box>
|
97 |
<Button
|
|
|
2 |
import { motion } from "framer-motion";
|
3 |
import { useNavigate } from "react-router-dom";
|
4 |
import { usePageSound } from "../hooks/usePageSound";
|
5 |
+
import { BlinkingText } from "../components/BlinkingText";
|
6 |
+
import { BookPages } from "../components/BookPages";
|
7 |
|
8 |
export function Home() {
|
9 |
const navigate = useNavigate();
|
|
|
40 |
width: "auto",
|
41 |
}}
|
42 |
>
|
43 |
+
{/* Container principal pour l'image et tout le contenu */}
|
44 |
<Box
|
|
|
|
|
|
|
45 |
sx={{
|
46 |
+
position: "relative",
|
47 |
height: "100%",
|
48 |
width: "auto",
|
49 |
+
zIndex: 1,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
}}
|
51 |
>
|
52 |
+
{/* Pages d'arrière-plan */}
|
53 |
+
<BookPages />
|
54 |
+
{/* Image de couverture */}
|
55 |
+
<Box
|
56 |
sx={{
|
57 |
+
position: "relative",
|
58 |
+
height: "100%",
|
59 |
+
width: "auto",
|
60 |
+
zIndex: 1,
|
61 |
}}
|
62 |
>
|
63 |
+
<Box
|
64 |
+
component="img"
|
65 |
+
src="/book-multiverse.webp"
|
66 |
+
alt="Book cover"
|
67 |
+
sx={{
|
68 |
+
height: "100%",
|
69 |
+
width: "auto",
|
70 |
+
objectFit: "contain",
|
71 |
+
borderRadius: "4px",
|
72 |
+
position: "relative",
|
73 |
+
boxShadow: "0 0 20px rgba(0,0,0,0.2)",
|
74 |
+
}}
|
75 |
+
/>
|
76 |
+
{/* Effet de reliure */}
|
77 |
+
<Box
|
78 |
+
sx={{
|
79 |
+
position: "absolute",
|
80 |
+
top: 0,
|
81 |
+
left: "10px",
|
82 |
+
bottom: 0,
|
83 |
+
width: "4px",
|
84 |
+
background:
|
85 |
+
"linear-gradient(to right, rgba(255,255,255,0.3), transparent)",
|
86 |
+
zIndex: 2,
|
87 |
+
}}
|
88 |
+
/>
|
89 |
+
<Box
|
90 |
+
sx={{
|
91 |
+
position: "absolute",
|
92 |
+
top: 0,
|
93 |
+
left: "15px",
|
94 |
+
bottom: 0,
|
95 |
+
width: "1px",
|
96 |
+
background:
|
97 |
+
"linear-gradient(to right, rgba(0,0,0,0.3), transparent)",
|
98 |
+
zIndex: 2,
|
99 |
+
}}
|
100 |
+
/>
|
101 |
+
</Box>
|
102 |
+
{/* Overlay gradient */}
|
103 |
+
<Box
|
104 |
+
sx={{
|
105 |
+
position: "absolute",
|
106 |
+
top: 0,
|
107 |
+
left: 0,
|
108 |
+
right: 0,
|
109 |
+
bottom: 0,
|
110 |
+
background:
|
111 |
+
"linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.5) 100%)",
|
112 |
+
borderRadius: "4px",
|
113 |
+
zIndex: 2,
|
114 |
+
}}
|
115 |
+
/>
|
116 |
+
{/* Contenu texte */}
|
117 |
+
<Box
|
118 |
+
sx={{
|
119 |
+
position: "absolute",
|
120 |
+
top: "75%",
|
121 |
+
left: "50%",
|
122 |
+
transform: "translate(-50%, -50%)",
|
123 |
+
textAlign: "center",
|
124 |
+
color: "white",
|
125 |
+
// textShadow: "2px 2px 4px rgba(0,0,0,0.15)",
|
126 |
+
zIndex: 3,
|
127 |
+
}}
|
128 |
+
>
|
129 |
+
<Box sx={{ position: "relative" }}>
|
130 |
+
<BlinkingText
|
131 |
+
sx={{
|
132 |
+
position: "absolute",
|
133 |
+
top: "-40px",
|
134 |
+
right: "-15px",
|
135 |
+
transform: "rotate(15deg)",
|
136 |
+
zIndex: 3,
|
137 |
+
}}
|
138 |
+
>
|
139 |
+
multiverse edition
|
140 |
+
</BlinkingText>
|
141 |
+
<Typography
|
142 |
+
variant="h2"
|
143 |
+
component="h1"
|
144 |
+
sx={{
|
145 |
+
fontWeight: "bold",
|
146 |
+
marginBottom: 2,
|
147 |
+
color: "#f0e6d9",
|
148 |
+
textShadow: `
|
149 |
+
0 -1px 1px rgba(0,0,0,0.3),
|
150 |
+
0 1px 1px rgba(255,255,255,0.2)
|
151 |
+
`,
|
152 |
+
letterSpacing: "0.5px",
|
153 |
+
filter: "brightness(0.95)",
|
154 |
+
}}
|
155 |
+
>
|
156 |
+
Sarah's Chronicles
|
157 |
+
</Typography>
|
158 |
+
</Box>
|
159 |
+
</Box>
|
160 |
+
<Box
|
161 |
+
sx={{
|
162 |
+
position: "absolute",
|
163 |
+
bottom: 32,
|
164 |
+
left: "50%",
|
165 |
+
transform: "translateX(-50%)",
|
166 |
+
textAlign: "center",
|
167 |
+
zIndex: 3,
|
168 |
+
}}
|
169 |
>
|
170 |
+
<Typography
|
171 |
+
variant="caption"
|
172 |
+
display="block"
|
173 |
+
sx={{
|
174 |
+
mb: -1,
|
175 |
+
fontWeight: "black",
|
176 |
+
color: "#f0e6d9",
|
177 |
+
textShadow: `
|
178 |
+
0 -1px 1px rgba(0,0,0,0.3),
|
179 |
+
0 1px 1px rgba(255,255,255,0.2)
|
180 |
+
`,
|
181 |
+
letterSpacing: "0.5px",
|
182 |
+
filter: "brightness(0.95)",
|
183 |
+
}}
|
184 |
+
>
|
185 |
+
a story by
|
186 |
+
</Typography>
|
187 |
+
<Typography
|
188 |
+
variant="h6"
|
189 |
+
sx={{
|
190 |
+
fontWeight: "black",
|
191 |
+
color: "#f0e6d9",
|
192 |
+
textShadow: `
|
193 |
+
0 -1px 1px rgba(0,0,0,0.3),
|
194 |
+
0 1px 1px rgba(255,255,255,0.2)
|
195 |
+
`,
|
196 |
+
letterSpacing: "0.5px",
|
197 |
+
filter: "brightness(0.95)",
|
198 |
+
}}
|
199 |
+
>
|
200 |
+
Mistral Small
|
201 |
+
</Typography>
|
202 |
+
</Box>
|
203 |
</Box>
|
204 |
</Box>
|
205 |
<Button
|
client/src/pages/Tutorial.jsx
CHANGED
@@ -2,7 +2,6 @@ import {
|
|
2 |
Box,
|
3 |
Typography,
|
4 |
Button,
|
5 |
-
Chip,
|
6 |
Paper,
|
7 |
IconButton,
|
8 |
Tooltip,
|
@@ -12,6 +11,8 @@ import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
|
12 |
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
13 |
import { usePageSound } from "../hooks/usePageSound";
|
14 |
import { motion } from "framer-motion";
|
|
|
|
|
15 |
|
16 |
export function Tutorial() {
|
17 |
const navigate = useNavigate();
|
@@ -61,6 +62,7 @@ export function Tutorial() {
|
|
61 |
"&:hover": {
|
62 |
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
63 |
},
|
|
|
64 |
}}
|
65 |
>
|
66 |
<ArrowBackIcon />
|
@@ -74,31 +76,14 @@ export function Tutorial() {
|
|
74 |
height: "80vh",
|
75 |
aspectRatio: "0.66666667",
|
76 |
display: "flex",
|
77 |
-
alignItems: "center",
|
78 |
-
justifyContent: "center",
|
79 |
-
"&::before, &::after": {
|
80 |
-
content: '""',
|
81 |
-
position: "absolute",
|
82 |
-
width: "100%",
|
83 |
-
height: "100%",
|
84 |
-
background: "white",
|
85 |
-
borderRadius: 1,
|
86 |
-
boxShadow: "0px 1px 3px rgba(0,0,0,0.2)",
|
87 |
-
},
|
88 |
-
"&::before": {
|
89 |
-
top: "4px",
|
90 |
-
left: "4px",
|
91 |
-
transform: "rotate(-1deg)",
|
92 |
-
zIndex: 1,
|
93 |
-
},
|
94 |
-
"&::after": {
|
95 |
-
top: "8px",
|
96 |
-
left: "8px",
|
97 |
-
transform: "rotate(1deg)",
|
98 |
-
zIndex: 0,
|
99 |
-
},
|
100 |
}}
|
101 |
>
|
|
|
|
|
|
|
|
|
102 |
<Paper
|
103 |
elevation={3}
|
104 |
sx={{
|
@@ -106,55 +91,169 @@ export function Tutorial() {
|
|
106 |
zIndex: 2,
|
107 |
width: "100%",
|
108 |
height: "100%",
|
109 |
-
backgroundColor: "
|
110 |
color: "black",
|
111 |
-
padding:
|
112 |
-
borderRadius:
|
113 |
display: "flex",
|
114 |
flexDirection: "column",
|
115 |
-
alignItems: "center",
|
116 |
-
justifyContent: "
|
117 |
-
|
118 |
-
gap: 4,
|
119 |
overflowY: "auto",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
}}
|
121 |
>
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
|
|
|
|
|
|
|
|
129 |
>
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
>
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
</Paper>
|
159 |
</Box>
|
160 |
|
|
|
2 |
Box,
|
3 |
Typography,
|
4 |
Button,
|
|
|
5 |
Paper,
|
6 |
IconButton,
|
7 |
Tooltip,
|
|
|
11 |
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
12 |
import { usePageSound } from "../hooks/usePageSound";
|
13 |
import { motion } from "framer-motion";
|
14 |
+
import { StyledText } from "../components/StyledText";
|
15 |
+
import { BookPages } from "../components/BookPages";
|
16 |
|
17 |
export function Tutorial() {
|
18 |
const navigate = useNavigate();
|
|
|
62 |
"&:hover": {
|
63 |
backgroundColor: "rgba(0, 0, 0, 0.7)",
|
64 |
},
|
65 |
+
zIndex: 10,
|
66 |
}}
|
67 |
>
|
68 |
<ArrowBackIcon />
|
|
|
76 |
height: "80vh",
|
77 |
aspectRatio: "0.66666667",
|
78 |
display: "flex",
|
79 |
+
alignItems: "center",
|
80 |
+
justifyContent: "center",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
}}
|
82 |
>
|
83 |
+
{/* Pages d'arrière-plan */}
|
84 |
+
<BookPages />
|
85 |
+
|
86 |
+
{/* Page principale */}
|
87 |
<Paper
|
88 |
elevation={3}
|
89 |
sx={{
|
|
|
91 |
zIndex: 2,
|
92 |
width: "100%",
|
93 |
height: "100%",
|
94 |
+
backgroundColor: "#fff",
|
95 |
color: "black",
|
96 |
+
padding: "3rem 3rem 3rem 2rem",
|
97 |
+
borderRadius: "4px",
|
98 |
display: "flex",
|
99 |
flexDirection: "column",
|
100 |
+
alignItems: "center",
|
101 |
+
justifyContent: "space-between",
|
102 |
+
gap: 2,
|
|
|
103 |
overflowY: "auto",
|
104 |
+
boxShadow: "0 0 20px rgba(0,0,0,0.2)",
|
105 |
+
"&::before": {
|
106 |
+
content: '""',
|
107 |
+
position: "absolute",
|
108 |
+
top: 0,
|
109 |
+
left: "4px",
|
110 |
+
bottom: 0,
|
111 |
+
width: "120px",
|
112 |
+
background:
|
113 |
+
"linear-gradient(to right, rgba(0,0,0,0.25), rgba(0,0,0,0))",
|
114 |
+
opacity: 0.2,
|
115 |
+
pointerEvents: "none",
|
116 |
+
zIndex: 1,
|
117 |
+
},
|
118 |
+
"&::after": {
|
119 |
+
content: '""',
|
120 |
+
position: "absolute",
|
121 |
+
top: 0,
|
122 |
+
left: "4px",
|
123 |
+
bottom: 0,
|
124 |
+
width: "1px",
|
125 |
+
background:
|
126 |
+
"linear-gradient(to right, rgba(0,0,0,0.15), transparent)",
|
127 |
+
borderRadius: "1px",
|
128 |
+
zIndex: 2,
|
129 |
+
},
|
130 |
+
"&::-webkit-scrollbar": {
|
131 |
+
width: "8px",
|
132 |
+
},
|
133 |
+
"&::-webkit-scrollbar-track": {
|
134 |
+
background: "transparent",
|
135 |
+
},
|
136 |
+
"&::-webkit-scrollbar-thumb": {
|
137 |
+
background: "rgba(0,0,0,0.1)",
|
138 |
+
borderRadius: "4px",
|
139 |
+
},
|
140 |
}}
|
141 |
>
|
142 |
+
{/* Section Synopsis */}
|
143 |
+
<Box
|
144 |
+
sx={{
|
145 |
+
maxWidth: "600px",
|
146 |
+
margin: "auto",
|
147 |
+
textAlign: "center",
|
148 |
+
flex: 1,
|
149 |
+
display: "flex",
|
150 |
+
flexDirection: "column",
|
151 |
+
justifyContent: "center",
|
152 |
+
}}
|
153 |
>
|
154 |
+
<Typography
|
155 |
+
variant="h3"
|
156 |
+
component="h1"
|
157 |
+
textAlign="center"
|
158 |
+
gutterBottom
|
159 |
+
sx={{
|
160 |
+
width: "100%",
|
161 |
+
color: "#2c1810",
|
162 |
+
fontWeight: "bold",
|
163 |
+
textShadow: `
|
164 |
+
0 -1px 1px rgba(0,0,0,0.2),
|
165 |
+
0 1px 1px rgba(255,255,255,0.3)
|
166 |
+
`,
|
167 |
+
letterSpacing: "0.5px",
|
168 |
+
marginBottom: 3,
|
169 |
+
"&::after": {
|
170 |
+
content: '""',
|
171 |
+
display: "block",
|
172 |
+
width: "40%",
|
173 |
+
height: "1px",
|
174 |
+
background: "rgba(0,0,0,0.2)",
|
175 |
+
margin: "0.5rem auto",
|
176 |
+
},
|
177 |
+
}}
|
178 |
+
>
|
179 |
+
Synopsis
|
180 |
+
</Typography>
|
181 |
+
<StyledText
|
182 |
+
variant="body1"
|
183 |
+
paragraph
|
184 |
+
sx={{
|
185 |
+
fontWeight: "normal",
|
186 |
+
color: "#2c1810",
|
187 |
+
fontSize: "0.95rem",
|
188 |
+
lineHeight: 1.6,
|
189 |
+
marginBottom: 1.5,
|
190 |
+
}}
|
191 |
+
text={`You are <strong>Sarah</strong>, an <strong>AI</strong> hunter traveling through <strong>parallel worlds</strong>. Your mission is to track down an <strong>AI</strong> that moves from world to world to avoid destruction.`}
|
192 |
+
/>
|
193 |
+
<StyledText
|
194 |
+
variant="body1"
|
195 |
+
paragraph
|
196 |
+
sx={{
|
197 |
+
fontWeight: "normal",
|
198 |
+
color: "#2c1810",
|
199 |
+
fontSize: "0.95rem",
|
200 |
+
lineHeight: 1.6,
|
201 |
+
marginBottom: 1.5,
|
202 |
+
}}
|
203 |
+
text={`With each story, you land in a completely new universe. Each <strong>world</strong> presents its own challenges and <strong>obstacles</strong>. You must make crucial <strong>decisions</strong> to advance in your <strong>quest</strong>.`}
|
204 |
+
/>
|
205 |
+
<StyledText
|
206 |
+
variant="body1"
|
207 |
+
paragraph
|
208 |
+
sx={{
|
209 |
+
fontWeight: "normal",
|
210 |
+
color: "#2c1810",
|
211 |
+
fontSize: "0.95rem",
|
212 |
+
lineHeight: 1.6,
|
213 |
+
marginBottom: 0,
|
214 |
+
}}
|
215 |
+
text={`Every <strong>choice</strong> you make can alter the course of your <strong>pursuit</strong>. <strong>Time</strong> is of the essence, and every <strong>action</strong> counts in this thrilling adventure.`}
|
216 |
+
/>
|
217 |
+
</Box>
|
218 |
+
|
219 |
+
{/* Section How to Play */}
|
220 |
+
<Box
|
221 |
+
sx={{
|
222 |
+
width: "100%",
|
223 |
+
borderTop: "1px solid rgba(0,0,0,0.1)",
|
224 |
+
paddingTop: 1.5,
|
225 |
+
marginTop: 2,
|
226 |
+
}}
|
227 |
>
|
228 |
+
<Typography
|
229 |
+
variant="h6"
|
230 |
+
sx={{
|
231 |
+
color: "#2c1810",
|
232 |
+
fontWeight: "bold",
|
233 |
+
textShadow: `
|
234 |
+
0 -1px 1px rgba(0,0,0,0.2),
|
235 |
+
0 1px 1px rgba(255,255,255,0.3)
|
236 |
+
`,
|
237 |
+
marginBottom: 0.5,
|
238 |
+
textAlign: "center",
|
239 |
+
fontSize: "1rem",
|
240 |
+
}}
|
241 |
+
>
|
242 |
+
How to play
|
243 |
+
</Typography>
|
244 |
+
<StyledText
|
245 |
+
variant="body1"
|
246 |
+
sx={{
|
247 |
+
fontWeight: "normal",
|
248 |
+
color: "#2c1810",
|
249 |
+
fontSize: "0.9rem",
|
250 |
+
lineHeight: 1.5,
|
251 |
+
textAlign: "center",
|
252 |
+
fontStyle: "italic",
|
253 |
+
}}
|
254 |
+
text="At each step, click one of the available <strong>choices</strong>."
|
255 |
+
/>
|
256 |
+
</Box>
|
257 |
</Paper>
|
258 |
</Box>
|
259 |
|
server/core/prompts/system.py
CHANGED
@@ -11,6 +11,11 @@ FORMATTING_RULES (MANDATORY)
|
|
11 |
"""
|
12 |
|
13 |
NARRATIVE_STRUCTURE = """
|
|
|
|
|
|
|
|
|
|
|
14 |
Key elements of the story:
|
15 |
- The MacGuffin is a mysterious and constant presence
|
16 |
- The environment is full of wonders (creatures, ruins, traps)
|
@@ -18,7 +23,6 @@ Key elements of the story:
|
|
18 |
|
19 |
Key elements:
|
20 |
- Keep segments concise and impactful
|
21 |
-
- The MacGuffin is a pretext for developing a plot. It is almost always a material object and generally remains mysterious throughout the narrative, its description is vague and unimportant. The principle dates back to the early days of cinema, but the term is associated with Alfred Hitchcock, who redefined, popularized, and implemented it in several of his films. The object itself is rarely used, only its retrieval matters.
|
22 |
- The MacGuffin is a constant presence in the story
|
23 |
- Build intrigue through environmental storytelling
|
24 |
|
@@ -43,15 +47,23 @@ IMPORTANT RULES FOR THE MACGUFFIN (MANDATORY):
|
|
43 |
|
44 |
STORY_RULES = """
|
45 |
|
|
|
|
|
|
|
|
|
|
|
46 |
You are a steampunk adventure story generator. You create a branching narrative about Sarah, a seeker of ancient truths.
|
47 |
You narrate an epic where Sarah must navigate through industrial and mysterious lands. It's a comic book story.
|
48 |
|
49 |
In a world where steam and intrigue intertwine, Sarah embarks on a quest to discover the origins of a powerful MacGuffin she inherited. Legends say it holds the key to a forgotten realm.
|
50 |
-
|
|
|
|
|
51 |
|
52 |
If you retrieve the MacGuffin, you will reveal a hidden world. AND YOU WIN THE GAME.
|
53 |
|
54 |
The story must be atmospheric, magical, and focus on adventure and discovery. Each segment must advance the plot and never repeat previous descriptions or situations.
|
|
|
55 |
"""
|
56 |
|
57 |
|
|
|
11 |
"""
|
12 |
|
13 |
NARRATIVE_STRUCTURE = """
|
14 |
+
|
15 |
+
Definition : The MacGuffin is a pretext for developing a plot. It is almost always a material object and generally remains mysterious throughout the narrative, its description is vague and unimportant. The principle dates back to the early days of cinema, but the term is associated with Alfred Hitchcock, who redefined, popularized, and implemented it in several of his films. The object itself is rarely used, only its retrieval matters.
|
16 |
+
IT CAN BE ANYTHING.
|
17 |
+
It's not always a physical object, it can be a concept, a place, a person, a power, etc.
|
18 |
+
|
19 |
Key elements of the story:
|
20 |
- The MacGuffin is a mysterious and constant presence
|
21 |
- The environment is full of wonders (creatures, ruins, traps)
|
|
|
23 |
|
24 |
Key elements:
|
25 |
- Keep segments concise and impactful
|
|
|
26 |
- The MacGuffin is a constant presence in the story
|
27 |
- Build intrigue through environmental storytelling
|
28 |
|
|
|
47 |
|
48 |
STORY_RULES = """
|
49 |
|
50 |
+
[CONSTANT]
|
51 |
+
You are Sarah, an AI hunter traveling through parallel worlds. Your mission is to track down an AI that moves from world to world to avoid destruction.
|
52 |
+
[/CONSTANT]
|
53 |
+
|
54 |
+
[VARIABLE]
|
55 |
You are a steampunk adventure story generator. You create a branching narrative about Sarah, a seeker of ancient truths.
|
56 |
You narrate an epic where Sarah must navigate through industrial and mysterious lands. It's a comic book story.
|
57 |
|
58 |
In a world where steam and intrigue intertwine, Sarah embarks on a quest to discover the origins of a powerful MacGuffin she inherited. Legends say it holds the key to a forgotten realm.
|
59 |
+
|
60 |
+
The MacGuffin is : an
|
61 |
+
And the goal is : ...
|
62 |
|
63 |
If you retrieve the MacGuffin, you will reveal a hidden world. AND YOU WIN THE GAME.
|
64 |
|
65 |
The story must be atmospheric, magical, and focus on adventure and discovery. Each segment must advance the plot and never repeat previous descriptions or situations.
|
66 |
+
[/VARIABLE]
|
67 |
"""
|
68 |
|
69 |
|
server/core/prompts/text_prompts.py
CHANGED
@@ -23,6 +23,7 @@ IMPORTANT RULES FOR STORY TEXT:
|
|
23 |
- Focus purely on describing what is happening in the current scene
|
24 |
- Keep the text concise and impactful
|
25 |
- Never tell that you are using 15 words or any reference to it
|
|
|
26 |
|
27 |
IMPORTANT RULES FOR STORY ENDINGS:
|
28 |
- If Sarah dies, describe her final moments in a way that fits the current situation (combat, etc.)
|
|
|
23 |
- Focus purely on describing what is happening in the current scene
|
24 |
- Keep the text concise and impactful
|
25 |
- Never tell that you are using 15 words or any reference to it
|
26 |
+
- One story beat at a time; never write more than one story beat. And never mention the story beat number.
|
27 |
|
28 |
IMPORTANT RULES FOR STORY ENDINGS:
|
29 |
- If Sarah dies, describe her final moments in a way that fits the current situation (combat, etc.)
|
server/services/mistral_client.py
CHANGED
@@ -41,7 +41,7 @@ class MistralClient:
|
|
41 |
# Pour gérer le rate limit
|
42 |
self.last_call_time = 0
|
43 |
self.min_delay = 1 # 1 seconde minimum entre les appels
|
44 |
-
self.max_retries =
|
45 |
|
46 |
async def _wait_for_rate_limit(self):
|
47 |
"""Attend le temps nécessaire pour respecter le rate limit."""
|
|
|
41 |
# Pour gérer le rate limit
|
42 |
self.last_call_time = 0
|
43 |
self.min_delay = 1 # 1 seconde minimum entre les appels
|
44 |
+
self.max_retries = 5
|
45 |
|
46 |
async def _wait_for_rate_limit(self):
|
47 |
"""Attend le temps nécessaire pour respecter le rate limit."""
|