Ankit commited on
Commit ·
c476ef5
1
Parent(s): daa6294
application files
Browse files- .gitattributes +1 -35
- README.md +0 -12
- app.py +1 -0
- backend/main.py +224 -0
- frontend/build/examples/img1.png +3 -0
- frontend/build/examples/img2.png +3 -0
- frontend/build/examples/img3.png +3 -0
- frontend/build/examples/img4.png +3 -0
- frontend/build/examples/img5.png +3 -0
- frontend/build/examples/img6.png +3 -0
- frontend/build/index.html +110 -0
- frontend/build/script.js +487 -0
- frontend/build/style.css +626 -0
- requirements.txt +9 -0
.gitattributes
CHANGED
|
@@ -1,35 +1 @@
|
|
| 1 |
-
*.
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
frontend/build/examples/*.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
DELETED
|
@@ -1,12 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Wallgen
|
| 3 |
-
emoji: 📈
|
| 4 |
-
colorFrom: pink
|
| 5 |
-
colorTo: purple
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
license: other
|
| 9 |
-
short_description: wallpaper generation using diffusion models
|
| 10 |
-
---
|
| 11 |
-
|
| 12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from backend.main import app
|
backend/main.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from fastapi.responses import JSONResponse
|
| 3 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 4 |
+
from pydantic import BaseModel
|
| 5 |
+
import torch
|
| 6 |
+
import uvicorn
|
| 7 |
+
from fastapi.staticfiles import StaticFiles
|
| 8 |
+
from diffusers import StableDiffusionPipeline, LCMScheduler
|
| 9 |
+
from PIL import Image
|
| 10 |
+
import numpy as np
|
| 11 |
+
from sklearn.cluster import KMeans
|
| 12 |
+
import random
|
| 13 |
+
import io
|
| 14 |
+
import base64
|
| 15 |
+
from typing import Optional
|
| 16 |
+
import os
|
| 17 |
+
from fastapi.staticfiles import StaticFiles
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
print("Model will load on first request...")
|
| 22 |
+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
| 23 |
+
# Force CPU in Docker container for consistency
|
| 24 |
+
device = "cpu" if os.getenv("DOCKER_CONTAINER", False) else ("cuda" if torch.cuda.is_available() else "cpu")
|
| 25 |
+
MODEL_CACHE_DIR = os.getenv("MODEL_CACHE_DIR", os.path.abspath(os.path.join(BASE_DIR, "..", "models")))
|
| 26 |
+
pipe = None # lazy-loaded on first generation request
|
| 27 |
+
|
| 28 |
+
def load_pipeline():
|
| 29 |
+
global pipe
|
| 30 |
+
if pipe is not None:
|
| 31 |
+
return pipe
|
| 32 |
+
|
| 33 |
+
print("Loading model into memory...")
|
| 34 |
+
local_device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 35 |
+
|
| 36 |
+
# Directly load from Hugging Face Hub, safetensors by default (no .bin)
|
| 37 |
+
p = StableDiffusionPipeline.from_pretrained(
|
| 38 |
+
"SimianLuo/LCM_Dreamshaper_v7",
|
| 39 |
+
torch_dtype=torch.float16,
|
| 40 |
+
safety_checker=None,
|
| 41 |
+
requires_safety_checker=False,
|
| 42 |
+
low_cpu_mem_usage=True,
|
| 43 |
+
# cache_sir = "../models"
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
p.scheduler = LCMScheduler.from_config(p.scheduler.config)
|
| 47 |
+
p = p.to(local_device)
|
| 48 |
+
if local_device == "cuda":
|
| 49 |
+
p.enable_model_cpu_offload()
|
| 50 |
+
else:
|
| 51 |
+
p.enable_attention_slicing()
|
| 52 |
+
pipe = p
|
| 53 |
+
print("Model has been loaded successfully!")
|
| 54 |
+
return pipe
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def extract_colors(image: Image.Image, num_colors=6):
|
| 58 |
+
img_array = np.array(image)
|
| 59 |
+
pixels = img_array.reshape(-1, 3)
|
| 60 |
+
kmeans = KMeans(n_clusters=num_colors, random_state=42, n_init=10)
|
| 61 |
+
kmeans.fit(pixels)
|
| 62 |
+
colors = kmeans.cluster_centers_.astype(int)
|
| 63 |
+
hex_colors = ['#{:02x}{:02x}{:02x}'.format(r, g, b) for r, g, b in colors]
|
| 64 |
+
return hex_colors
|
| 65 |
+
|
| 66 |
+
def resize_image(image: Image.Image, width: int, height: int):
|
| 67 |
+
return image.resize((width, height), Image.Resampling.LANCZOS)
|
| 68 |
+
|
| 69 |
+
app = FastAPI()
|
| 70 |
+
app.mount("/", StaticFiles(directory="../frontend/build", html=True), name="static")
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
app.add_middleware(
|
| 74 |
+
CORSMiddleware,
|
| 75 |
+
allow_origins=["*"],
|
| 76 |
+
allow_credentials=True,
|
| 77 |
+
allow_methods=["*"],
|
| 78 |
+
allow_headers=["*"],
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
class GenerationRequest(BaseModel):
|
| 82 |
+
style: str = "Gradient"
|
| 83 |
+
seed: int = 42
|
| 84 |
+
resolution: str = "Desktop (1920x1080)"
|
| 85 |
+
steps: int = 4
|
| 86 |
+
color: Optional[str] = None
|
| 87 |
+
|
| 88 |
+
@app.post("/generate-wallpaper/")
|
| 89 |
+
async def generate_wallpaper_endpoint(request: GenerationRequest):
|
| 90 |
+
prompts = {
|
| 91 |
+
"Geometric": "sharp geometric patterns, triangular tessellations, hexagonal grids, precise angular shapes, colorful polygon arrangements, geometric mandala designs, crystalline symmetric patterns, abstract mathematical forms, vector art precision, clean geometric compositions, bright geometric color blocks, structured pattern design, 8k wallpaper",
|
| 92 |
+
|
| 93 |
+
"Organic": "flowing organic forms, silky fluid textures, soft ethereal lighting, high-detail depth, oil painting feel, zen abstract design, bioluminescent flowing lines, translucent silk ribbons, watercolor blending, natural formations",
|
| 94 |
+
|
| 95 |
+
"Vibrant": "futuristic neon shapes, cyberpunk color palette, dark background, glowing fractals, high contrast, vivid reflections, neon geometric patterns, electric plasma effects, holographic surfaces, laser light trails, ultra bright colors",
|
| 96 |
+
|
| 97 |
+
"Minimal": "watercolor drops on white canvas, circular water color spots, bright colored water drops spreading on paper, wet watercolor bleeding circles, vibrant color drops with soft edges, colorful water stains on clean background, translucent watercolor circles, artistic water drops pattern, high contrast bright drops, clean minimalist watercolor design",
|
| 98 |
+
|
| 99 |
+
"Ink Flow": "flowing liquid ink patterns, organic ink bleeds, watercolor paint flows, dynamic brush strokes, fluid color transitions, ink spreading on wet paper, abstract paint movements, colorful ink drops merging, artistic liquid patterns, paint flow dynamics",
|
| 100 |
+
|
| 101 |
+
"Gradient": "smooth color gradients, organic flowing waves, ethereal abstract patterns, bright vivid color transitions, watercolor style blending, soft gradient meshes, flowing energy patterns, color field painting, seamless color flows, high resolution gradients",
|
| 102 |
+
|
| 103 |
+
"Crystal": "high-resolution crystal surfaces, prism reflections, iridescent colors, shattered glass texture, futuristic diamond shapes, cinematic lighting, dichroic glass effects, crystalline structures, refracting light patterns",
|
| 104 |
+
|
| 105 |
+
"Retro Wave": "synthwave aesthetic, neon grid lines, 1980s retro futuristic, purple pink gradients, chrome reflections, vintage synthesizer vibes, outrun highway aesthetic, neon palm trees, retrowave sunset",
|
| 106 |
+
|
| 107 |
+
"Botanical": "botanical illustrations, detailed leaves and flowers, vintage naturalist style, earth tones, Art Nouveau vine patterns, organic plant geometry, natural forms, herbarium specimens, botanical cross-sections",
|
| 108 |
+
|
| 109 |
+
"Space Cosmic": "deep space nebula, cosmic dust clouds, distant galaxies, purple blue cosmic colors, stellar formations, aurora borealis patterns, cosmic energy flows, nebula formations, space photography aesthetic",
|
| 110 |
+
|
| 111 |
+
"Psychedelic": "trippy psychedelic patterns, kaleidoscope effects, vibrant swirling colors, mind-bending fractals, mandala patterns, liquid light shows, synesthetic color flows, geometric portals, infinite pattern recursion",
|
| 112 |
+
|
| 113 |
+
"Industrial": "metal textures, rust patterns, industrial materials, concrete and steel aesthetics, weathered surfaces, industrial photography, high contrast lighting, urban decay patterns, metallic reflections",
|
| 114 |
+
|
| 115 |
+
"Fractal": "complex fractal spirals, infinite zoom illusion, glowing edges, depth of field blur, mathematical precision, cosmic art, Mandelbrot-inspired structure"
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
resolutions = {
|
| 119 |
+
"Mobile Portrait (1080x1920)": (1080, 1920),
|
| 120 |
+
"Desktop (1920x1080)": (1920, 1080),
|
| 121 |
+
"Square (1080x1080)": (1080, 1080),
|
| 122 |
+
"Ultrawide (2560x1080)": (2560, 1080),
|
| 123 |
+
"4K Desktop (3840x2160)": (3840, 2160),
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
base_prompt = prompts.get(request.style, prompts["Gradient"])
|
| 127 |
+
|
| 128 |
+
# Add quality enhancers and style variations
|
| 129 |
+
quality_modifiers = [
|
| 130 |
+
"masterpiece", "best quality", "ultra-detailed", "8k resolution",
|
| 131 |
+
"professional", "award-winning", "trending on artstation", "highly detailed"
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
# Randomly select 2-3 quality modifiers to add variety
|
| 135 |
+
selected_modifiers = random.sample(quality_modifiers, k=random.randint(2, 3))
|
| 136 |
+
|
| 137 |
+
# Build the final prompt with variations
|
| 138 |
+
final_prompt = f"{base_prompt}, {', '.join(selected_modifiers)}"
|
| 139 |
+
|
| 140 |
+
# Add artistic style variations occasionally
|
| 141 |
+
if random.random() > 0.7:
|
| 142 |
+
artistic_styles = [
|
| 143 |
+
"oil painting style", "digital art", "photorealistic", "concept art",
|
| 144 |
+
"matte painting", "volumetric lighting", "ray tracing", "octane render"
|
| 145 |
+
]
|
| 146 |
+
final_prompt += f", {random.choice(artistic_styles)}"
|
| 147 |
+
|
| 148 |
+
# Add composition variations
|
| 149 |
+
if random.random() > 0.6:
|
| 150 |
+
compositions = [
|
| 151 |
+
"rule of thirds", "golden ratio composition", "symmetrical balance",
|
| 152 |
+
"dynamic composition", "centered composition", "diagonal flow"
|
| 153 |
+
]
|
| 154 |
+
final_prompt += f", {random.choice(compositions)}"
|
| 155 |
+
|
| 156 |
+
if request.color and request.color.strip():
|
| 157 |
+
# Enhanced color integration
|
| 158 |
+
color_integration = random.choice([
|
| 159 |
+
f"dominated by {request.color.strip()} tones",
|
| 160 |
+
f"featuring prominent {request.color.strip()} accents",
|
| 161 |
+
f"with {request.color.strip()} color palette",
|
| 162 |
+
f"infused with {request.color.strip()} hues",
|
| 163 |
+
f"{request.color.strip()} color harmony"
|
| 164 |
+
])
|
| 165 |
+
final_prompt += f", {color_integration}"
|
| 166 |
+
|
| 167 |
+
# Add negative prompt to avoid common issues and maintain abstract focus
|
| 168 |
+
negative_prompt = "low quality, blurry, pixelated, watermark, text, logo, signature, artifacts, distorted, people, person, human, face, car, vehicle, building, realistic objects, photography, portrait, landscape"
|
| 169 |
+
|
| 170 |
+
try:
|
| 171 |
+
# Set seed
|
| 172 |
+
torch.manual_seed(request.seed)
|
| 173 |
+
if torch.cuda.is_available():
|
| 174 |
+
torch.cuda.manual_seed(request.seed)
|
| 175 |
+
|
| 176 |
+
# Generate base image
|
| 177 |
+
# Ensure pipeline is loaded lazily
|
| 178 |
+
p = load_pipeline()
|
| 179 |
+
with torch.no_grad():
|
| 180 |
+
base_image = p(
|
| 181 |
+
final_prompt,
|
| 182 |
+
num_inference_steps=request.steps,
|
| 183 |
+
guidance_scale=1.0,
|
| 184 |
+
negative_prompt=negative_prompt,
|
| 185 |
+
height=512,
|
| 186 |
+
width=512
|
| 187 |
+
).images[0]
|
| 188 |
+
|
| 189 |
+
# Resize image
|
| 190 |
+
target_width, target_height = resolutions.get(request.resolution, (1920, 1080))
|
| 191 |
+
resized_image = resize_image(base_image, target_width, target_height)
|
| 192 |
+
|
| 193 |
+
# Extract colors
|
| 194 |
+
colors = extract_colors(resized_image)
|
| 195 |
+
|
| 196 |
+
# Convert image to Base64 string to send via JSON
|
| 197 |
+
buffered = io.BytesIO()
|
| 198 |
+
resized_image.save(buffered, format="PNG")
|
| 199 |
+
img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
|
| 200 |
+
|
| 201 |
+
return JSONResponse(content={
|
| 202 |
+
"image": f"data:image/png;base64,{img_str}",
|
| 203 |
+
"palette": colors,
|
| 204 |
+
"seed": request.seed,
|
| 205 |
+
"style": request.style,
|
| 206 |
+
"steps": request.steps,
|
| 207 |
+
"resolution": f"{target_width}x{target_height}"
|
| 208 |
+
})
|
| 209 |
+
except Exception as e:
|
| 210 |
+
print(f"Error during generation: {e}")
|
| 211 |
+
return JSONResponse(status_code=500, content={"error": str(e)})
|
| 212 |
+
|
| 213 |
+
@app.get("/random-seed/")
|
| 214 |
+
async def random_seed_endpoint():
|
| 215 |
+
return {"seed": random.randint(1, 999999)}
|
| 216 |
+
|
| 217 |
+
@app.get("/health")
|
| 218 |
+
async def health_check():
|
| 219 |
+
return {"status": "healthy", "message": "AI Wallpaper Generator API is running"}
|
| 220 |
+
|
| 221 |
+
if __name__ == "__main__":
|
| 222 |
+
port = int(os.getenv("PORT", 8000))
|
| 223 |
+
print("Starting AI Wallpaper Generator API...")
|
| 224 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
frontend/build/examples/img1.png
ADDED
|
Git LFS Details
|
frontend/build/examples/img2.png
ADDED
|
Git LFS Details
|
frontend/build/examples/img3.png
ADDED
|
Git LFS Details
|
frontend/build/examples/img4.png
ADDED
|
Git LFS Details
|
frontend/build/examples/img5.png
ADDED
|
Git LFS Details
|
frontend/build/examples/img6.png
ADDED
|
Git LFS Details
|
frontend/build/index.html
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>AI Wallpaper Generator</title>
|
| 7 |
+
<link rel="stylesheet" href="style.css">
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<div class="particles-container" id="particles"></div>
|
| 11 |
+
|
| 12 |
+
<div id="background-container"></div>
|
| 13 |
+
</div>
|
| 14 |
+
|
| 15 |
+
<div class="main-content">
|
| 16 |
+
<header>
|
| 17 |
+
<h1>AI Wallpaper Generator</h1>
|
| 18 |
+
<p id="tagline"></p>
|
| 19 |
+
</header>
|
| 20 |
+
|
| 21 |
+
<main>
|
| 22 |
+
<div class="controls">
|
| 23 |
+
<div class="control-group">
|
| 24 |
+
<label for="style-select">Style</label>
|
| 25 |
+
<select id="style-select">
|
| 26 |
+
<option value="Geometric">Geometric</option>
|
| 27 |
+
<option value="Organic">Organic</option>
|
| 28 |
+
<option value="Vibrant">Vibrant</option>
|
| 29 |
+
<option value="Minimal">Minimal</option>
|
| 30 |
+
<option value="Ink Flow">Ink Flow</option>
|
| 31 |
+
<option value="Gradient" selected>Gradient</option>
|
| 32 |
+
<option value="Crystal">Crystal</option>
|
| 33 |
+
<option value="Retro Wave">Retro Wave</option>
|
| 34 |
+
<option value="Botanical">Botanical</option>
|
| 35 |
+
<option value="Space Cosmic">Space Cosmic</option>
|
| 36 |
+
<option value="Psychedelic">Psychedelic</option>
|
| 37 |
+
<option value="Industrial">Industrial</option>
|
| 38 |
+
</select>
|
| 39 |
+
</div>
|
| 40 |
+
|
| 41 |
+
<div class="control-group">
|
| 42 |
+
<label for="color-input">Color Influence</label>
|
| 43 |
+
<input type="text" id="color-input" placeholder="e.g., pastel pink, dark green">
|
| 44 |
+
</div>
|
| 45 |
+
|
| 46 |
+
<div class="control-group">
|
| 47 |
+
<label for="resolution-select">Resolution</label>
|
| 48 |
+
<select id="resolution-select">
|
| 49 |
+
<option value="Desktop (1920x1080)">Desktop (1920x1080)</option>
|
| 50 |
+
<option value="Mobile Portrait (1080x1920)">Mobile Portrait (1080x1920)</option>
|
| 51 |
+
<option value="Square (1080x1080)">Square (1080x1080)</option>
|
| 52 |
+
<option value="Ultrawide (2560x1080)">Ultrawide (2560x1080)</option>
|
| 53 |
+
<option value="4K Desktop (3840x2160)">4K Desktop (3840x2160)</option>
|
| 54 |
+
</select>
|
| 55 |
+
</div>
|
| 56 |
+
|
| 57 |
+
<div class="control-group">
|
| 58 |
+
<label for="seed-input">Seed</label>
|
| 59 |
+
<div class="seed-wrapper">
|
| 60 |
+
<input type="number" id="seed-input" value="42">
|
| 61 |
+
<button id="random-seed-btn" title="Randomize Seed">🎲</button>
|
| 62 |
+
</div>
|
| 63 |
+
</div>
|
| 64 |
+
</div>
|
| 65 |
+
|
| 66 |
+
<div class="action-buttons">
|
| 67 |
+
<button id="generate-btn"> Generate</button>
|
| 68 |
+
<button id="surprise-btn"> Surprise Me</button>
|
| 69 |
+
</div>
|
| 70 |
+
|
| 71 |
+
<div class="output">
|
| 72 |
+
<div id="status-display" class="status">Click Generate to start!</div>
|
| 73 |
+
<div id="loader" class="hidden"></div>
|
| 74 |
+
<img id="result-image" src="" alt="Generated Wallpaper" class="hidden">
|
| 75 |
+
|
| 76 |
+
<div id="post-generation-tools" class="hidden">
|
| 77 |
+
<button id="copy-seed-btn" title="Copy Seed">
|
| 78 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
| 79 |
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
| 80 |
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
| 81 |
+
</svg>
|
| 82 |
+
<span id="copy-seed-feedback">Copy Seed</span>
|
| 83 |
+
</button>
|
| 84 |
+
<button id="download-btn" title="Download Wallpaper">
|
| 85 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
| 86 |
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
| 87 |
+
<polyline points="7 10 12 15 17 10"></polyline>
|
| 88 |
+
<line x1="12" y1="15" x2="12" y2="3"></line>
|
| 89 |
+
</svg>
|
| 90 |
+
<span>Download</span>
|
| 91 |
+
</button>
|
| 92 |
+
</div>
|
| 93 |
+
|
| 94 |
+
<div id="palette-container"></div>
|
| 95 |
+
</div>
|
| 96 |
+
</main>
|
| 97 |
+
</div>
|
| 98 |
+
|
| 99 |
+
<section id="recent-creations" class="hidden">
|
| 100 |
+
<h2>Recent Creations</h2>
|
| 101 |
+
<div id="recent-gallery" class="hide-scrollbar"></div>
|
| 102 |
+
</section>
|
| 103 |
+
|
| 104 |
+
<div class="side-text">
|
| 105 |
+
<span>© 2025 Wallbeauti | AI Wallpaper Generator</span>
|
| 106 |
+
</div>
|
| 107 |
+
|
| 108 |
+
<script src="script.js"></script>
|
| 109 |
+
</body>
|
| 110 |
+
</html>
|
frontend/build/script.js
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 3 |
+
const elements = {
|
| 4 |
+
generateBtn: document.getElementById('generate-btn'),
|
| 5 |
+
surpriseBtn: document.getElementById('surprise-btn'),
|
| 6 |
+
randomSeedBtn: document.getElementById('random-seed-btn'),
|
| 7 |
+
resultImage: document.getElementById('result-image'),
|
| 8 |
+
paletteContainer: document.getElementById('palette-container'),
|
| 9 |
+
loader: document.getElementById('loader'),
|
| 10 |
+
backgroundContainer: document.getElementById('background-container'),
|
| 11 |
+
statusDisplay: document.getElementById('status-display'),
|
| 12 |
+
styleSelect: document.getElementById('style-select'),
|
| 13 |
+
seedInput: document.getElementById('seed-input'),
|
| 14 |
+
colorInput: document.getElementById('color-input'),
|
| 15 |
+
postGenerationTools: document.getElementById('post-generation-tools'),
|
| 16 |
+
copySeedBtn: document.getElementById('copy-seed-btn'),
|
| 17 |
+
downloadBtn: document.getElementById('download-btn'),
|
| 18 |
+
copySeedFeedback: document.getElementById('copy-seed-feedback'),
|
| 19 |
+
tagline: document.getElementById('tagline'),
|
| 20 |
+
recentCreationsSection: document.getElementById('recent-creations'),
|
| 21 |
+
recentGallery: document.getElementById('recent-gallery'),
|
| 22 |
+
particlesContainer: document.getElementById('particles'),
|
| 23 |
+
};
|
| 24 |
+
|
| 25 |
+
// API URL for your Python backend
|
| 26 |
+
const API_URL = '';
|
| 27 |
+
|
| 28 |
+
// const API_URL = 'http://127.0.0.1:8000';
|
| 29 |
+
|
| 30 |
+
// State Variables
|
| 31 |
+
let backgroundImages = [];
|
| 32 |
+
let recentCreations = [];
|
| 33 |
+
let statusInterval;
|
| 34 |
+
const MAX_BG_IMAGES = 6;
|
| 35 |
+
const MAX_RECENTS = 4;
|
| 36 |
+
|
| 37 |
+
// Preloaded example images for floating background
|
| 38 |
+
const EXAMPLE_IMAGES = [
|
| 39 |
+
'examples/img1.png',
|
| 40 |
+
'examples/img2.png',
|
| 41 |
+
'examples/img3.png',
|
| 42 |
+
'examples/img4.png',
|
| 43 |
+
'examples/img5.png',
|
| 44 |
+
'examples/img6.png',
|
| 45 |
+
];
|
| 46 |
+
|
| 47 |
+
// Enhanced Particles System
|
| 48 |
+
function createParticles() {
|
| 49 |
+
const colors = ['#4D6473', '#A58A73', '#B59B67', '#F4C2C2', '#E6CFC1'];
|
| 50 |
+
for (let i = 0; i < 20; i++) {
|
| 51 |
+
const particle = document.createElement('div');
|
| 52 |
+
particle.className = 'particle';
|
| 53 |
+
particle.style.width = Math.random() * 8 + 4 + 'px';
|
| 54 |
+
particle.style.height = particle.style.width;
|
| 55 |
+
particle.style.left = Math.random() * 100 + '%';
|
| 56 |
+
particle.style.top = Math.random() * 100 + '%';
|
| 57 |
+
particle.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
|
| 58 |
+
particle.style.animationDelay = Math.random() * 15 + 's';
|
| 59 |
+
particle.style.animationDuration = (Math.random() * 10 + 10) + 's';
|
| 60 |
+
elements.particlesContainer.appendChild(particle);
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
// Enhanced Typing Animation
|
| 65 |
+
function typeText(element, text, speed = 100) {
|
| 66 |
+
let i = 0;
|
| 67 |
+
element.innerHTML = "";
|
| 68 |
+
const typing = setInterval(() => {
|
| 69 |
+
if (i < text.length) {
|
| 70 |
+
element.innerHTML += text.charAt(i);
|
| 71 |
+
i++;
|
| 72 |
+
} else {
|
| 73 |
+
clearInterval(typing);
|
| 74 |
+
setTimeout(() => {
|
| 75 |
+
element.style.textShadow = '0 0 20px rgba(77, 100, 115, 0.5)';
|
| 76 |
+
setTimeout(() => {
|
| 77 |
+
element.style.textShadow = 'none';
|
| 78 |
+
}, 1000);
|
| 79 |
+
}, 500);
|
| 80 |
+
}
|
| 81 |
+
}, speed);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
// Enhanced Floating Background Logic
|
| 85 |
+
function addFloatingImage(imageUrl) {
|
| 86 |
+
const img = document.createElement('img');
|
| 87 |
+
img.src = imageUrl;
|
| 88 |
+
img.className = 'floating-bg';
|
| 89 |
+
if (backgroundImages.length >= MAX_BG_IMAGES) {
|
| 90 |
+
const oldImg = backgroundImages.shift();
|
| 91 |
+
oldImg.style.opacity = '0';
|
| 92 |
+
oldImg.style.transform = 'scale(0.8)';
|
| 93 |
+
setTimeout(() => oldImg.remove(), 1000);
|
| 94 |
+
}
|
| 95 |
+
const size = Math.random() * 180 + 120;
|
| 96 |
+
img.style.width = `${size}px`;
|
| 97 |
+
img.style.height = 'auto';
|
| 98 |
+
img.style.top = `${Math.random() * 80 + 10}%`;
|
| 99 |
+
img.style.left = `${Math.random() * 80 + 10}%`;
|
| 100 |
+
img.style.animationDuration = `${Math.random() * 15 + 20}s`;
|
| 101 |
+
img.style.zIndex = Math.floor(Math.random() * 3) + 1;
|
| 102 |
+
elements.backgroundContainer.appendChild(img);
|
| 103 |
+
backgroundImages.push(img);
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
function addFloatingImageFromExamples() {
|
| 107 |
+
const imageUrl = EXAMPLE_IMAGES[Math.floor(Math.random() * EXAMPLE_IMAGES.length)];
|
| 108 |
+
addFloatingImage(imageUrl);
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
function addFloatingImageAt(imageUrl, topPercent, leftPercent, cellWPercent) {
|
| 112 |
+
const img = document.createElement('img');
|
| 113 |
+
img.src = imageUrl;
|
| 114 |
+
img.className = 'floating-bg';
|
| 115 |
+
|
| 116 |
+
if (backgroundImages.length >= MAX_BG_IMAGES) {
|
| 117 |
+
const oldImg = backgroundImages.shift();
|
| 118 |
+
oldImg.style.opacity = '0';
|
| 119 |
+
oldImg.style.transform = 'scale(0.8)';
|
| 120 |
+
setTimeout(() => oldImg.remove(), 1000);
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
// Size scaled to cell width for better spacing
|
| 124 |
+
const cellWidthPx = window.innerWidth * (cellWPercent / 100);
|
| 125 |
+
const targetWidth = Math.max(120, Math.min(220, cellWidthPx * 0.6));
|
| 126 |
+
img.style.width = `${Math.round(targetWidth)}px`;
|
| 127 |
+
img.style.height = 'auto';
|
| 128 |
+
|
| 129 |
+
// track percent positions for later adjustment
|
| 130 |
+
img.dataset.topPercent = String(topPercent);
|
| 131 |
+
img.dataset.leftPercent = String(leftPercent);
|
| 132 |
+
img.style.top = `${topPercent}%`;
|
| 133 |
+
img.style.left = `${leftPercent}%`;
|
| 134 |
+
img.style.animationDuration = `${Math.random() * 15 + 20}s`;
|
| 135 |
+
img.style.zIndex = Math.floor(Math.random() * 3) + 1;
|
| 136 |
+
|
| 137 |
+
elements.backgroundContainer.appendChild(img);
|
| 138 |
+
|
| 139 |
+
// After the image has dimensions, nudge it so no more than 50% is under the controls card
|
| 140 |
+
if (img.complete) {
|
| 141 |
+
adjustToAvoidControls(img, 8);
|
| 142 |
+
} else {
|
| 143 |
+
img.addEventListener('load', () => adjustToAvoidControls(img, 8), { once: true });
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
backgroundImages.push(img);
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
function computeEvenPositions(count, margin = 8) {
|
| 150 |
+
const cols = Math.ceil(Math.sqrt(count));
|
| 151 |
+
const rows = Math.ceil(count / cols);
|
| 152 |
+
const availableW = 100 - 2 * margin;
|
| 153 |
+
const availableH = 100 - 2 * margin;
|
| 154 |
+
const cellW = availableW / cols;
|
| 155 |
+
const cellH = availableH / rows;
|
| 156 |
+
const positions = [];
|
| 157 |
+
for (let i = 0; i < count; i++) {
|
| 158 |
+
const r = Math.floor(i / cols);
|
| 159 |
+
const c = i % cols;
|
| 160 |
+
let top = margin + (r + 0.5) * cellH;
|
| 161 |
+
let left = margin + (c + 0.5) * cellW;
|
| 162 |
+
const jitterX = (Math.random() - 0.5) * cellW * 0.3;
|
| 163 |
+
const jitterY = (Math.random() - 0.5) * cellH * 0.3;
|
| 164 |
+
top += jitterY;
|
| 165 |
+
left += jitterX;
|
| 166 |
+
top = Math.max(margin, Math.min(100 - margin, top));
|
| 167 |
+
left = Math.max(margin, Math.min(100 - margin, left));
|
| 168 |
+
positions.push({ top, left });
|
| 169 |
+
}
|
| 170 |
+
return { positions, cellW, cellH, rows, cols };
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
function computeOverlapArea(r1, r2) {
|
| 174 |
+
const x = Math.max(0, Math.min(r1.right, r2.right) - Math.max(r1.left, r2.left));
|
| 175 |
+
const y = Math.max(0, Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top));
|
| 176 |
+
return x * y;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
function adjustToAvoidControls(img, margin = 8) {
|
| 180 |
+
const controls = document.querySelector('.controls');
|
| 181 |
+
if (!controls) return;
|
| 182 |
+
|
| 183 |
+
const clamp = (val, min, max) => Math.max(min, Math.min(max, val));
|
| 184 |
+
let topP = parseFloat(img.dataset.topPercent || '50');
|
| 185 |
+
let leftP = parseFloat(img.dataset.leftPercent || '50');
|
| 186 |
+
|
| 187 |
+
const step = 3; // percent per nudge
|
| 188 |
+
const maxTries = 25;
|
| 189 |
+
|
| 190 |
+
for (let i = 0; i < maxTries; i++) {
|
| 191 |
+
const imgRect = img.getBoundingClientRect();
|
| 192 |
+
const ctrlRect = controls.getBoundingClientRect();
|
| 193 |
+
const imgArea = imgRect.width * imgRect.height;
|
| 194 |
+
if (!imgArea) break;
|
| 195 |
+
|
| 196 |
+
const overlap = computeOverlapArea(imgRect, ctrlRect);
|
| 197 |
+
const ratio = overlap / imgArea;
|
| 198 |
+
if (ratio <= 0.5) break; // acceptable
|
| 199 |
+
|
| 200 |
+
const cx = ctrlRect.left + ctrlRect.width / 2;
|
| 201 |
+
const cy = ctrlRect.top + ctrlRect.height / 2;
|
| 202 |
+
const ix = imgRect.left + imgRect.width / 2;
|
| 203 |
+
const iy = imgRect.top + imgRect.height / 2;
|
| 204 |
+
|
| 205 |
+
const overlapW = Math.max(0, Math.min(imgRect.right, ctrlRect.right) - Math.max(imgRect.left, ctrlRect.left));
|
| 206 |
+
const overlapH = Math.max(0, Math.min(imgRect.bottom, ctrlRect.bottom) - Math.max(imgRect.top, ctrlRect.top));
|
| 207 |
+
|
| 208 |
+
if (overlapW >= overlapH) {
|
| 209 |
+
const dir = ix < cx ? -1 : 1; // move away horizontally
|
| 210 |
+
leftP = clamp(leftP + dir * step, margin, 100 - margin);
|
| 211 |
+
} else {
|
| 212 |
+
const dir = iy < cy ? -1 : 1; // move away vertically
|
| 213 |
+
topP = clamp(topP + dir * step, margin, 100 - margin);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
img.dataset.topPercent = String(topP);
|
| 217 |
+
img.dataset.leftPercent = String(leftP);
|
| 218 |
+
img.style.top = `${topP}%`;
|
| 219 |
+
img.style.left = `${leftP}%`;
|
| 220 |
+
}
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
// Enhanced Status Messages
|
| 224 |
+
function startStatusUpdates() {
|
| 225 |
+
const messages = [
|
| 226 |
+
" Mixing digital paint...",
|
| 227 |
+
" Consulting the AI muse...",
|
| 228 |
+
" Channeling creative energy...",
|
| 229 |
+
" Weaving visual magic...",
|
| 230 |
+
" Launching imagination...",
|
| 231 |
+
" Almost there..."
|
| 232 |
+
];
|
| 233 |
+
let msgIndex = 0;
|
| 234 |
+
elements.statusDisplay.textContent = messages[msgIndex];
|
| 235 |
+
statusInterval = setInterval(() => {
|
| 236 |
+
msgIndex = (msgIndex + 1) % messages.length;
|
| 237 |
+
elements.statusDisplay.textContent = messages[msgIndex];
|
| 238 |
+
}, 2500);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
function stopStatusUpdates(finalMessage) {
|
| 242 |
+
clearInterval(statusInterval);
|
| 243 |
+
elements.statusDisplay.textContent = finalMessage;
|
| 244 |
+
elements.statusDisplay.style.transform = 'scale(1.1)';
|
| 245 |
+
elements.statusDisplay.style.color = '#4D6473';
|
| 246 |
+
setTimeout(() => {
|
| 247 |
+
elements.statusDisplay.style.transform = 'scale(1)';
|
| 248 |
+
elements.statusDisplay.style.color = '';
|
| 249 |
+
}, 300);
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
// Enhanced Recent Creations
|
| 253 |
+
function renderRecents() {
|
| 254 |
+
if (recentCreations.length === 0) return;
|
| 255 |
+
elements.recentGallery.innerHTML = "";
|
| 256 |
+
recentCreations.forEach((creation, index) => {
|
| 257 |
+
const img = document.createElement('img');
|
| 258 |
+
img.src = creation.src;
|
| 259 |
+
img.title = `Style: ${creation.style}, Seed: ${creation.seed}`;
|
| 260 |
+
img.style.animationDelay = `${index * 0.1}s`;
|
| 261 |
+
img.addEventListener('click', () => {
|
| 262 |
+
elements.styleSelect.value = creation.style;
|
| 263 |
+
elements.seedInput.value = creation.seed;
|
| 264 |
+
elements.colorInput.value = creation.color || "";
|
| 265 |
+
window.scrollTo({
|
| 266 |
+
top: 0,
|
| 267 |
+
behavior: 'smooth'
|
| 268 |
+
});
|
| 269 |
+
elements.styleSelect.style.boxShadow = '0 0 20px rgba(77, 100, 115, 0.5)';
|
| 270 |
+
setTimeout(() => {
|
| 271 |
+
elements.styleSelect.style.boxShadow = '';
|
| 272 |
+
}, 2000);
|
| 273 |
+
});
|
| 274 |
+
elements.recentGallery.appendChild(img);
|
| 275 |
+
});
|
| 276 |
+
elements.recentCreationsSection.classList.remove('hidden');
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
// Enhanced Color Palette Display
|
| 280 |
+
function displayColorPalette(colors) {
|
| 281 |
+
elements.paletteContainer.innerHTML = '';
|
| 282 |
+
colors.forEach((color, index) => {
|
| 283 |
+
const wrapper = document.createElement('div');
|
| 284 |
+
wrapper.className = 'color-swatch-wrapper';
|
| 285 |
+
wrapper.style.animationDelay = `${index * 0.1}s`;
|
| 286 |
+
const swatch = document.createElement('div');
|
| 287 |
+
swatch.className = 'color-swatch';
|
| 288 |
+
swatch.style.backgroundColor = color;
|
| 289 |
+
const hexCode = document.createElement('span');
|
| 290 |
+
hexCode.className = 'hex-code';
|
| 291 |
+
hexCode.textContent = color;
|
| 292 |
+
swatch.addEventListener('click', () => {
|
| 293 |
+
navigator.clipboard.writeText(color).then(() => {
|
| 294 |
+
hexCode.textContent = 'Copied!';
|
| 295 |
+
swatch.style.transform = 'scale(1.3) rotate(10deg)';
|
| 296 |
+
setTimeout(() => {
|
| 297 |
+
hexCode.textContent = color;
|
| 298 |
+
swatch.style.transform = '';
|
| 299 |
+
}, 1500);
|
| 300 |
+
});
|
| 301 |
+
});
|
| 302 |
+
wrapper.appendChild(swatch);
|
| 303 |
+
wrapper.appendChild(hexCode);
|
| 304 |
+
elements.paletteContainer.appendChild(wrapper);
|
| 305 |
+
});
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
// **UPDATED** Generation Function with actual API call
|
| 309 |
+
async function performGeneration() {
|
| 310 |
+
elements.loader.classList.remove('hidden');
|
| 311 |
+
elements.resultImage.classList.add('hidden');
|
| 312 |
+
elements.paletteContainer.innerHTML = '';
|
| 313 |
+
elements.generateBtn.disabled = true;
|
| 314 |
+
elements.surpriseBtn.disabled = true;
|
| 315 |
+
elements.postGenerationTools.classList.add('hidden');
|
| 316 |
+
startStatusUpdates();
|
| 317 |
+
|
| 318 |
+
const generationData = {
|
| 319 |
+
style: elements.styleSelect.value,
|
| 320 |
+
resolution: document.getElementById('resolution-select').value,
|
| 321 |
+
seed: parseInt(elements.seedInput.value, 10),
|
| 322 |
+
color: elements.colorInput.value,
|
| 323 |
+
steps: 4,
|
| 324 |
+
};
|
| 325 |
+
|
| 326 |
+
try {
|
| 327 |
+
const response = await fetch(`${API_URL}/generate-wallpaper/`, {
|
| 328 |
+
method: 'POST',
|
| 329 |
+
headers: {
|
| 330 |
+
'Content-Type': 'application/json'
|
| 331 |
+
},
|
| 332 |
+
body: JSON.stringify(generationData),
|
| 333 |
+
});
|
| 334 |
+
|
| 335 |
+
if (!response.ok) {
|
| 336 |
+
throw new Error(`Server error! Status: ${response.status}`);
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
const data = await response.json();
|
| 340 |
+
|
| 341 |
+
// Display result
|
| 342 |
+
elements.resultImage.src = data.image;
|
| 343 |
+
elements.resultImage.classList.remove('hidden');
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
// Show tools and palette
|
| 347 |
+
elements.postGenerationTools.classList.remove('hidden');
|
| 348 |
+
displayColorPalette(data.palette);
|
| 349 |
+
|
| 350 |
+
// Add to recents
|
| 351 |
+
recentCreations.unshift({ ...generationData,
|
| 352 |
+
src: data.image
|
| 353 |
+
});
|
| 354 |
+
if (recentCreations.length > MAX_RECENTS) {
|
| 355 |
+
recentCreations.pop();
|
| 356 |
+
}
|
| 357 |
+
renderRecents();
|
| 358 |
+
|
| 359 |
+
stopStatusUpdates(' Masterpiece created!');
|
| 360 |
+
|
| 361 |
+
} catch (error) {
|
| 362 |
+
console.error('Generation failed:', error);
|
| 363 |
+
stopStatusUpdates(`Error: ${error.message}`);
|
| 364 |
+
} finally {
|
| 365 |
+
elements.loader.classList.add('hidden');
|
| 366 |
+
elements.generateBtn.disabled = false;
|
| 367 |
+
elements.surpriseBtn.disabled = false;
|
| 368 |
+
}
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
// **UPDATED** Random Seed Generator with actual API call
|
| 372 |
+
async function updateRandomSeed() {
|
| 373 |
+
try {
|
| 374 |
+
const response = await fetch(`${API_URL}/random-seed/`);
|
| 375 |
+
if (!response.ok) {
|
| 376 |
+
throw new Error('Failed to fetch seed from server.');
|
| 377 |
+
}
|
| 378 |
+
const data = await response.json();
|
| 379 |
+
elements.seedInput.value = data.seed;
|
| 380 |
+
|
| 381 |
+
// Visual feedback
|
| 382 |
+
elements.seedInput.style.background = 'rgba(77, 100, 115, 0.2)';
|
| 383 |
+
setTimeout(() => {
|
| 384 |
+
elements.seedInput.style.background = '';
|
| 385 |
+
}, 500);
|
| 386 |
+
|
| 387 |
+
} catch (error) {
|
| 388 |
+
console.error('Failed to get random seed:', error);
|
| 389 |
+
elements.seedInput.value = Math.floor(Math.random() * 999999);
|
| 390 |
+
}
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
elements.generateBtn.addEventListener('click', performGeneration);
|
| 394 |
+
elements.randomSeedBtn.addEventListener('click', updateRandomSeed);
|
| 395 |
+
|
| 396 |
+
elements.surpriseBtn.addEventListener('click', async () => {
|
| 397 |
+
const options = elements.styleSelect.options;
|
| 398 |
+
const randomIndex = Math.floor(Math.random() * options.length);
|
| 399 |
+
elements.styleSelect.value = options[randomIndex].value;
|
| 400 |
+
elements.colorInput.value = "";
|
| 401 |
+
await updateRandomSeed();
|
| 402 |
+
elements.surpriseBtn.style.transform = 'rotate(360deg)';
|
| 403 |
+
setTimeout(() => {
|
| 404 |
+
elements.surpriseBtn.style.transform = '';
|
| 405 |
+
performGeneration();
|
| 406 |
+
}, 500);
|
| 407 |
+
});
|
| 408 |
+
|
| 409 |
+
elements.copySeedBtn.addEventListener('click', () => {
|
| 410 |
+
const seedValue = elements.seedInput.value;
|
| 411 |
+
navigator.clipboard.writeText(seedValue).then(() => {
|
| 412 |
+
elements.copySeedFeedback.textContent = 'Copied!';
|
| 413 |
+
elements.copySeedBtn.style.background = 'rgba(77, 100, 115, 0.3)';
|
| 414 |
+
setTimeout(() => {
|
| 415 |
+
elements.copySeedFeedback.textContent = 'Copy Seed';
|
| 416 |
+
elements.copySeedBtn.style.background = '';
|
| 417 |
+
}, 2000);
|
| 418 |
+
});
|
| 419 |
+
});
|
| 420 |
+
|
| 421 |
+
elements.downloadBtn.addEventListener('click', () => {
|
| 422 |
+
if (!elements.resultImage.src || elements.resultImage.src.startsWith('http')) return;
|
| 423 |
+
const link = document.createElement('a');
|
| 424 |
+
link.href = elements.resultImage.src;
|
| 425 |
+
const style = elements.styleSelect.value.replace(/\s+/g, '-').toLowerCase();
|
| 426 |
+
const seed = elements.seedInput.value;
|
| 427 |
+
link.download = `wallpaper-${style}-${seed}.png`;
|
| 428 |
+
document.body.appendChild(link);
|
| 429 |
+
link.click();
|
| 430 |
+
document.body.removeChild(link);
|
| 431 |
+
elements.downloadBtn.style.transform = 'scale(1.1)';
|
| 432 |
+
setTimeout(() => {
|
| 433 |
+
elements.downloadBtn.style.transform = '';
|
| 434 |
+
}, 200);
|
| 435 |
+
});
|
| 436 |
+
|
| 437 |
+
// Enhanced Input Interactions
|
| 438 |
+
[elements.styleSelect, elements.colorInput, elements.seedInput].forEach(input => {
|
| 439 |
+
input.addEventListener('focus', (e) => {
|
| 440 |
+
e.target.style.transform = 'translateY(-2px)';
|
| 441 |
+
});
|
| 442 |
+
input.addEventListener('blur', (e) => {
|
| 443 |
+
e.target.style.transform = '';
|
| 444 |
+
});
|
| 445 |
+
});
|
| 446 |
+
|
| 447 |
+
// Initialize Everything
|
| 448 |
+
createParticles();
|
| 449 |
+
typeText(elements.tagline, "Create unique, high-resolution wallpapers with AI magic ");
|
| 450 |
+
if (typeof initThemeSelector === 'function') {
|
| 451 |
+
initThemeSelector();
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
// Compute an even spread layout and seed images
|
| 455 |
+
const layout = computeEvenPositions(EXAMPLE_IMAGES.length, 8);
|
| 456 |
+
EXAMPLE_IMAGES.forEach((src, i) => {
|
| 457 |
+
const pos = layout.positions[i];
|
| 458 |
+
setTimeout(() => addFloatingImageAt(src, pos.top, pos.left, layout.cellW), i * 400);
|
| 459 |
+
});
|
| 460 |
+
|
| 461 |
+
// Keep floating background anchored during scroll (no parallax)
|
| 462 |
+
window.addEventListener('scroll', () => {
|
| 463 |
+
elements.backgroundContainer.style.transform = 'translateY(0)';
|
| 464 |
+
});
|
| 465 |
+
|
| 466 |
+
// Add keyboard shortcuts
|
| 467 |
+
document.addEventListener('keydown', (e) => {
|
| 468 |
+
if (e.ctrlKey || e.metaKey) {
|
| 469 |
+
switch (e.key) {
|
| 470 |
+
case 'Enter':
|
| 471 |
+
e.preventDefault();
|
| 472 |
+
performGeneration();
|
| 473 |
+
break;
|
| 474 |
+
case 'r':
|
| 475 |
+
e.preventDefault();
|
| 476 |
+
updateRandomSeed();
|
| 477 |
+
break;
|
| 478 |
+
case 's':
|
| 479 |
+
e.preventDefault();
|
| 480 |
+
elements.surpriseBtn.click();
|
| 481 |
+
break;
|
| 482 |
+
}
|
| 483 |
+
}
|
| 484 |
+
});
|
| 485 |
+
|
| 486 |
+
console.log('AI Wallpaper Generator initialized successfully!');
|
| 487 |
+
});
|
frontend/build/style.css
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 2 |
+
|
| 3 |
+
:root {
|
| 4 |
+
/* Enhanced Color Palette */
|
| 5 |
+
--cream-light: #FAF7F3;
|
| 6 |
+
--cream-medium: #F5EDE6;
|
| 7 |
+
--cream-dark: #EBD9CA;
|
| 8 |
+
--sage-light: #E9EAE5;
|
| 9 |
+
--sage-medium: #D4D6CE;
|
| 10 |
+
--sage-dark: #3B3F3A;
|
| 11 |
+
--slate-blue: #4D6473;
|
| 12 |
+
--slate-dark: #3C505C;
|
| 13 |
+
--warm-brown: #A58A73;
|
| 14 |
+
--golden-brown: #B59B67;
|
| 15 |
+
--accent-coral: #F4C2C2;
|
| 16 |
+
--accent-pink: #FFB7C5;
|
| 17 |
+
--accent-red: #E52B50;
|
| 18 |
+
|
| 19 |
+
/* Dynamic Variables */
|
| 20 |
+
--text-primary: #3E3B39;
|
| 21 |
+
--text-light: #f0f0f0;
|
| 22 |
+
--glass-bg: rgba(255, 255, 255, 0.15);
|
| 23 |
+
--glass-border: rgba(255, 255, 255, 0.3);
|
| 24 |
+
--shadow-soft: 0 8px 32px rgba(0, 0, 0, 0.1);
|
| 25 |
+
--shadow-hover: 0 12px 40px rgba(0, 0, 0, 0.15);
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
* { box-sizing: border-box; }
|
| 29 |
+
|
| 30 |
+
body {
|
| 31 |
+
font-family: 'Inter', sans-serif;
|
| 32 |
+
margin: 0;
|
| 33 |
+
padding: 0;
|
| 34 |
+
overflow-x: hidden;
|
| 35 |
+
scrollbar-width: none;
|
| 36 |
+
-ms-overflow-style: none;
|
| 37 |
+
background: linear-gradient(
|
| 38 |
+
135deg,
|
| 39 |
+
var(--cream-light) 0%,
|
| 40 |
+
var(--sage-light) 25%,
|
| 41 |
+
var(--cream-medium) 50%,
|
| 42 |
+
var(--warm-brown) 75%,
|
| 43 |
+
var(--slate-blue) 100%
|
| 44 |
+
);
|
| 45 |
+
background-size: 400% 400%;
|
| 46 |
+
animation: gradientFlow 20s ease infinite;
|
| 47 |
+
min-height: 100vh;
|
| 48 |
+
color: var(--text-primary);
|
| 49 |
+
position: relative;
|
| 50 |
+
}
|
| 51 |
+
html::-webkit-scrollbar {
|
| 52 |
+
display: none;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
@keyframes gradientFlow {
|
| 56 |
+
0%, 100% { background-position: 0% 50%; }
|
| 57 |
+
25% { background-position: 100% 50%; }
|
| 58 |
+
50% { background-position: 50% 100%; }
|
| 59 |
+
75% { background-position: 0% 0%; }
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
.particles-container {
|
| 63 |
+
position: fixed;
|
| 64 |
+
top: 0;
|
| 65 |
+
left: 0;
|
| 66 |
+
width: 100%;
|
| 67 |
+
height: 100%;
|
| 68 |
+
pointer-events: none;
|
| 69 |
+
z-index: 0;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.particle {
|
| 73 |
+
position: absolute;
|
| 74 |
+
border-radius: 50%;
|
| 75 |
+
opacity: 0.6;
|
| 76 |
+
animation: float 15s infinite ease-in-out;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
@keyframes float {
|
| 80 |
+
0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0.3; }
|
| 81 |
+
25% { transform: translateY(-20px) rotate(90deg); opacity: 0.8; }
|
| 82 |
+
50% { transform: translateY(-40px) rotate(180deg); opacity: 0.5; }
|
| 83 |
+
75% { transform: translateY(-20px) rotate(270deg); opacity: 0.7; }
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
/* Floating Background Images */
|
| 87 |
+
#background-container {
|
| 88 |
+
position: fixed;
|
| 89 |
+
top: 0;
|
| 90 |
+
left: 0;
|
| 91 |
+
width: 100%;
|
| 92 |
+
height: 100%;
|
| 93 |
+
z-index: 1;
|
| 94 |
+
pointer-events: none;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
.floating-bg {
|
| 98 |
+
position: absolute;
|
| 99 |
+
border-radius: 16px;
|
| 100 |
+
opacity: 0;
|
| 101 |
+
box-shadow: var(--shadow-soft);
|
| 102 |
+
border: 2px solid var(--glass-border);
|
| 103 |
+
filter: blur(1px);
|
| 104 |
+
animation: subtleFloat 30s infinite ease-in-out alternate;
|
| 105 |
+
transition: all 0.5s ease;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
@keyframes subtleFloat {
|
| 109 |
+
0% {
|
| 110 |
+
transform: translateY(30px) rotate(-3deg) scale(0.9);
|
| 111 |
+
opacity: 0;
|
| 112 |
+
}
|
| 113 |
+
15% { opacity: 0.4; }
|
| 114 |
+
85% { opacity: 0.6; }
|
| 115 |
+
100% {
|
| 116 |
+
transform: translateY(-30px) rotate(5deg) scale(1.05);
|
| 117 |
+
opacity: 0;
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/* Main Content */
|
| 122 |
+
.main-content {
|
| 123 |
+
position: relative;
|
| 124 |
+
z-index: 2;
|
| 125 |
+
display: flex;
|
| 126 |
+
flex-direction: column;
|
| 127 |
+
align-items: center;
|
| 128 |
+
min-height: 100vh;
|
| 129 |
+
padding: 2rem;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
/* Enhanced Header */
|
| 133 |
+
header {
|
| 134 |
+
text-align: center;
|
| 135 |
+
margin-bottom: 3rem;
|
| 136 |
+
position: relative;
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
header::before {
|
| 140 |
+
content: '';
|
| 141 |
+
position: absolute;
|
| 142 |
+
top: -20px;
|
| 143 |
+
left: 50%;
|
| 144 |
+
transform: translateX(-50%);
|
| 145 |
+
width: 100px;
|
| 146 |
+
height: 4px;
|
| 147 |
+
background: linear-gradient(90deg, var(--slate-blue), var(--golden-brown), var(--accent-coral));
|
| 148 |
+
border-radius: 2px;
|
| 149 |
+
animation: headerAccent 3s ease infinite;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
@keyframes headerAccent {
|
| 153 |
+
0%, 100% { width: 100px; opacity: 0.7; }
|
| 154 |
+
50% { width: 150px; opacity: 1; }
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
h1 {
|
| 158 |
+
font-size: clamp(2rem, 5vw, 3.5rem);
|
| 159 |
+
font-weight: 700;
|
| 160 |
+
margin: 0.5rem 0;
|
| 161 |
+
background: linear-gradient(
|
| 162 |
+
135deg,
|
| 163 |
+
var(--slate-blue) 0%,
|
| 164 |
+
var(--warm-brown) 30%,
|
| 165 |
+
var(--golden-brown) 60%,
|
| 166 |
+
var(--slate-dark) 100%
|
| 167 |
+
);
|
| 168 |
+
background-size: 200% auto;
|
| 169 |
+
-webkit-background-clip: text;
|
| 170 |
+
background-clip: text;
|
| 171 |
+
color: transparent;
|
| 172 |
+
animation: textGradient 8s linear infinite;
|
| 173 |
+
text-shadow: 0 0 30px rgba(77, 100, 115, 0.3);
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
@keyframes textGradient {
|
| 177 |
+
0% { background-position: 0% center; }
|
| 178 |
+
100% { background-position: 200% center; }
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
#tagline {
|
| 182 |
+
font-size: 1.2rem;
|
| 183 |
+
font-weight: 400;
|
| 184 |
+
color: var(--text-primary);
|
| 185 |
+
opacity: 0.8;
|
| 186 |
+
min-height: 2em;
|
| 187 |
+
position: relative;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
#tagline::after {
|
| 191 |
+
content: '|';
|
| 192 |
+
animation: blink 1s infinite;
|
| 193 |
+
color: var(--slate-blue);
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
@keyframes blink {
|
| 197 |
+
0%, 50% { opacity: 1; }
|
| 198 |
+
51%, 100% { opacity: 0; }
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
/* Enhanced Controls */
|
| 202 |
+
.controls {
|
| 203 |
+
display: grid;
|
| 204 |
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
| 205 |
+
gap: 2rem;
|
| 206 |
+
width: 100%;
|
| 207 |
+
max-width: 1000px;
|
| 208 |
+
margin-bottom: 3rem;
|
| 209 |
+
padding: 3rem;
|
| 210 |
+
background: var(--glass-bg);
|
| 211 |
+
backdrop-filter: blur(20px);
|
| 212 |
+
-webkit-backdrop-filter: blur(20px);
|
| 213 |
+
border: 1px solid var(--glass-border);
|
| 214 |
+
border-radius: 24px;
|
| 215 |
+
box-shadow: var(--shadow-soft);
|
| 216 |
+
position: relative;
|
| 217 |
+
overflow: hidden;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
.controls::before {
|
| 221 |
+
content: '';
|
| 222 |
+
position: absolute;
|
| 223 |
+
top: 0;
|
| 224 |
+
left: -100%;
|
| 225 |
+
width: 100%;
|
| 226 |
+
height: 2px;
|
| 227 |
+
background: linear-gradient(90deg, transparent, var(--slate-blue), transparent);
|
| 228 |
+
animation: borderGlow 4s ease-in-out infinite;
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
@keyframes borderGlow {
|
| 232 |
+
0% { left: -100%; }
|
| 233 |
+
100% { left: 100%; }
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
.control-group {
|
| 237 |
+
display: flex;
|
| 238 |
+
flex-direction: column;
|
| 239 |
+
gap: 0.5rem;
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
.control-group label {
|
| 243 |
+
font-weight: 600;
|
| 244 |
+
font-size: 0.9rem;
|
| 245 |
+
text-transform: uppercase;
|
| 246 |
+
letter-spacing: 0.5px;
|
| 247 |
+
color: var(--text-primary);
|
| 248 |
+
margin-bottom: 0.5rem;
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
.controls select,
|
| 252 |
+
.controls input[type="text"],
|
| 253 |
+
.controls input[type="number"] {
|
| 254 |
+
height: 52px;
|
| 255 |
+
padding: 0 1.5rem;
|
| 256 |
+
background: rgba(255, 255, 255, 0.4);
|
| 257 |
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
| 258 |
+
border-radius: 16px;
|
| 259 |
+
color: var(--text-primary);
|
| 260 |
+
font-size: 1rem;
|
| 261 |
+
font-family: 'Inter', sans-serif;
|
| 262 |
+
font-weight: 500;
|
| 263 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 264 |
+
appearance: none;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
.controls select {
|
| 268 |
+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%234D6473' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
|
| 269 |
+
background-repeat: no-repeat;
|
| 270 |
+
background-position: right 1.5rem center;
|
| 271 |
+
background-size: 1em;
|
| 272 |
+
padding-right: 3rem;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
.controls select:focus,
|
| 276 |
+
.controls input:focus {
|
| 277 |
+
outline: none;
|
| 278 |
+
border-color: var(--slate-blue);
|
| 279 |
+
background: rgba(255, 255, 255, 0.6);
|
| 280 |
+
box-shadow: 0 0 0 4px rgba(77, 100, 115, 0.2);
|
| 281 |
+
transform: translateY(-2px);
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
.seed-wrapper {
|
| 285 |
+
display: flex;
|
| 286 |
+
align-items: stretch;
|
| 287 |
+
position: relative;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
.seed-wrapper input {
|
| 291 |
+
flex: 1;
|
| 292 |
+
border-radius: 16px 16px 16px 16px;
|
| 293 |
+
border-right: none;
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
.seed-wrapper button {
|
| 297 |
+
height: 52px;
|
| 298 |
+
/* padding: 0; */
|
| 299 |
+
background: rgba(255, 255, 255, 0.4);
|
| 300 |
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
| 301 |
+
border-left: none;
|
| 302 |
+
border-radius: 16px 16px 16px 16px;
|
| 303 |
+
cursor: pointer;
|
| 304 |
+
transition: all 0.3s ease;
|
| 305 |
+
font-size: 1.2rem;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
.seed-wrapper button:hover {
|
| 309 |
+
background: var(--golden-brown);
|
| 310 |
+
color: white;
|
| 311 |
+
transform: scale(1.05);
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
/* Enhanced Action Buttons */
|
| 315 |
+
.action-buttons {
|
| 316 |
+
display: flex;
|
| 317 |
+
gap: 1.5rem;
|
| 318 |
+
margin-bottom: 3rem;
|
| 319 |
+
flex-wrap: wrap;
|
| 320 |
+
justify-content: center;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
.action-buttons button {
|
| 324 |
+
position: relative;
|
| 325 |
+
padding: 1rem 2rem;
|
| 326 |
+
border: none;
|
| 327 |
+
border-radius: 50px;
|
| 328 |
+
font-family: 'Inter', sans-serif;
|
| 329 |
+
font-weight: 600;
|
| 330 |
+
font-size: 1.1rem;
|
| 331 |
+
cursor: pointer;
|
| 332 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 333 |
+
overflow: hidden;
|
| 334 |
+
box-shadow: var(--shadow-soft);
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
.action-buttons button::before {
|
| 338 |
+
content: '';
|
| 339 |
+
position: absolute;
|
| 340 |
+
top: 0;
|
| 341 |
+
left: -100%;
|
| 342 |
+
width: 100%;
|
| 343 |
+
height: 100%;
|
| 344 |
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
| 345 |
+
transition: left 0.6s ease;
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
.action-buttons button:hover::before {
|
| 349 |
+
left: 100%;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
#generate-btn {
|
| 353 |
+
background: linear-gradient(135deg, var(--slate-blue), var(--slate-dark));
|
| 354 |
+
color: white;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
#surprise-btn {
|
| 358 |
+
background: linear-gradient(135deg, var(--golden-brown), var(--warm-brown));
|
| 359 |
+
color: white;
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
.action-buttons button:hover {
|
| 363 |
+
transform: translateY(-3px) scale(1.05);
|
| 364 |
+
box-shadow: var(--shadow-hover);
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
.action-buttons button:active {
|
| 368 |
+
transform: translateY(-1px) scale(1.02);
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
.action-buttons button:disabled {
|
| 372 |
+
opacity: 0.6;
|
| 373 |
+
cursor: not-allowed;
|
| 374 |
+
transform: none;
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
/* Enhanced Output Area */
|
| 378 |
+
.output {
|
| 379 |
+
width: 100%;
|
| 380 |
+
max-width: 900px;
|
| 381 |
+
text-align: center;
|
| 382 |
+
justify-content: center;
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
.status {
|
| 386 |
+
font-size: 1.2rem;
|
| 387 |
+
font-weight: 500;
|
| 388 |
+
margin-bottom: 2rem;
|
| 389 |
+
min-height: 1.5em;
|
| 390 |
+
color: var(--text-primary);
|
| 391 |
+
position: relative;
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
#result-image {
|
| 395 |
+
max-width: 100%;
|
| 396 |
+
max-height: 70vh;
|
| 397 |
+
border-radius: 20px;
|
| 398 |
+
border: 3px solid var(--glass-border);
|
| 399 |
+
margin-bottom: 2rem;
|
| 400 |
+
box-shadow: var(--shadow-hover);
|
| 401 |
+
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
#result-image:hover {
|
| 405 |
+
transform: scale(1.02);
|
| 406 |
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
#loader {
|
| 410 |
+
width: 60px;
|
| 411 |
+
height: 60px;
|
| 412 |
+
margin: 2rem auto;
|
| 413 |
+
position: relative;
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
#loader::before,
|
| 417 |
+
#loader::after {
|
| 418 |
+
content: '';
|
| 419 |
+
position: absolute;
|
| 420 |
+
border-radius: 50%;
|
| 421 |
+
animation: loader 2s ease-in-out infinite;
|
| 422 |
+
}
|
| 423 |
+
|
| 424 |
+
#loader::before {
|
| 425 |
+
width: 60px;
|
| 426 |
+
height: 60px;
|
| 427 |
+
border: 4px solid var(--slate-blue);
|
| 428 |
+
border-top-color: transparent;
|
| 429 |
+
animation: spin 1s linear infinite;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
#loader::after {
|
| 433 |
+
width: 40px;
|
| 434 |
+
height: 40px;
|
| 435 |
+
top: 10px;
|
| 436 |
+
left: 10px;
|
| 437 |
+
border: 4px solid var(--golden-brown);
|
| 438 |
+
border-bottom-color: transparent;
|
| 439 |
+
animation: spin 1.5s linear infinite reverse;
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
@keyframes spin {
|
| 443 |
+
to { transform: rotate(360deg); }
|
| 444 |
+
}
|
| 445 |
+
|
| 446 |
+
#post-generation-tools {
|
| 447 |
+
display: flex;
|
| 448 |
+
justify-content: center;
|
| 449 |
+
gap: 1rem;
|
| 450 |
+
margin: 1rem 0 2rem;
|
| 451 |
+
opacity: 0;
|
| 452 |
+
transform: translateY(20px);
|
| 453 |
+
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
#post-generation-tools:not(.hidden) {
|
| 457 |
+
opacity: 1;
|
| 458 |
+
transform: translateY(0);
|
| 459 |
+
}
|
| 460 |
+
|
| 461 |
+
#post-generation-tools button {
|
| 462 |
+
display: flex;
|
| 463 |
+
align-items: center;
|
| 464 |
+
gap: 0.5rem;
|
| 465 |
+
padding: 0.75rem 1.5rem;
|
| 466 |
+
background: var(--glass-bg);
|
| 467 |
+
border: 1px solid var(--glass-border);
|
| 468 |
+
border-radius: 25px;
|
| 469 |
+
color: var(--text-primary);
|
| 470 |
+
font-family: 'Inter', sans-serif;
|
| 471 |
+
font-weight: 500;
|
| 472 |
+
cursor: pointer;
|
| 473 |
+
backdrop-filter: blur(10px);
|
| 474 |
+
transition: all 0.3s ease;
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
#post-generation-tools button:hover {
|
| 478 |
+
background: rgba(255, 255, 255, 0.3);
|
| 479 |
+
transform: translateY(-2px);
|
| 480 |
+
box-shadow: var(--shadow-soft);
|
| 481 |
+
}
|
| 482 |
+
|
| 483 |
+
#palette-container {
|
| 484 |
+
display: flex;
|
| 485 |
+
justify-content: center;
|
| 486 |
+
flex-wrap: wrap;
|
| 487 |
+
gap: 1rem;
|
| 488 |
+
margin-top: 2rem;
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
.color-swatch-wrapper {
|
| 492 |
+
display: flex;
|
| 493 |
+
flex-direction: column;
|
| 494 |
+
align-items: center;
|
| 495 |
+
gap: 0.5rem;
|
| 496 |
+
transition: all 0.3s ease;
|
| 497 |
+
}
|
| 498 |
+
|
| 499 |
+
.color-swatch-wrapper:hover {
|
| 500 |
+
transform: translateY(-5px);
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
.color-swatch {
|
| 504 |
+
width: 50px;
|
| 505 |
+
height: 50px;
|
| 506 |
+
border-radius: 12px;
|
| 507 |
+
border: 3px solid white;
|
| 508 |
+
cursor: pointer;
|
| 509 |
+
box-shadow: var(--shadow-soft);
|
| 510 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
.color-swatch:hover {
|
| 514 |
+
transform: scale(1.2) rotate(5deg);
|
| 515 |
+
box-shadow: var(--shadow-hover);
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
.hex-code {
|
| 519 |
+
font-family: 'Courier New', monospace;
|
| 520 |
+
font-size: 0.8rem;
|
| 521 |
+
font-weight: 500;
|
| 522 |
+
background: rgba(255, 255, 255, 0.6);
|
| 523 |
+
padding: 2px 8px;
|
| 524 |
+
border-radius: 8px;
|
| 525 |
+
color: var(--text-primary);
|
| 526 |
+
backdrop-filter: blur(5px);
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
#recent-creations {
|
| 530 |
+
width: 100%;
|
| 531 |
+
max-width: 1200px;
|
| 532 |
+
margin: 4rem auto 2rem;
|
| 533 |
+
padding: 0 2rem;
|
| 534 |
+
opacity: 0;
|
| 535 |
+
transform: translateY(30px);
|
| 536 |
+
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
#recent-creations:not(.hidden) {
|
| 540 |
+
opacity: 1;
|
| 541 |
+
transform: translateY(0);
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
#recent-creations h2 {
|
| 545 |
+
text-align: center;
|
| 546 |
+
font-size: 2rem;
|
| 547 |
+
font-weight: 600;
|
| 548 |
+
margin-bottom: 2rem;
|
| 549 |
+
background: linear-gradient(135deg, var(--slate-blue), var(--warm-brown));
|
| 550 |
+
-webkit-background-clip: text;
|
| 551 |
+
background-clip: text;
|
| 552 |
+
color: transparent;
|
| 553 |
+
}
|
| 554 |
+
|
| 555 |
+
#recent-gallery {
|
| 556 |
+
display: flex;
|
| 557 |
+
gap: 1.5rem;
|
| 558 |
+
overflow-x: auto;
|
| 559 |
+
padding: 1rem;
|
| 560 |
+
scrollbar-width: none;
|
| 561 |
+
-ms-overflow-style: none;
|
| 562 |
+
}
|
| 563 |
+
|
| 564 |
+
#recent-gallery::-webkit-scrollbar {
|
| 565 |
+
display: none;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
#recent-gallery img {
|
| 569 |
+
height: 140px;
|
| 570 |
+
border-radius: 12px;
|
| 571 |
+
border: 2px solid var(--glass-border);
|
| 572 |
+
cursor: pointer;
|
| 573 |
+
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
| 574 |
+
box-shadow: var(--shadow-soft);
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
#recent-gallery img:hover {
|
| 578 |
+
transform: scale(1.1) rotate(2deg);
|
| 579 |
+
box-shadow: var(--shadow-hover);
|
| 580 |
+
border-color: var(--slate-blue);
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
.side-text {
|
| 584 |
+
position: fixed;
|
| 585 |
+
bottom: 2rem;
|
| 586 |
+
right: 2rem;
|
| 587 |
+
z-index: 10;
|
| 588 |
+
}
|
| 589 |
+
|
| 590 |
+
.side-text span {
|
| 591 |
+
font-size: 0.8rem;
|
| 592 |
+
color: rgba(62, 59, 57, 0.6);
|
| 593 |
+
letter-spacing: 0.1em;
|
| 594 |
+
text-transform: uppercase;
|
| 595 |
+
font-weight: 500;
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
.hidden { display: none !important; }
|
| 599 |
+
|
| 600 |
+
@media (max-width: 768px) {
|
| 601 |
+
.controls {
|
| 602 |
+
grid-template-columns: 1fr;
|
| 603 |
+
padding: 2rem;
|
| 604 |
+
}
|
| 605 |
+
|
| 606 |
+
.action-buttons {
|
| 607 |
+
flex-direction: column;
|
| 608 |
+
align-items: center;
|
| 609 |
+
}
|
| 610 |
+
.action-buttons button {
|
| 611 |
+
width: 100%;
|
| 612 |
+
max-width: 300px;
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
#post-generation-tools {
|
| 616 |
+
flex-direction: column;
|
| 617 |
+
align-items: center;
|
| 618 |
+
}
|
| 619 |
+
}
|
| 620 |
+
.random-seed-btn{
|
| 621 |
+
left: unset;
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
.theme-warm { background: linear-gradient(135deg, var(--golden-brown), var(--warm-brown)); }
|
| 625 |
+
.theme-cool { background: linear-gradient(135deg, var(--slate-blue), var(--slate-dark)); }
|
| 626 |
+
.theme-coral { background: linear-gradient(135deg, var(--accent-coral), var(--accent-pink)); }
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch --index-url https://download.pytorch.org/whl/cpu
|
| 2 |
+
fastapi
|
| 3 |
+
uvicorn[standard]
|
| 4 |
+
accelerate
|
| 5 |
+
diffusers
|
| 6 |
+
scikit-learn
|
| 7 |
+
Pillow
|
| 8 |
+
numpy
|
| 9 |
+
python-multipart
|