Ankit commited on
Commit
c476ef5
·
1 Parent(s): daa6294

application files

Browse files
.gitattributes CHANGED
@@ -1,35 +1 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
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

  • SHA256: 24310a6ae80a2f1dc79aa8d5c5bd60208a48ebf9271e29c1d48d3a56e7790386
  • Pointer size: 132 Bytes
  • Size of remote file: 1.96 MB
frontend/build/examples/img2.png ADDED

Git LFS Details

  • SHA256: 93a73cdb9fcddbc95461d1a15c2443c28ac505dfc32fe953964b181478182e53
  • Pointer size: 132 Bytes
  • Size of remote file: 3.38 MB
frontend/build/examples/img3.png ADDED

Git LFS Details

  • SHA256: e3454a0070680cb8be1c0d6bf909866184d0a14e22d779be94df7681ed15e112
  • Pointer size: 132 Bytes
  • Size of remote file: 1.38 MB
frontend/build/examples/img4.png ADDED

Git LFS Details

  • SHA256: 0095fffefa0c53daec127a7873ff7bfc6e1806fd4f94850b1131274f9e37419d
  • Pointer size: 132 Bytes
  • Size of remote file: 1.94 MB
frontend/build/examples/img5.png ADDED

Git LFS Details

  • SHA256: 406d7d3204acc5fb77afa6f9abf9d4c1f951184a8b3ca8a776651e88347d1dac
  • Pointer size: 132 Bytes
  • Size of remote file: 1.44 MB
frontend/build/examples/img6.png ADDED

Git LFS Details

  • SHA256: b3d3ee940a93d1dc2fe83c1cc1bdc734f527d21603fc04c98aaf0c13856e33f7
  • Pointer size: 132 Bytes
  • Size of remote file: 1.81 MB
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>&copy; 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