File size: 5,734 Bytes
1e8f4c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import React, { useState, useEffect } from "react";
import {
  Box,
  Grid,
  Card,
  CardContent,
  Typography,
  CircularProgress,
  Chip,
  Stack,
} from "@mui/material";
import {
  Palette as PaletteIcon,
  Person as PersonIcon,
  Category as CategoryIcon,
  AccessTime as AccessTimeIcon,
} from "@mui/icons-material";
import { storyApi, universeApi } from "../utils/api";

const UniverseCard = ({ universe, imagePrompt }) => {
  const [imageUrl, setImageUrl] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const generateImage = async () => {
      try {
        const result = await storyApi.generateImage(imagePrompt, 512, 512);
        if (result && result.success) {
          setImageUrl(`data:image/png;base64,${result.image_base64}`);
        }
      } catch (error) {
        console.error("Error generating image:", error);
      } finally {
        setIsLoading(false);
      }
    };

    generateImage();
  }, [imagePrompt]);

  return (
    <Card sx={{ height: "100%", display: "flex", flexDirection: "column" }}>
      <Box sx={{ position: "relative", paddingTop: "100%" }}>
        {isLoading ? (
          <Box
            sx={{
              position: "absolute",
              top: 0,
              left: 0,
              right: 0,
              bottom: 0,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              backgroundColor: "rgba(0, 0, 0, 0.1)",
            }}
          >
            <CircularProgress />
          </Box>
        ) : (
          <Box
            component="img"
            src={imageUrl}
            alt="Universe preview"
            sx={{
              position: "absolute",
              top: 0,
              left: 0,
              width: "100%",
              height: "100%",
              objectFit: "cover",
            }}
          />
        )}
      </Box>
      <CardContent sx={{ py: 1.5, px: 2, "&:last-child": { pb: 1.5 } }}>
        <Stack spacing={0.5}>
          <Stack direction="row" spacing={1} alignItems="center">
            <PaletteIcon fontSize="small" color="primary" />
            <Typography variant="subtitle2" sx={{ fontSize: "0.875rem" }}>
              {universe.style.name}
            </Typography>
          </Stack>
          {universe.style.selected_artist && (
            <Stack direction="row" spacing={1} alignItems="center">
              <PersonIcon fontSize="small" color="primary" />
              <Typography variant="body2" sx={{ fontSize: "0.8rem" }}>
                {universe.style.selected_artist}
              </Typography>
            </Stack>
          )}
          <Stack direction="row" spacing={1} alignItems="center">
            <CategoryIcon fontSize="small" color="primary" />
            <Typography variant="body2" sx={{ fontSize: "0.8rem" }}>
              {universe.genre}
            </Typography>
          </Stack>
          <Stack direction="row" spacing={1} alignItems="center">
            <AccessTimeIcon fontSize="small" color="primary" />
            <Typography variant="body2" sx={{ fontSize: "0.8rem" }}>
              {universe.epoch}
            </Typography>
          </Stack>
        </Stack>
      </CardContent>
    </Card>
  );
};

export function Universe() {
  const [universes, setUniverses] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const generateUniverses = async () => {
      try {
        const generatedUniverses = await Promise.all(
          Array(6)
            .fill()
            .map(async () => {
              const universe = await universeApi.generate();
              return {
                ...universe,
                imagePrompt: `${
                  universe.style.selected_artist ||
                  universe.style.references[0].artist
                } style, epic wide shot of a detailed scene -- A dramatic establishing shot of a ${universe.genre.toLowerCase()} world in ${
                  universe.epoch
                }, with rich atmosphere and dynamic composition. The scene should reflect the essence of ${
                  universe.style.name
                } visual style, with appropriate lighting and mood. In the scene, Sarah is a young woman in her late twenties with short dark hair, wearing a mysterious amulet around her neck. Her blue eyes hide untold secrets.`,
              };
            })
        );
        setUniverses(generatedUniverses);
      } catch (error) {
        console.error("Error generating universes:", error);
      } finally {
        setIsLoading(false);
      }
    };

    generateUniverses();
  }, []);

  if (isLoading) {
    return (
      <Box
        sx={{
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          minHeight: "100vh",
        }}
      >
        <CircularProgress />
      </Box>
    );
  }

  return (
    <Box
      sx={{
        height: "100vh",
        display: "flex",
        flexDirection: "column",
        backgroundColor: "background.default",
      }}
    >
      <Box sx={{ p: 3, pb: 2 }}>
        <Typography variant="h4">Univers Parallèles</Typography>
      </Box>

      <Box
        sx={{
          flex: 1,
          overflow: "auto",
          px: 3,
          pb: 3,
        }}
      >
        <Grid container spacing={3}>
          {universes.map((universe, index) => (
            <Grid item xs={12} sm={6} md={4} key={index}>
              <UniverseCard
                universe={universe}
                imagePrompt={universe.imagePrompt}
              />
            </Grid>
          ))}
        </Grid>
      </Box>
    </Box>
  );
}

export default Universe;