AI Image Studio Phase 1
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography, Chip, Button } from '@mui/material';
|
||||
import { controlAssets } from '../constants';
|
||||
|
||||
export const ControlEffectPreview: React.FC = () => {
|
||||
const [videoKey, setVideoKey] = React.useState(0);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
background: 'rgba(15,23,42,0.5)',
|
||||
p: { xs: 2, md: 3 },
|
||||
}}
|
||||
>
|
||||
<Stack direction={{ xs: 'column', md: 'row' }} spacing={2} alignItems="stretch">
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.12)',
|
||||
background: 'linear-gradient(135deg,#8b5cf6,#a855f7)',
|
||||
color: '#f3e8ff',
|
||||
p: 2,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1.5,
|
||||
}}
|
||||
>
|
||||
<Typography variant="overline" sx={{ letterSpacing: 2, color: '#e9d5ff' }}>
|
||||
Control Input
|
||||
</Typography>
|
||||
<Box
|
||||
component="img"
|
||||
src={controlAssets.inputImage}
|
||||
alt="Control reference"
|
||||
sx={{ width: '100%', borderRadius: 2, border: '2px solid rgba(255,255,255,0.2)', boxShadow: '0 10px 25px rgba(139,92,246,0.3)' }}
|
||||
/>
|
||||
<Stack spacing={1}>
|
||||
<Typography variant="caption" sx={{ color: '#e9d5ff', fontWeight: 600 }}>
|
||||
Prompt
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ fontSize: '0.875rem', lineHeight: 1.5 }}>
|
||||
{controlAssets.prompt}
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap">
|
||||
<Chip
|
||||
size="small"
|
||||
label={`Seed ${controlAssets.seed}`}
|
||||
sx={{ background: 'rgba(255,255,255,0.2)', color: '#0f172a', borderRadius: 999 }}
|
||||
/>
|
||||
<Chip
|
||||
size="small"
|
||||
label={controlAssets.resolution}
|
||||
sx={{ background: 'rgba(255,255,255,0.2)', color: '#0f172a', borderRadius: 999 }}
|
||||
/>
|
||||
<Chip
|
||||
size="small"
|
||||
label={`${controlAssets.duration}s`}
|
||||
sx={{ background: 'rgba(255,255,255,0.2)', color: '#0f172a', borderRadius: 999 }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1.5,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.12)',
|
||||
background: '#020617',
|
||||
p: { xs: 1, md: 2 },
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1 }}>
|
||||
<Typography variant="overline" sx={{ letterSpacing: 2, color: '#a78bfa' }}>
|
||||
Generated Output
|
||||
</Typography>
|
||||
<Chip
|
||||
label="WAN 2.5"
|
||||
size="small"
|
||||
sx={{ background: 'rgba(167,139,250,0.15)', color: '#a78bfa', borderRadius: 999 }}
|
||||
/>
|
||||
<Button size="small" onClick={() => setVideoKey(prev => prev + 1)} sx={{ ml: 'auto', color: '#a78bfa', textTransform: 'none' }}>
|
||||
Reset preview
|
||||
</Button>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
overflow: 'hidden',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<video key={videoKey} controls poster={controlAssets.inputImage} style={{ width: '100%', display: 'block' }} src={controlAssets.outputVideo} />
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
left: 12,
|
||||
background: 'rgba(15,23,42,0.7)',
|
||||
borderRadius: 999,
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
color: '#f8fafc',
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
Voiceover · {controlAssets.duration}s
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1.5 }}>
|
||||
Alibaba WAN 2.5 converts text or images into videos (480p/720p/1080p) with synced audio, faster and more affordable than Google Veo3.
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography, Chip } from '@mui/material';
|
||||
import { createExamples } from '../constants';
|
||||
|
||||
export const CreateEffectPreview: React.FC = () => {
|
||||
const [textHovered, setTextHovered] = React.useState(false);
|
||||
const [exampleIndex, setExampleIndex] = React.useState(0);
|
||||
const example = createExamples[exampleIndex];
|
||||
const imageWidth = textHovered ? '20%' : '70%';
|
||||
const textWidth = textHovered ? '80%' : '30%';
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
border: '3px solid',
|
||||
borderImage:
|
||||
'linear-gradient(135deg, rgba(124,58,237,0.8), rgba(14,165,233,0.8), rgba(16,185,129,0.8)) 1',
|
||||
overflow: 'hidden',
|
||||
height: { xs: 240, md: 280 },
|
||||
display: 'flex',
|
||||
background: '#0f172a',
|
||||
mt: 1,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
flex: '0 0 auto',
|
||||
width: imageWidth,
|
||||
transition: 'width 0.4s ease, filter 0.4s ease',
|
||||
backgroundImage: `url(${example.image})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
filter: textHovered ? 'saturate(1.1)' : 'saturate(1)',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 16,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
background: 'rgba(15,23,42,0.8)',
|
||||
borderRadius: 999,
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
boxShadow: '0 10px 20px rgba(2,6,23,0.45)',
|
||||
}}
|
||||
>
|
||||
{createExamples.map((_, idx) => (
|
||||
<Box
|
||||
key={_.id}
|
||||
onClick={() => setExampleIndex(idx)}
|
||||
sx={{
|
||||
width: 32,
|
||||
height: 10,
|
||||
borderRadius: 999,
|
||||
background: idx === exampleIndex ? '#c4b5fd' : 'rgba(255,255,255,0.3)',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.2s ease',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
flex: '0 0 auto',
|
||||
width: textWidth,
|
||||
background: 'rgba(248,250,252,0.95)',
|
||||
color: '#0f172a',
|
||||
p: 3,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
boxShadow: '-12px 0 24px rgba(15,23,42,0.25)',
|
||||
transition: 'width 0.4s ease',
|
||||
}}
|
||||
onMouseEnter={() => setTextHovered(true)}
|
||||
onMouseLeave={() => setTextHovered(false)}
|
||||
>
|
||||
<Stack spacing={0.5} sx={{ overflowY: textHovered ? 'auto' : 'hidden', pr: 1 }}>
|
||||
<Typography variant="overline" sx={{ letterSpacing: 1.5, color: '#818cf8' }}>
|
||||
{example.label}
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" fontWeight={700}>
|
||||
Prompt
|
||||
</Typography>
|
||||
<Typography variant="body2">{example.prompt}</Typography>
|
||||
<Typography variant="body2">{example.description}</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap">
|
||||
<Chip
|
||||
size="small"
|
||||
label={`Price ${example.price}`}
|
||||
sx={{ background: '#ede9fe', color: '#4c1d95', borderRadius: 999, fontWeight: 600 }}
|
||||
/>
|
||||
<Chip
|
||||
size="small"
|
||||
label={`Turnaround ${example.eta}`}
|
||||
sx={{ background: '#cffafe', color: '#0f766e', borderRadius: 999, fontWeight: 600 }}
|
||||
/>
|
||||
<Chip
|
||||
size="small"
|
||||
label={example.provider}
|
||||
sx={{ background: '#dcfce7', color: '#166534', borderRadius: 999, fontWeight: 600 }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography, Chip, Tooltip } from '@mui/material';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
import { editBeforeAfter } from '../constants';
|
||||
|
||||
export const EditEffectPreview: React.FC = () => {
|
||||
const [exampleIndex, setExampleIndex] = React.useState(0);
|
||||
const pair = editBeforeAfter[exampleIndex];
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
background: 'rgba(15,23,42,0.5)',
|
||||
p: 2,
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 2 }}>
|
||||
<Typography variant="overline" sx={{ color: '#fcd34d', letterSpacing: 2 }}>
|
||||
Before → After
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title={
|
||||
<Stack spacing={1}>
|
||||
<Typography variant="body2">
|
||||
Hover to reveal the original upload vs. AI-edited output. Perfect for showing background swaps,
|
||||
inpainting, or relighting.
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap">
|
||||
{['Erase objects cleanly', 'Smart relight', 'Replace backgrounds'].map(label => (
|
||||
<Chip
|
||||
key={label}
|
||||
size="small"
|
||||
label={label}
|
||||
sx={{ background: 'rgba(236,252,203,0.12)', color: '#fef3c7', borderRadius: 999 }}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<InfoOutlinedIcon sx={{ fontSize: 18, color: 'rgba(252,211,77,0.85)', cursor: 'pointer' }} />
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
'--gap': '8px',
|
||||
display: 'grid',
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
borderRadius: 3,
|
||||
overflow: 'hidden',
|
||||
border: '4px solid #22d3ee',
|
||||
minHeight: { xs: 260, md: 300 },
|
||||
'& > img': {
|
||||
'--progress': 'calc(-1 * var(--gap))',
|
||||
gridArea: '1 / 1',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
transition: 'clip-path 0.4s 0.1s',
|
||||
},
|
||||
'& > img:first-of-type': {
|
||||
clipPath: 'polygon(0 0, calc(100% + var(--progress)) 0, 0 calc(100% + var(--progress)))',
|
||||
},
|
||||
'& > img:last-of-type': {
|
||||
clipPath: 'polygon(100% 100%, 100% calc(0% - var(--progress)), calc(0% - var(--progress)) 100%)',
|
||||
},
|
||||
'&:hover > img:last-of-type, &:hover > img:first-of-type:hover': {
|
||||
'--progress': 'calc(50% - var(--gap))',
|
||||
},
|
||||
'&:hover > img:first-of-type, &:hover > img:first-of-type:hover + img': {
|
||||
'--progress': 'calc(-50% - var(--gap))',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box component="img" src={pair.before} alt="Original asset" />
|
||||
<Box component="img" src={pair.after} alt="Edited asset" />
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
right: 12,
|
||||
background: 'rgba(15,23,42,0.8)',
|
||||
borderRadius: 999,
|
||||
px: 1,
|
||||
py: 0.5,
|
||||
boxShadow: '0 10px 20px rgba(2,6,23,0.5)',
|
||||
}}
|
||||
>
|
||||
{editBeforeAfter.map((_, idx) => (
|
||||
<Box
|
||||
key={idx}
|
||||
onClick={() => setExampleIndex(idx)}
|
||||
sx={{
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: '50%',
|
||||
background: idx === exampleIndex ? '#f472b6' : 'rgba(255,255,255,0.4)',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 12,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
background: 'rgba(15,23,42,0.85)',
|
||||
px: 1.5,
|
||||
py: 0.75,
|
||||
borderRadius: 999,
|
||||
boxShadow: '0 10px 25px rgba(2,6,23,0.6)',
|
||||
}}
|
||||
>
|
||||
<Chip label="Original" size="small" sx={{ background: '#fef3c7', color: '#78350f', fontWeight: 600 }} />
|
||||
<Chip label="Edited" size="small" sx={{ background: '#a5b4fc', color: '#1e1b4b', fontWeight: 600 }} />
|
||||
</Stack>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1.5 }}>
|
||||
Prompt used: {pair.prompt}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography, Chip } from '@mui/material';
|
||||
import { transformAssets, platformPresets } from '../constants';
|
||||
|
||||
export const SocialOptimizerEffectPreview: React.FC = () => (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
background: 'rgba(15,23,42,0.5)',
|
||||
p: { xs: 2, md: 3 },
|
||||
}}
|
||||
>
|
||||
<Typography variant="overline" sx={{ letterSpacing: 2, color: '#fcd34d' }}>
|
||||
Platform Auto-Crop
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Smart resize finds the focal point and generates safe-zone aware crops for every surface.
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.1)',
|
||||
background: '#020617',
|
||||
p: 2,
|
||||
position: 'relative',
|
||||
minHeight: 280,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="img"
|
||||
src={transformAssets.storyboard}
|
||||
alt="Source creative"
|
||||
sx={{ width: '100%', height: '100%', objectFit: 'cover', borderRadius: 2, filter: 'brightness(0.8)' }}
|
||||
/>
|
||||
{platformPresets.map(frame => (
|
||||
<Box
|
||||
key={frame.label}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
border: '2px solid rgba(248,250,252,0.8)',
|
||||
borderRadius: 2,
|
||||
boxShadow: '0 10px 20px rgba(2,6,23,0.45)',
|
||||
transition: 'transform 0.2s ease',
|
||||
cursor: 'pointer',
|
||||
'&:hover': { transform: 'scale(1.05)' },
|
||||
...frame,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: -24,
|
||||
left: 0,
|
||||
background: 'rgba(15,23,42,0.85)',
|
||||
color: '#f8fafc',
|
||||
px: 1,
|
||||
py: 0.25,
|
||||
borderRadius: 999,
|
||||
fontSize: 11,
|
||||
}}
|
||||
>
|
||||
{frame.label}
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap" sx={{ mt: 2 }}>
|
||||
{['Safe zones', 'Focal cropping', 'Batch export'].map(label => (
|
||||
<Chip key={label} size="small" label={label} sx={{ background: 'rgba(15,118,110,0.2)', color: '#5eead4', borderRadius: 999 }} />
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography, Chip, Button } from '@mui/material';
|
||||
import { transformAssets } from '../constants';
|
||||
|
||||
export const TransformEffectPreview: React.FC = () => {
|
||||
const [videoKey, setVideoKey] = React.useState(0);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
background: 'rgba(15,23,42,0.5)',
|
||||
p: { xs: 2, md: 3 },
|
||||
}}
|
||||
>
|
||||
<Stack direction={{ xs: 'column', md: 'row' }} spacing={2} alignItems="stretch">
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.12)',
|
||||
background: 'linear-gradient(135deg,#0ea5e9,#6366f1)',
|
||||
color: '#e0f2fe',
|
||||
p: 2,
|
||||
minHeight: 260,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1.2,
|
||||
}}
|
||||
>
|
||||
<Typography variant="overline" sx={{ letterSpacing: 2, color: '#cffafe' }}>
|
||||
Storyboard Prompt
|
||||
</Typography>
|
||||
<Typography variant="body2">{transformAssets.script}</Typography>
|
||||
<Stack direction="row" spacing={1} flexWrap="wrap">
|
||||
{['Image-to-video', 'WAN 2.5', '10s duration'].map(label => (
|
||||
<Chip
|
||||
key={label}
|
||||
size="small"
|
||||
label={label}
|
||||
sx={{ background: 'rgba(255,255,255,0.2)', color: '#0f172a', borderRadius: 999 }}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
mt: 'auto',
|
||||
borderRadius: 2,
|
||||
overflow: 'hidden',
|
||||
border: '1px solid rgba(255,255,255,0.25)',
|
||||
boxShadow: '0 20px 45px rgba(2,6,23,0.45)',
|
||||
}}
|
||||
>
|
||||
<Box component="img" src={transformAssets.storyboard} alt="Storyboard still" sx={{ width: '100%', display: 'block' }} />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1.5,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.12)',
|
||||
background: '#020617',
|
||||
p: { xs: 1, md: 2 },
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1 }}>
|
||||
<Typography variant="overline" sx={{ letterSpacing: 2, color: '#38bdf8' }}>
|
||||
Render Preview
|
||||
</Typography>
|
||||
<Chip
|
||||
label="1080p"
|
||||
size="small"
|
||||
sx={{ background: 'rgba(56,189,248,0.15)', color: '#38bdf8', borderRadius: 999 }}
|
||||
/>
|
||||
<Button size="small" onClick={() => setVideoKey(prev => prev + 1)} sx={{ ml: 'auto', color: '#38bdf8', textTransform: 'none' }}>
|
||||
Reset preview
|
||||
</Button>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
overflow: 'hidden',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<video key={videoKey} controls poster={transformAssets.storyboard} style={{ width: '100%', display: 'block' }} src={transformAssets.video} />
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
left: 12,
|
||||
background: 'rgba(15,23,42,0.7)',
|
||||
borderRadius: 999,
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
color: '#f8fafc',
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
Scene 1 · Cloud Kitchen
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1.5 }}>
|
||||
Convert hero images into narrated clips with motion presets, subtitles, and audio uploads.
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography, Chip } from '@mui/material';
|
||||
import { upscaleSamples } from '../constants';
|
||||
|
||||
export const UpscaleEffectPreview: React.FC = () => (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
background: 'rgba(15,23,42,0.5)',
|
||||
p: { xs: 2, md: 3 },
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" spacing={1} alignItems="center" justifyContent="space-between">
|
||||
<Box>
|
||||
<Typography variant="overline" sx={{ letterSpacing: 2, color: '#f9a8d4' }}>
|
||||
4× Upscale Showcase
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Flip the panels to compare the low-res upload with the 4K-ready output.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Chip
|
||||
label="Fast vs Creative"
|
||||
size="small"
|
||||
sx={{ background: 'rgba(236,72,153,0.15)', color: '#f9a8d4', borderRadius: 999 }}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
mt: 2,
|
||||
display: 'flex',
|
||||
gap: 2,
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
'&:hover .flip-left': { transform: 'rotateY(-180deg)' },
|
||||
'&:hover .flip-right': { transform: 'rotateY(180deg)' },
|
||||
}}
|
||||
>
|
||||
{[
|
||||
{ key: 'low', label: 'Before 600×400', className: 'flip-left', image: upscaleSamples.lowRes },
|
||||
{ key: 'high', label: 'After 2400×1600', className: 'flip-right', image: upscaleSamples.hiRes },
|
||||
].map(card => (
|
||||
<Box key={card.key} sx={{ perspective: 1000, width: { xs: 140, sm: 180 }, height: { xs: 200, sm: 240 } }}>
|
||||
<Box
|
||||
className={card.className}
|
||||
sx={{ position: 'relative', width: '100%', height: '100%', transition: '0.6s', transformStyle: 'preserve-3d' }}
|
||||
>
|
||||
<Box
|
||||
className="front"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
backfaceVisibility: 'hidden',
|
||||
borderRadius: 3,
|
||||
border: '1px solid rgba(255,255,255,0.1)',
|
||||
background:
|
||||
card.key === 'low'
|
||||
? 'linear-gradient(135deg,#4c1d95,#9333ea)'
|
||||
: 'linear-gradient(135deg,#0f766e,#14b8a6)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: card.key === 'low' ? 'flex-end' : 'flex-start',
|
||||
px: 2,
|
||||
color: '#fff',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: '50%',
|
||||
background: 'rgba(255,255,255,0.2)',
|
||||
border: '2px solid rgba(255,255,255,0.8)',
|
||||
}}
|
||||
/>
|
||||
<Typography variant="caption" sx={{ ml: card.key === 'low' ? -6 : 2, mr: card.key === 'low' ? 2 : -6 }}>
|
||||
{card.label}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
className="back"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
backfaceVisibility: 'hidden',
|
||||
borderRadius: 3,
|
||||
transform: 'rotateY(180deg)',
|
||||
overflow: 'hidden',
|
||||
border: '1px solid rgba(255,255,255,0.15)',
|
||||
}}
|
||||
>
|
||||
<Box component="img" src={card.image} alt={card.label} sx={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1.5 }}>
|
||||
Try creative upscaling for texture enhancement, or fast mode for previews.
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export { CreateEffectPreview } from './CreateEffectPreview';
|
||||
export { EditEffectPreview } from './EditEffectPreview';
|
||||
export { UpscaleEffectPreview } from './UpscaleEffectPreview';
|
||||
export { TransformEffectPreview } from './TransformEffectPreview';
|
||||
export { SocialOptimizerEffectPreview } from './SocialOptimizerEffectPreview';
|
||||
export { ControlEffectPreview } from './ControlEffectPreview';
|
||||
|
||||
Reference in New Issue
Block a user