AdarshJi commited on
Commit
2e342df
·
verified ·
1 Parent(s): 14b2cb1

Upload 5 files

Browse files
Files changed (5) hide show
  1. app.py +211 -0
  2. static/css/styles.css +1832 -0
  3. static/data/history.json +1 -0
  4. static/js/main.js +978 -0
  5. templates/index.html +406 -0
app.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, render_template, send_from_directory
2
+ import requests
3
+ import time
4
+ import os
5
+ import random
6
+ import json
7
+
8
+ app = Flask(__name__, static_folder='static', template_folder='templates')
9
+
10
+ # Image generation API functions
11
+ def get_common_headers():
12
+ return {
13
+ "Accept": "application/json, text/javascript, */*; q=0.01",
14
+ "Content-Type": "application/json; charset=UTF-8",
15
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
16
+ "X-Requested-With": "XMLHttpRequest"
17
+ }
18
+
19
+ def get_upgraded_prompt(prompt):
20
+ url = "https://editor.imagelabs.net/upgrade_prompt"
21
+ payload = {"prompt": prompt}
22
+ headers = get_common_headers()
23
+
24
+ try:
25
+ response = requests.post(url, json=payload, headers=headers)
26
+ response.raise_for_status()
27
+ data = response.json()
28
+ return data.get("upgraded_prompt") or data.get("prompt") or prompt
29
+ except Exception as e:
30
+ print(f"Error upgrading prompt: {e}")
31
+ return prompt
32
+
33
+ def generate_image(prompt, seed, subseed, attention, width, height, tiling, negative_prompt, reference_image, reference_image_type, reference_strength):
34
+ url = "https://editor.imagelabs.net/txt2img"
35
+ payload = {
36
+ "prompt": prompt,
37
+ "seed": seed,
38
+ "subseed": subseed,
39
+ "attention": attention,
40
+ "width": width,
41
+ "height": height,
42
+ "tiling": tiling,
43
+ "negative_prompt": negative_prompt,
44
+ "reference_image": reference_image,
45
+ "reference_image_type": reference_image_type,
46
+ "reference_strength": reference_strength
47
+ }
48
+ headers = get_common_headers()
49
+
50
+ try:
51
+ response = requests.post(url, json=payload, headers=headers)
52
+ response.raise_for_status()
53
+ data = response.json()
54
+ return data.get("task_id")
55
+ except Exception as e:
56
+ print(f"Error generating image: {e}")
57
+ raise
58
+
59
+ def poll_progress(task_id):
60
+ url = "https://editor.imagelabs.net/progress"
61
+ payload = {"task_id": task_id}
62
+ headers = get_common_headers()
63
+ max_attempts = 15
64
+ attempt = 0
65
+
66
+ while attempt < max_attempts:
67
+ try:
68
+ response = requests.post(url, json=payload, headers=headers)
69
+ response.raise_for_status()
70
+ data = response.json()
71
+
72
+ final_image_url = data.get("final_image_url")
73
+ status = (data.get("status") or "").lower()
74
+
75
+ if final_image_url:
76
+ return final_image_url
77
+ if status == "done":
78
+ return None
79
+
80
+ # Wait before next poll
81
+ time.sleep(2)
82
+ attempt += 1
83
+ except Exception as e:
84
+ print(f"Error polling progress: {e}")
85
+ raise
86
+
87
+ raise Exception("Polling timed out after 30 seconds")
88
+
89
+ # Routes
90
+ @app.route('/')
91
+ def index():
92
+ return render_template('index.html')
93
+
94
+ @app.route('/healthcheck')
95
+ def healthcheck():
96
+ return "Flask application is running!"
97
+
98
+ @app.route('/api/upgrade-prompt', methods=['POST'])
99
+ def upgrade_prompt():
100
+ data = request.json
101
+ if not data or 'prompt' not in data or not data['prompt']:
102
+ return jsonify({"error": "Invalid prompt. Please provide a text prompt."}), 400
103
+
104
+ try:
105
+ upgraded_prompt = get_upgraded_prompt(data['prompt'])
106
+ return jsonify({"upgradedPrompt": upgraded_prompt})
107
+ except Exception as e:
108
+ return jsonify({"error": str(e)}), 500
109
+
110
+ @app.route('/api/generate', methods=['POST'])
111
+ def api_generate_image():
112
+ data = request.json
113
+ if not data or 'prompt' not in data or not data['prompt']:
114
+ return jsonify({"error": "Invalid prompt. Please provide a text prompt."}), 400
115
+
116
+ try:
117
+ prompt = data['prompt']
118
+ seed = data.get('seed', random.randint(0, 999999))
119
+ subseed = data.get('subseed', 0)
120
+ attention = data.get('attention', 0.5)
121
+ width = data.get('width', 512)
122
+ height = data.get('height', 512)
123
+ tiling = data.get('tiling', False)
124
+ negative_prompt = data.get('negative_prompt', "")
125
+ reference_image = data.get('reference_image')
126
+ reference_image_type = data.get('reference_image_type')
127
+ reference_strength = data.get('reference_strength', 0.5)
128
+
129
+ task_id = generate_image(
130
+ prompt, seed, subseed, attention, width, height, tiling,
131
+ negative_prompt, reference_image, reference_image_type, reference_strength
132
+ )
133
+
134
+ return jsonify({"taskId": task_id, "status": "processing"})
135
+ except Exception as e:
136
+ return jsonify({"error": str(e)}), 500
137
+
138
+ @app.route('/api/poll', methods=['POST'])
139
+ def api_poll_progress():
140
+ data = request.json
141
+ if not data or 'taskId' not in data or not data['taskId']:
142
+ return jsonify({"error": "Invalid task ID. Please provide a valid task ID."}), 400
143
+
144
+ try:
145
+ task_id = data['taskId']
146
+ image_url = poll_progress(task_id)
147
+
148
+ return jsonify({
149
+ "final_image_url": image_url,
150
+ "status": "done" if image_url else "processing"
151
+ })
152
+ except Exception as e:
153
+ return jsonify({"error": str(e)}), 500
154
+
155
+ # Helper route to save image history
156
+ @app.route('/api/save-history', methods=['POST'])
157
+ def save_history():
158
+ data = request.json
159
+
160
+ try:
161
+ history_file = os.path.join(app.static_folder, 'data', 'history.json')
162
+ os.makedirs(os.path.dirname(history_file), exist_ok=True)
163
+
164
+ # Read existing history
165
+ history = []
166
+ if os.path.exists(history_file):
167
+ with open(history_file, 'r') as f:
168
+ try:
169
+ history = json.load(f)
170
+ except:
171
+ history = []
172
+
173
+ # Add new image
174
+ if data and isinstance(history, list):
175
+ # Set an ID if none exists
176
+ if 'id' not in data:
177
+ data['id'] = str(int(time.time() * 1000))
178
+
179
+ # Add to the beginning of the array
180
+ history.insert(0, data)
181
+
182
+ # Keep only the last 20 images
183
+ history = history[:20]
184
+
185
+ with open(history_file, 'w') as f:
186
+ json.dump(history, f)
187
+
188
+ return jsonify({"success": True})
189
+
190
+ return jsonify({"error": "Invalid data format"}), 400
191
+ except Exception as e:
192
+ return jsonify({"error": str(e)}), 500
193
+
194
+ @app.route('/api/get-history', methods=['GET'])
195
+ def get_history():
196
+ try:
197
+ history_file = os.path.join(app.static_folder, 'data', 'history.json')
198
+
199
+ if os.path.exists(history_file):
200
+ with open(history_file, 'r') as f:
201
+ history = json.load(f)
202
+ return jsonify({"images": history})
203
+
204
+ return jsonify({"images": []})
205
+ except Exception as e:
206
+ return jsonify({"error": str(e)}), 500
207
+
208
+ if __name__ == '__main__':
209
+ # Create data directory if it doesn't exist
210
+ os.makedirs(os.path.join(app.static_folder, 'data'), exist_ok=True)
211
+ app.run(host='0.0.0.0', port=8080, debug=True)
static/css/styles.css ADDED
@@ -0,0 +1,1832 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Variables */
2
+ :root {
3
+ /* Dark Theme (Default) */
4
+ --bg-color: #0d0f18;
5
+ --bg-secondary: #12141e;
6
+ --bg-tertiary: #171a26;
7
+ --bg-card: #1e2132;
8
+ --text-color: #f5f6fa;
9
+ --text-secondary: #a2a8bc;
10
+ --text-tertiary: #717790;
11
+ --border-color: #2c3049;
12
+ --primary-color: #6366f1;
13
+ --primary-hover: #818cf8;
14
+ --primary-light: rgba(99, 102, 241, 0.15);
15
+ --secondary-color: #334155;
16
+ --secondary-hover: #475569;
17
+ --danger-color: #ef4444;
18
+ --success-color: #10b981;
19
+ --warning-color: #f59e0b;
20
+ --info-color: #3b82f6;
21
+ --shadow-color: rgba(0, 0, 0, 0.3);
22
+ --modal-overlay: rgba(0, 0, 0, 0.75);
23
+ --gradient-start: #6366f1;
24
+ --gradient-middle: #8b5cf6;
25
+ --gradient-end: #d946ef;
26
+ --placeholder-color: #202437;
27
+ --glass-bg: rgba(18, 20, 30, 0.7);
28
+ --blur-amount: 10px;
29
+ --border-radius: 1rem;
30
+ --button-radius: 0.75rem;
31
+ --card-radius: 1.25rem;
32
+ --transition-slow: 0.5s;
33
+ --transition-medium: 0.3s;
34
+ --transition-fast: 0.15s;
35
+ }
36
+
37
+ /* Light Theme */
38
+ .light-theme {
39
+ --bg-color: #f8f9fc;
40
+ --bg-secondary: #ffffff;
41
+ --bg-tertiary: #eef2ff;
42
+ --bg-card: #ffffff;
43
+ --text-color: #1e293b;
44
+ --text-secondary: #475569;
45
+ --text-tertiary: #94a3b8;
46
+ --border-color: #e2e8f0;
47
+ --primary-color: #4f46e5;
48
+ --primary-hover: #4338ca;
49
+ --primary-light: rgba(79, 70, 229, 0.1);
50
+ --secondary-color: #e2e8f0;
51
+ --secondary-hover: #cbd5e1;
52
+ --shadow-color: rgba(15, 23, 42, 0.1);
53
+ --modal-overlay: rgba(15, 23, 42, 0.5);
54
+ --placeholder-color: #f1f5f9;
55
+ --glass-bg: rgba(255, 255, 255, 0.8);
56
+ }
57
+
58
+ /* Reset & Base Styles */
59
+ *, *::before, *::after {
60
+ box-sizing: border-box;
61
+ margin: 0;
62
+ padding: 0;
63
+ }
64
+
65
+ html {
66
+ scroll-behavior: smooth;
67
+ }
68
+
69
+ body {
70
+ font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
71
+ background-color: var(--bg-color);
72
+ color: var(--text-color);
73
+ transition: background-color var(--transition-medium), color var(--transition-medium);
74
+ line-height: 1.6;
75
+ overflow-x: hidden;
76
+ min-height: 100vh;
77
+ }
78
+
79
+ h1, h2, h3, h4, h5, h6 {
80
+ font-family: 'Plus Jakarta Sans', sans-serif;
81
+ font-weight: 700;
82
+ margin-bottom: 1rem;
83
+ line-height: 1.3;
84
+ }
85
+
86
+ /* Typography */
87
+ h1 {
88
+ font-size: 2.5rem;
89
+ }
90
+
91
+ h2 {
92
+ font-size: 1.75rem;
93
+ }
94
+
95
+ h3 {
96
+ font-size: 1.3rem;
97
+ }
98
+
99
+ small, .small {
100
+ font-size: 0.875rem;
101
+ }
102
+
103
+ /* Animations */
104
+ @keyframes fadeIn {
105
+ from { opacity: 0; }
106
+ to { opacity: 1; }
107
+ }
108
+
109
+ @keyframes slideUp {
110
+ from { transform: translateY(30px); opacity: 0; }
111
+ to { transform: translateY(0); opacity: 1; }
112
+ }
113
+
114
+ @keyframes slideDown {
115
+ from { transform: translateY(-30px); opacity: 0; }
116
+ to { transform: translateY(0); opacity: 1; }
117
+ }
118
+
119
+ @keyframes slideLeft {
120
+ from { transform: translateX(30px); opacity: 0; }
121
+ to { transform: translateX(0); opacity: 1; }
122
+ }
123
+
124
+ @keyframes slideRight {
125
+ from { transform: translateX(-30px); opacity: 0; }
126
+ to { transform: translateX(0); opacity: 1; }
127
+ }
128
+
129
+ @keyframes pulse {
130
+ 0% { transform: scale(1); }
131
+ 50% { transform: scale(1.05); }
132
+ 100% { transform: scale(1); }
133
+ }
134
+
135
+ @keyframes rotateIn {
136
+ from { transform: rotate(-10deg) scale(0.9); opacity: 0; }
137
+ to { transform: rotate(0) scale(1); opacity: 1; }
138
+ }
139
+
140
+ @keyframes shimmer {
141
+ 0% { background-position: -1000px 0; }
142
+ 100% { background-position: 1000px 0; }
143
+ }
144
+
145
+ @keyframes dots {
146
+ 0%, 20% { transform: translateY(0); }
147
+ 50% { transform: translateY(-5px); }
148
+ 80%, 100% { transform: translateY(0); }
149
+ }
150
+
151
+ @keyframes gradientMove {
152
+ 0% { background-position: 0% 50%; }
153
+ 50% { background-position: 100% 50%; }
154
+ 100% { background-position: 0% 50%; }
155
+ }
156
+
157
+ @keyframes floatUp {
158
+ 0% { transform: translateY(0); }
159
+ 50% { transform: translateY(-10px); }
160
+ 100% { transform: translateY(0); }
161
+ }
162
+
163
+ @keyframes spin {
164
+ 0% { transform: rotate(0deg); }
165
+ 100% { transform: rotate(360deg); }
166
+ }
167
+
168
+ @keyframes bounce {
169
+ 0%, 100% { transform: translateY(0); }
170
+ 50% { transform: translateY(-10px); }
171
+ }
172
+
173
+ @keyframes zoomInOut {
174
+ 0%, 100% { transform: scale(1); }
175
+ 50% { transform: scale(1.2); }
176
+ }
177
+
178
+ @keyframes glowPulse {
179
+ 0%, 100% { box-shadow: 0 0 5px 0 var(--primary-color); }
180
+ 50% { box-shadow: 0 0 20px 5px var(--primary-color); }
181
+ }
182
+
183
+ /* Utility classes */
184
+ .hidden {
185
+ display: none !important;
186
+ }
187
+
188
+ .gradient-text {
189
+ background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
190
+ -webkit-background-clip: text;
191
+ background-clip: text;
192
+ color: transparent;
193
+ display: inline-block;
194
+ }
195
+
196
+ .setting-hint {
197
+ display: block;
198
+ color: var(--text-tertiary);
199
+ font-size: 0.8rem;
200
+ margin-top: 0.25rem;
201
+ }
202
+
203
+ .helper-tip {
204
+ display: block;
205
+ font-size: 0.85rem;
206
+ margin-top: 0.5rem;
207
+ color: var(--text-tertiary);
208
+ opacity: 0.8;
209
+ }
210
+
211
+ .pulse-effect {
212
+ animation: pulse 2s infinite;
213
+ }
214
+
215
+ .pulse-slow {
216
+ animation: pulse 4s infinite;
217
+ }
218
+
219
+ .float-animation {
220
+ animation: floatUp 6s ease-in-out infinite;
221
+ }
222
+
223
+ .fa-spin-hover:hover {
224
+ animation: spin 1.5s linear infinite;
225
+ }
226
+
227
+ .fa-spin-pulse {
228
+ animation: spin 15s linear infinite;
229
+ }
230
+
231
+ .pulse-animation {
232
+ position: relative;
233
+ overflow: hidden;
234
+ }
235
+
236
+ .pulse-animation::after {
237
+ content: '';
238
+ position: absolute;
239
+ top: 0;
240
+ left: 0;
241
+ right: 0;
242
+ bottom: 0;
243
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
244
+ animation: shimmer 2s infinite;
245
+ transform: skewX(-20deg);
246
+ }
247
+
248
+ .full-width {
249
+ width: 100% !important;
250
+ }
251
+
252
+ /* Loading Screen */
253
+ #loading-screen {
254
+ position: fixed;
255
+ top: 0;
256
+ left: 0;
257
+ width: 100%;
258
+ height: 100%;
259
+ display: flex;
260
+ justify-content: center;
261
+ align-items: center;
262
+ background-color: var(--bg-color);
263
+ z-index: 9999;
264
+ transition: opacity 0.8s ease, visibility 0.8s ease;
265
+ }
266
+
267
+ .loading-container {
268
+ display: flex;
269
+ flex-direction: column;
270
+ align-items: center;
271
+ gap: 3rem;
272
+ animation: fadeIn 0.8s ease-out;
273
+ }
274
+
275
+ .loading-logo {
276
+ position: relative;
277
+ margin-bottom: 1rem;
278
+ }
279
+
280
+ .logo-animation {
281
+ display: flex;
282
+ flex-direction: column;
283
+ align-items: center;
284
+ animation: pulse 3s infinite;
285
+ }
286
+
287
+ .logo-circle {
288
+ width: 100px;
289
+ height: 100px;
290
+ border-radius: 50%;
291
+ background: linear-gradient(135deg, var(--gradient-start), var(--gradient-middle), var(--gradient-end));
292
+ box-shadow: 0 10px 25px -5px rgba(99, 102, 241, 0.5);
293
+ margin-bottom: 1.5rem;
294
+ animation: zoomInOut 4s infinite;
295
+ }
296
+
297
+ .logo-text {
298
+ display: flex;
299
+ flex-direction: column;
300
+ align-items: center;
301
+ gap: 0.25rem;
302
+ }
303
+
304
+ .logo-text .gradient-text {
305
+ font-size: 2.5rem;
306
+ font-weight: 800;
307
+ letter-spacing: -1px;
308
+ }
309
+
310
+ .logo-text .subtext {
311
+ font-size: 1rem;
312
+ font-weight: 600;
313
+ color: var(--text-secondary);
314
+ letter-spacing: 2px;
315
+ text-transform: uppercase;
316
+ }
317
+
318
+ .loading-animation {
319
+ margin-bottom: 1.5rem;
320
+ }
321
+
322
+ .lds-ellipsis {
323
+ display: inline-block;
324
+ position: relative;
325
+ width: 80px;
326
+ height: 80px;
327
+ }
328
+
329
+ .lds-ellipsis div {
330
+ position: absolute;
331
+ top: 33px;
332
+ width: 13px;
333
+ height: 13px;
334
+ border-radius: 50%;
335
+ background: var(--primary-color);
336
+ animation-timing-function: cubic-bezier(0, 1, 1, 0);
337
+ }
338
+
339
+ .lds-ellipsis div:nth-child(1) {
340
+ left: 8px;
341
+ animation: lds-ellipsis1 0.6s infinite;
342
+ }
343
+
344
+ .lds-ellipsis div:nth-child(2) {
345
+ left: 8px;
346
+ animation: lds-ellipsis2 0.6s infinite;
347
+ }
348
+
349
+ .lds-ellipsis div:nth-child(3) {
350
+ left: 32px;
351
+ animation: lds-ellipsis2 0.6s infinite;
352
+ }
353
+
354
+ .lds-ellipsis div:nth-child(4) {
355
+ left: 56px;
356
+ animation: lds-ellipsis3 0.6s infinite;
357
+ }
358
+
359
+ @keyframes lds-ellipsis1 {
360
+ 0% { transform: scale(0); }
361
+ 100% { transform: scale(1); }
362
+ }
363
+
364
+ @keyframes lds-ellipsis2 {
365
+ 0% { transform: translate(0, 0); }
366
+ 100% { transform: translate(24px, 0); }
367
+ }
368
+
369
+ @keyframes lds-ellipsis3 {
370
+ 0% { transform: scale(1); }
371
+ 100% { transform: scale(0); }
372
+ }
373
+
374
+ .loading-text {
375
+ font-size: 1.35rem;
376
+ color: var(--text-secondary);
377
+ display: flex;
378
+ align-items: center;
379
+ }
380
+
381
+ .animated-text {
382
+ display: inline-block;
383
+ background: linear-gradient(90deg, var(--text-color), var(--text-secondary));
384
+ background-size: 200% auto;
385
+ color: transparent;
386
+ -webkit-background-clip: text;
387
+ background-clip: text;
388
+ animation: gradientMove 3s linear infinite;
389
+ }
390
+
391
+ .loading-dots {
392
+ display: inline-flex;
393
+ margin-left: 4px;
394
+ }
395
+
396
+ .loading-dots .dot {
397
+ animation: dots 1.4s infinite;
398
+ display: inline-block;
399
+ }
400
+
401
+ .loading-dots .dot:nth-child(2) {
402
+ animation-delay: 0.2s;
403
+ }
404
+
405
+ .loading-dots .dot:nth-child(3) {
406
+ animation-delay: 0.4s;
407
+ }
408
+
409
+ /* App Header */
410
+ .app-header {
411
+ position: fixed;
412
+ top: 0;
413
+ left: 0;
414
+ right: 0;
415
+ height: 70px;
416
+ background-color: var(--bg-secondary);
417
+ backdrop-filter: blur(var(--blur-amount));
418
+ -webkit-backdrop-filter: blur(var(--blur-amount));
419
+ border-bottom: 1px solid var(--border-color);
420
+ display: flex;
421
+ justify-content: space-between;
422
+ align-items: center;
423
+ padding: 0 1.5rem;
424
+ z-index: 20;
425
+ box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
426
+ }
427
+
428
+ .header-logo {
429
+ display: flex;
430
+ flex-direction: column;
431
+ align-items: flex-start;
432
+ }
433
+
434
+ .header-logo .gradient-text {
435
+ font-size: 1.4rem;
436
+ font-weight: 700;
437
+ letter-spacing: -0.5px;
438
+ }
439
+
440
+ .header-version {
441
+ font-size: 0.7rem;
442
+ font-weight: 600;
443
+ color: var(--text-tertiary);
444
+ text-transform: uppercase;
445
+ letter-spacing: 1px;
446
+ }
447
+
448
+ .header-controls {
449
+ display: flex;
450
+ align-items: center;
451
+ gap: 1rem;
452
+ }
453
+
454
+ /* Theme Toggle */
455
+ .theme-toggle {
456
+ background-color: var(--bg-tertiary);
457
+ color: var(--text-color);
458
+ width: 40px;
459
+ height: 40px;
460
+ border-radius: 50%;
461
+ border: 1px solid var(--border-color);
462
+ cursor: pointer;
463
+ display: flex;
464
+ align-items: center;
465
+ justify-content: center;
466
+ font-size: 1.1rem;
467
+ transition: background-color var(--transition-medium), transform var(--transition-medium), box-shadow var(--transition-medium);
468
+ box-shadow: 0 2px 10px var(--shadow-color);
469
+ }
470
+
471
+ .theme-toggle:hover {
472
+ background-color: var(--primary-color);
473
+ transform: translateY(-2px);
474
+ color: white;
475
+ box-shadow: 0 5px 15px rgba(99, 102, 241, 0.4);
476
+ }
477
+
478
+ /* Menu Toggle */
479
+ .menu-toggle {
480
+ display: flex;
481
+ align-items: center;
482
+ gap: 0.5rem;
483
+ background-color: var(--bg-tertiary);
484
+ color: var(--text-color);
485
+ border: 1px solid var(--border-color);
486
+ border-radius: var(--button-radius);
487
+ padding: 0.6rem 1rem;
488
+ font-size: 0.9rem;
489
+ cursor: pointer;
490
+ transition: all var(--transition-medium);
491
+ box-shadow: 0 2px 10px var(--shadow-color);
492
+ }
493
+
494
+ .menu-toggle:hover {
495
+ background-color: var(--primary-color);
496
+ color: white;
497
+ box-shadow: 0 5px 15px rgba(99, 102, 241, 0.4);
498
+ }
499
+
500
+ /* Main Container */
501
+ .main-container {
502
+ display: flex;
503
+ min-height: 100vh;
504
+ padding-top: 70px; /* Match header height */
505
+ }
506
+
507
+ /* Sidebar */
508
+ .sidebar {
509
+ width: 350px;
510
+ flex-shrink: 0;
511
+ background-color: var(--bg-secondary);
512
+ border-right: 1px solid var(--border-color);
513
+ min-height: calc(100vh - 70px);
514
+ padding: 1.5rem;
515
+ overflow-y: auto;
516
+ transition: transform var(--transition-medium) ease;
517
+ position: fixed;
518
+ left: 0;
519
+ top: 70px;
520
+ bottom: 0;
521
+ z-index: 18;
522
+ box-shadow: 5px 0 20px var(--shadow-color);
523
+ scrollbar-width: thin;
524
+ scrollbar-color: var(--text-tertiary) var(--bg-tertiary);
525
+ }
526
+
527
+ .sidebar::-webkit-scrollbar {
528
+ width: 6px;
529
+ }
530
+
531
+ .sidebar::-webkit-scrollbar-track {
532
+ background: var(--bg-tertiary);
533
+ }
534
+
535
+ .sidebar::-webkit-scrollbar-thumb {
536
+ background-color: var(--text-tertiary);
537
+ border-radius: 20px;
538
+ }
539
+
540
+ .sidebar-header {
541
+ display: flex;
542
+ justify-content: space-between;
543
+ align-items: center;
544
+ margin-bottom: 2rem;
545
+ }
546
+
547
+ .app-title {
548
+ font-size: 1.6rem;
549
+ background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
550
+ -webkit-background-clip: text;
551
+ background-clip: text;
552
+ color: transparent;
553
+ margin-bottom: 0;
554
+ }
555
+
556
+ .sidebar-close {
557
+ display: none;
558
+ background: transparent;
559
+ border: none;
560
+ color: var(--text-secondary);
561
+ font-size: 1.2rem;
562
+ cursor: pointer;
563
+ transition: color var(--transition-medium);
564
+ }
565
+
566
+ .sidebar-close:hover {
567
+ color: var(--danger-color);
568
+ }
569
+
570
+ .sidebar-content {
571
+ display: flex;
572
+ flex-direction: column;
573
+ gap: 2rem;
574
+ }
575
+
576
+ .sidebar-section-title {
577
+ font-size: 1rem;
578
+ color: var(--text-secondary);
579
+ margin-bottom: 1rem;
580
+ border-bottom: 1px solid var(--border-color);
581
+ padding-bottom: 0.5rem;
582
+ }
583
+
584
+ /* Latest Creation Preview */
585
+ .latest-creation-preview {
586
+ margin-bottom: 1rem;
587
+ }
588
+
589
+ .latest-creation {
590
+ background-color: var(--bg-tertiary);
591
+ border-radius: var(--card-radius);
592
+ overflow: hidden;
593
+ aspect-ratio: 1/1;
594
+ position: relative;
595
+ box-shadow: 0 4px 12px var(--shadow-color);
596
+ transition: transform var(--transition-medium);
597
+ }
598
+
599
+ .latest-creation:hover {
600
+ transform: translateY(-5px);
601
+ }
602
+
603
+ .latest-creation img {
604
+ width: 100%;
605
+ height: 100%;
606
+ object-fit: cover;
607
+ display: block;
608
+ }
609
+
610
+ .latest-creation-placeholder {
611
+ width: 100%;
612
+ height: 100%;
613
+ display: flex;
614
+ flex-direction: column;
615
+ align-items: center;
616
+ justify-content: center;
617
+ color: var(--text-tertiary);
618
+ padding: 1rem;
619
+ text-align: center;
620
+ }
621
+
622
+ .latest-creation-placeholder i {
623
+ font-size: 3rem;
624
+ margin-bottom: 1rem;
625
+ opacity: 0.7;
626
+ }
627
+
628
+ /* Form Elements */
629
+ .form-group {
630
+ margin-bottom: 1.5rem;
631
+ }
632
+
633
+ .form-row {
634
+ display: flex;
635
+ gap: 1rem;
636
+ margin-bottom: 1.5rem;
637
+ }
638
+
639
+ .form-row .form-group {
640
+ flex: 1;
641
+ margin-bottom: 0;
642
+ }
643
+
644
+ .form-actions {
645
+ display: flex;
646
+ flex-direction: column;
647
+ gap: 1rem;
648
+ }
649
+
650
+ label {
651
+ display: block;
652
+ margin-bottom: 0.5rem;
653
+ font-weight: 500;
654
+ color: var(--text-secondary);
655
+ font-size: 0.9rem;
656
+ }
657
+
658
+ textarea, input[type="text"], input[type="number"], select {
659
+ width: 100%;
660
+ padding: 0.75rem 1rem;
661
+ border: 1px solid var(--border-color);
662
+ border-radius: var(--button-radius);
663
+ background-color: var(--bg-tertiary);
664
+ color: var(--text-color);
665
+ font-family: inherit;
666
+ font-size: 0.95rem;
667
+ transition: border-color var(--transition-medium), box-shadow var(--transition-medium);
668
+ }
669
+
670
+ textarea:focus, input[type="text"]:focus, input[type="number"]:focus, select:focus {
671
+ outline: none;
672
+ border-color: var(--primary-color);
673
+ box-shadow: 0 0 0 3px var(--primary-light);
674
+ }
675
+
676
+ .prompt-container {
677
+ position: relative;
678
+ }
679
+
680
+ .upgrade-btn {
681
+ position: absolute;
682
+ bottom: 0.5rem;
683
+ right: 0.5rem;
684
+ background-color: var(--bg-tertiary);
685
+ border: 1px solid var(--border-color);
686
+ color: var(--text-secondary);
687
+ font-size: 1rem;
688
+ cursor: pointer;
689
+ transition: all var(--transition-medium);
690
+ padding: 0.4rem;
691
+ border-radius: 0.5rem;
692
+ }
693
+
694
+ .upgrade-btn:hover {
695
+ color: white;
696
+ background-color: var(--primary-color);
697
+ border-color: var(--primary-color);
698
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.5);
699
+ }
700
+
701
+ .seed-container {
702
+ display: flex;
703
+ gap: 0.5rem;
704
+ }
705
+
706
+ .seed-container input {
707
+ flex: 1;
708
+ }
709
+
710
+ .random-btn {
711
+ background-color: var(--bg-tertiary);
712
+ border: 1px solid var(--border-color);
713
+ color: var(--text-secondary);
714
+ width: 40px;
715
+ height: 40px;
716
+ display: flex;
717
+ align-items: center;
718
+ justify-content: center;
719
+ border-radius: var(--button-radius);
720
+ cursor: pointer;
721
+ transition: all var(--transition-medium);
722
+ }
723
+
724
+ .random-btn:hover {
725
+ color: white;
726
+ background-color: var(--primary-color);
727
+ border-color: var(--primary-color);
728
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.5);
729
+ }
730
+
731
+ .checkbox-group {
732
+ display: flex;
733
+ align-items: center;
734
+ gap: 0.5rem;
735
+ }
736
+
737
+ .checkbox-group input[type="checkbox"] {
738
+ width: 20px;
739
+ height: 20px;
740
+ accent-color: var(--primary-color);
741
+ cursor: pointer;
742
+ }
743
+
744
+ .checkbox-group label {
745
+ margin-bottom: 0;
746
+ cursor: pointer;
747
+ }
748
+
749
+ .slider-container {
750
+ display: flex;
751
+ align-items: center;
752
+ gap: 1rem;
753
+ }
754
+
755
+ input[type="range"] {
756
+ flex: 1;
757
+ -webkit-appearance: none;
758
+ height: 6px;
759
+ background: var(--secondary-color);
760
+ border-radius: 3px;
761
+ outline: none;
762
+ }
763
+
764
+ input[type="range"]::-webkit-slider-thumb {
765
+ -webkit-appearance: none;
766
+ width: 18px;
767
+ height: 18px;
768
+ background: var(--primary-color);
769
+ border-radius: 50%;
770
+ cursor: pointer;
771
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
772
+ }
773
+
774
+ .slider-container span {
775
+ min-width: 40px;
776
+ font-size: 0.9rem;
777
+ color: var(--text-secondary);
778
+ font-weight: 600;
779
+ }
780
+
781
+ /* Buttons */
782
+ .primary-btn, .secondary-btn, .control-btn {
783
+ display: flex;
784
+ align-items: center;
785
+ justify-content: center;
786
+ gap: 0.5rem;
787
+ padding: 0.85rem 1.5rem;
788
+ border-radius: var(--button-radius);
789
+ font-weight: 600;
790
+ cursor: pointer;
791
+ transition: all var(--transition-medium);
792
+ border: none;
793
+ font-size: 1rem;
794
+ font-family: inherit;
795
+ }
796
+
797
+ .primary-btn {
798
+ background-color: var(--primary-color);
799
+ color: white;
800
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
801
+ }
802
+
803
+ .primary-btn:hover:not(:disabled) {
804
+ background-color: var(--primary-hover);
805
+ transform: translateY(-2px);
806
+ box-shadow: 0 6px 15px rgba(99, 102, 241, 0.5);
807
+ }
808
+
809
+ .secondary-btn {
810
+ background-color: var(--secondary-color);
811
+ color: var(--text-color);
812
+ }
813
+
814
+ .secondary-btn:hover:not(:disabled) {
815
+ background-color: var(--secondary-hover);
816
+ transform: translateY(-2px);
817
+ }
818
+
819
+ .control-btn {
820
+ background-color: var(--bg-tertiary);
821
+ color: var(--text-secondary);
822
+ padding: 0.6rem 1rem;
823
+ font-size: 0.9rem;
824
+ border: 1px solid var(--border-color);
825
+ }
826
+
827
+ .control-btn:hover:not(:disabled) {
828
+ background-color: var(--primary-color);
829
+ color: white;
830
+ border-color: var(--primary-color);
831
+ }
832
+
833
+ .primary-btn:disabled, .secondary-btn:disabled, .control-btn:disabled {
834
+ opacity: 0.6;
835
+ cursor: not-allowed;
836
+ }
837
+
838
+ /* Main Content */
839
+ .main-content {
840
+ flex: 1;
841
+ padding: 2rem;
842
+ margin-left: 350px;
843
+ transition: margin-left var(--transition-medium) ease;
844
+ }
845
+
846
+ .content-section {
847
+ margin-bottom: 3rem;
848
+ animation: slideUp 0.5s ease-out;
849
+ }
850
+
851
+ .section-title {
852
+ font-size: 1.8rem;
853
+ margin-bottom: 1.5rem;
854
+ position: relative;
855
+ display: inline-block;
856
+ }
857
+
858
+ .section-title::after {
859
+ content: '';
860
+ position: absolute;
861
+ bottom: -5px;
862
+ left: 0;
863
+ width: 60%;
864
+ height: 3px;
865
+ background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
866
+ border-radius: 3px;
867
+ }
868
+
869
+ /* Image Display */
870
+ .image-display {
871
+ width: 100%;
872
+ min-height: 500px;
873
+ background-color: var(--bg-secondary);
874
+ border-radius: var(--card-radius);
875
+ overflow: hidden;
876
+ box-shadow: 0 8px 30px var(--shadow-color);
877
+ position: relative;
878
+ display: flex;
879
+ align-items: center;
880
+ justify-content: center;
881
+ margin-bottom: 1rem;
882
+ transition: transform var(--transition-medium);
883
+ }
884
+
885
+ .image-display:hover {
886
+ transform: translateY(-5px);
887
+ }
888
+
889
+ .placeholder-wrapper {
890
+ width: 100%;
891
+ height: 100%;
892
+ min-height: 500px;
893
+ background-color: var(--placeholder-color);
894
+ display: flex;
895
+ align-items: center;
896
+ justify-content: center;
897
+ opacity: 0.7;
898
+ }
899
+
900
+ .placeholder-content {
901
+ text-align: center;
902
+ padding: 2rem;
903
+ }
904
+
905
+ .placeholder-icon {
906
+ font-size: 5rem;
907
+ margin-bottom: 1.5rem;
908
+ opacity: 0.5;
909
+ color: var(--text-secondary);
910
+ }
911
+
912
+ .display-image {
913
+ width: 100%;
914
+ height: auto;
915
+ display: block;
916
+ animation: rotateIn 0.7s ease-out;
917
+ }
918
+
919
+ .image-display .loading-indicator {
920
+ position: absolute;
921
+ top: 50%;
922
+ left: 50%;
923
+ transform: translate(-50%, -50%);
924
+ z-index: 2;
925
+ }
926
+
927
+ .loading-shimmer {
928
+ position: absolute;
929
+ top: 0;
930
+ left: 0;
931
+ width: 100%;
932
+ height: 100%;
933
+ background: linear-gradient(to right,
934
+ var(--placeholder-color) 0%,
935
+ var(--bg-secondary) 50%,
936
+ var(--placeholder-color) 100%);
937
+ background-size: 1000px 100%;
938
+ animation: shimmer 2s infinite linear;
939
+ }
940
+
941
+ /* Image Controls */
942
+ .image-controls {
943
+ display: flex;
944
+ gap: 1rem;
945
+ margin-top: 1rem;
946
+ }
947
+
948
+ .image-controls .control-btn {
949
+ flex: 1;
950
+ }
951
+
952
+ /* History Gallery */
953
+ .history-gallery {
954
+ display: grid;
955
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
956
+ gap: 1.5rem;
957
+ }
958
+
959
+ .history-item {
960
+ position: relative;
961
+ cursor: pointer;
962
+ border-radius: var(--card-radius);
963
+ overflow: hidden;
964
+ transition: transform var(--transition-medium), box-shadow var(--transition-medium);
965
+ box-shadow: 0 4px 20px var(--shadow-color);
966
+ aspect-ratio: 1 / 1;
967
+ background-color: var(--bg-secondary);
968
+ will-change: transform;
969
+ perspective: 1000px;
970
+ }
971
+
972
+ .history-item::before {
973
+ content: '';
974
+ position: absolute;
975
+ top: 0;
976
+ left: 0;
977
+ width: 100%;
978
+ height: 100%;
979
+ background: linear-gradient(45deg, var(--primary-color), transparent);
980
+ opacity: 0;
981
+ transition: opacity 0.4s ease;
982
+ z-index: 1;
983
+ mix-blend-mode: overlay;
984
+ pointer-events: none;
985
+ }
986
+
987
+ .history-item::after {
988
+ content: '';
989
+ position: absolute;
990
+ top: -50%;
991
+ left: -50%;
992
+ width: 200%;
993
+ height: 200%;
994
+ background: radial-gradient(circle, rgba(255,255,255,0.8) 0%, transparent 30%);
995
+ opacity: 0;
996
+ mix-blend-mode: overlay;
997
+ transform: scale(0.5);
998
+ z-index: 2;
999
+ transition: transform 0.6s ease, opacity 0.6s ease;
1000
+ pointer-events: none;
1001
+ }
1002
+
1003
+ .history-item:hover {
1004
+ transform: translateY(-8px) scale(1.02) rotateX(3deg) rotateY(-3deg);
1005
+ box-shadow: 0 15px 35px var(--shadow-color),
1006
+ 0 0 0 2px var(--primary-color),
1007
+ 10px 10px 20px rgba(0,0,0,0.1);
1008
+ }
1009
+
1010
+ .history-item:hover::before {
1011
+ opacity: 0.5;
1012
+ }
1013
+
1014
+ .history-item:hover::after {
1015
+ opacity: 0.3;
1016
+ transform: scale(1) translate(25%, 25%);
1017
+ }
1018
+
1019
+ .history-img {
1020
+ width: 100%;
1021
+ height: 100%;
1022
+ object-fit: cover;
1023
+ display: block;
1024
+ transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
1025
+ filter: brightness(1);
1026
+ }
1027
+
1028
+ .history-item:hover .history-img {
1029
+ transform: scale(1.08);
1030
+ filter: brightness(1.1) contrast(1.1);
1031
+ }
1032
+
1033
+ .history-overlay {
1034
+ position: absolute;
1035
+ top: 0;
1036
+ left: 0;
1037
+ width: 100%;
1038
+ height: 100%;
1039
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.4) 40%, transparent 70%);
1040
+ opacity: 0;
1041
+ transition: opacity 0.4s ease;
1042
+ display: flex;
1043
+ align-items: flex-end;
1044
+ padding: 1rem;
1045
+ z-index: 3;
1046
+ }
1047
+
1048
+ .history-item:hover .history-overlay {
1049
+ opacity: 1;
1050
+ }
1051
+
1052
+ .history-prompt {
1053
+ color: white;
1054
+ font-size: 0.9rem;
1055
+ display: -webkit-box;
1056
+ -webkit-line-clamp: 2;
1057
+ -webkit-box-orient: vertical;
1058
+ overflow: hidden;
1059
+ text-overflow: ellipsis;
1060
+ width: 100%;
1061
+ transform: translateY(10px);
1062
+ opacity: 0;
1063
+ transition: transform 0.4s ease, opacity 0.4s ease;
1064
+ }
1065
+
1066
+ .history-item:hover .history-prompt {
1067
+ transform: translateY(0);
1068
+ opacity: 1;
1069
+ }
1070
+
1071
+ .history-actions {
1072
+ display: flex;
1073
+ justify-content: center;
1074
+ gap: 10px;
1075
+ margin-top: 15px;
1076
+ opacity: 0;
1077
+ transform: translateY(20px);
1078
+ transition: opacity 0.5s ease, transform 0.5s ease;
1079
+ }
1080
+
1081
+ .history-item:hover .history-actions {
1082
+ opacity: 1;
1083
+ transform: translateY(0);
1084
+ }
1085
+
1086
+ .history-action-btn {
1087
+ width: 36px;
1088
+ height: 36px;
1089
+ border-radius: 50%;
1090
+ background-color: rgba(255, 255, 255, 0.15);
1091
+ backdrop-filter: blur(5px);
1092
+ -webkit-backdrop-filter: blur(5px);
1093
+ border: 1px solid rgba(255, 255, 255, 0.2);
1094
+ color: white;
1095
+ display: flex;
1096
+ align-items: center;
1097
+ justify-content: center;
1098
+ cursor: pointer;
1099
+ font-size: 0.9rem;
1100
+ padding: 0;
1101
+ transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1);
1102
+ transform: scale(1);
1103
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
1104
+ }
1105
+
1106
+ .history-action-btn:hover {
1107
+ transform: scale(1.2);
1108
+ background-color: var(--primary-color);
1109
+ border-color: transparent;
1110
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.6);
1111
+ }
1112
+
1113
+ .history-action-btn.view-btn:hover {
1114
+ background-color: var(--info-color);
1115
+ }
1116
+
1117
+ .history-action-btn.regenerate-btn:hover {
1118
+ background-color: var(--success-color);
1119
+ }
1120
+
1121
+ .history-action-btn.download-btn:hover {
1122
+ background-color: var(--warning-color);
1123
+ }
1124
+
1125
+ .history-action-btn i {
1126
+ transition: transform 0.3s ease;
1127
+ }
1128
+
1129
+ .history-action-btn:hover i {
1130
+ transform: scale(1.1);
1131
+ }
1132
+
1133
+ .history-item::before,
1134
+ .history-item:hover::before,
1135
+ .history-item::after,
1136
+ .history-item:hover::after,
1137
+ .history-item:hover,
1138
+ .history-overlay,
1139
+ .history-item:hover .history-overlay,
1140
+ .history-img,
1141
+ .history-item:hover .history-img,
1142
+ .history-prompt,
1143
+ .history-item:hover .history-prompt,
1144
+ .history-actions,
1145
+ .history-item:hover .history-actions,
1146
+ .history-action-btn,
1147
+ .history-action-btn:hover {
1148
+ will-change: transform, opacity, filter;
1149
+ }
1150
+
1151
+ .empty-history-message {
1152
+ grid-column: 1 / -1;
1153
+ text-align: center;
1154
+ padding: 4rem 2rem;
1155
+ color: var(--text-secondary);
1156
+ background-color: var(--bg-secondary);
1157
+ border-radius: var(--card-radius);
1158
+ box-shadow: 0 4px 20px var(--shadow-color);
1159
+ }
1160
+
1161
+ .empty-history-message i {
1162
+ font-size: 4rem;
1163
+ margin-bottom: 1.5rem;
1164
+ opacity: 0.4;
1165
+ color: var(--text-tertiary);
1166
+ }
1167
+
1168
+ /* Modals */
1169
+ .modal {
1170
+ position: fixed;
1171
+ top: 0;
1172
+ left: 0;
1173
+ width: 100%;
1174
+ height: 100%;
1175
+ background-color: var(--modal-overlay);
1176
+ display: flex;
1177
+ align-items: center;
1178
+ justify-content: center;
1179
+ z-index: 100;
1180
+ opacity: 0;
1181
+ visibility: hidden;
1182
+ transition: opacity var(--transition-medium), visibility var(--transition-medium);
1183
+ backdrop-filter: blur(5px);
1184
+ -webkit-backdrop-filter: blur(5px);
1185
+ }
1186
+
1187
+ .modal.active {
1188
+ opacity: 1;
1189
+ visibility: visible;
1190
+ }
1191
+
1192
+ .modal-content {
1193
+ background-color: var(--bg-secondary);
1194
+ border-radius: var(--border-radius);
1195
+ width: 90%;
1196
+ max-width: 550px;
1197
+ max-height: 90vh;
1198
+ overflow-y: auto;
1199
+ box-shadow: 0 15px 50px var(--shadow-color);
1200
+ animation: slideUp 0.5s ease-out;
1201
+ display: flex;
1202
+ flex-direction: column;
1203
+ border: 1px solid var(--border-color);
1204
+ }
1205
+
1206
+ .modal-content.fullscreen {
1207
+ max-width: 90%;
1208
+ width: 1100px;
1209
+ height: 90vh;
1210
+ }
1211
+
1212
+ .modal-header {
1213
+ padding: 1.5rem;
1214
+ border-bottom: 1px solid var(--border-color);
1215
+ display: flex;
1216
+ justify-content: space-between;
1217
+ align-items: center;
1218
+ position: sticky;
1219
+ top: 0;
1220
+ background-color: var(--bg-secondary);
1221
+ z-index: 10;
1222
+ }
1223
+
1224
+ .modal-header h2 {
1225
+ margin-bottom: 0;
1226
+ font-size: 1.5rem;
1227
+ display: flex;
1228
+ align-items: center;
1229
+ gap: 0.5rem;
1230
+ }
1231
+
1232
+ .close-modal {
1233
+ background: transparent;
1234
+ border: none;
1235
+ color: var(--text-secondary);
1236
+ font-size: 1.2rem;
1237
+ cursor: pointer;
1238
+ transition: color var(--transition-medium);
1239
+ width: 40px;
1240
+ height: 40px;
1241
+ border-radius: 50%;
1242
+ display: flex;
1243
+ align-items: center;
1244
+ justify-content: center;
1245
+ }
1246
+
1247
+ .close-modal:hover {
1248
+ color: var(--danger-color);
1249
+ background-color: var(--bg-tertiary);
1250
+ }
1251
+
1252
+ .modal-body {
1253
+ padding: 1.5rem;
1254
+ flex: 1;
1255
+ overflow-y: auto;
1256
+ }
1257
+
1258
+ .modal-footer {
1259
+ padding: 1.5rem;
1260
+ border-top: 1px solid var(--border-color);
1261
+ display: flex;
1262
+ justify-content: flex-end;
1263
+ gap: 1rem;
1264
+ position: sticky;
1265
+ bottom: 0;
1266
+ background-color: var(--bg-secondary);
1267
+ z-index: 10;
1268
+ }
1269
+
1270
+ .modal-footer .primary-btn,
1271
+ .modal-footer .secondary-btn {
1272
+ min-width: 130px;
1273
+ }
1274
+
1275
+ /* Enhancement Animation */
1276
+ .enhancement-animation {
1277
+ margin: 1.5rem 0;
1278
+ position: relative;
1279
+ height: 2px;
1280
+ background-color: var(--bg-tertiary);
1281
+ border-radius: 2px;
1282
+ overflow: hidden;
1283
+ }
1284
+
1285
+ .enhancement-line {
1286
+ height: 100%;
1287
+ width: 30%;
1288
+ background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
1289
+ border-radius: 2px;
1290
+ position: absolute;
1291
+ top: 0;
1292
+ left: 0;
1293
+ animation: shimmer 1.5s infinite;
1294
+ }
1295
+
1296
+ /* Modal Columns Layout */
1297
+ .modal-columns {
1298
+ display: grid;
1299
+ grid-template-columns: 1.5fr 1fr;
1300
+ gap: 2rem;
1301
+ height: 100%;
1302
+ }
1303
+
1304
+ /* Image Viewer */
1305
+ .image-viewer-container {
1306
+ display: flex;
1307
+ flex-direction: column;
1308
+ height: 100%;
1309
+ border-radius: var(--card-radius);
1310
+ overflow: hidden;
1311
+ box-shadow: 0 10px 30px var(--shadow-color);
1312
+ background-color: var(--bg-tertiary);
1313
+ }
1314
+
1315
+ .image-viewer-container img {
1316
+ width: 100%;
1317
+ height: auto;
1318
+ object-fit: contain;
1319
+ flex: 1;
1320
+ display: block;
1321
+ background-color: var(--bg-tertiary);
1322
+ }
1323
+
1324
+ .image-actions {
1325
+ display: flex;
1326
+ gap: 1rem;
1327
+ padding: 1rem;
1328
+ background-color: var(--bg-tertiary);
1329
+ border-top: 1px solid var(--border-color);
1330
+ }
1331
+
1332
+ .image-action-btn {
1333
+ flex: 1;
1334
+ display: flex;
1335
+ align-items: center;
1336
+ justify-content: center;
1337
+ gap: 0.5rem;
1338
+ padding: 0.75rem;
1339
+ border-radius: var(--button-radius);
1340
+ background-color: var(--bg-secondary);
1341
+ color: var(--text-secondary);
1342
+ border: 1px solid var(--border-color);
1343
+ cursor: pointer;
1344
+ transition: all var(--transition-medium);
1345
+ font-weight: 500;
1346
+ font-size: 0.9rem;
1347
+ }
1348
+
1349
+ .image-action-btn:hover {
1350
+ background-color: var(--primary-color);
1351
+ color: white;
1352
+ border-color: var(--primary-color);
1353
+ }
1354
+
1355
+ /* Image Details */
1356
+ .image-details {
1357
+ display: flex;
1358
+ flex-direction: column;
1359
+ height: 100%;
1360
+ }
1361
+
1362
+ .detail-section {
1363
+ flex: 1;
1364
+ margin-bottom: 2rem;
1365
+ }
1366
+
1367
+ .detail-section-title {
1368
+ font-size: 1.2rem;
1369
+ margin-bottom: 1.5rem;
1370
+ color: var(--text-secondary);
1371
+ position: relative;
1372
+ }
1373
+
1374
+ .detail-section-title::after {
1375
+ content: '';
1376
+ position: absolute;
1377
+ bottom: -7px;
1378
+ left: 0;
1379
+ width: 40px;
1380
+ height: 3px;
1381
+ background: linear-gradient(to right, var(--gradient-start), var(--gradient-end));
1382
+ border-radius: 3px;
1383
+ }
1384
+
1385
+ .detail-item {
1386
+ margin-bottom: 1.5rem;
1387
+ }
1388
+
1389
+ .detail-label {
1390
+ display: block;
1391
+ font-weight: 600;
1392
+ margin-bottom: 0.5rem;
1393
+ color: var(--text-secondary);
1394
+ font-size: 0.95rem;
1395
+ display: flex;
1396
+ align-items: center;
1397
+ gap: 0.5rem;
1398
+ }
1399
+
1400
+ .detail-value {
1401
+ line-height: 1.5;
1402
+ display: block;
1403
+ color: var(--text-color);
1404
+ font-size: 0.95rem;
1405
+ background-color: var(--bg-tertiary);
1406
+ padding: 0.75rem;
1407
+ border-radius: var(--button-radius);
1408
+ border: 1px solid var(--border-color);
1409
+ }
1410
+
1411
+ .detail-stats {
1412
+ display: grid;
1413
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
1414
+ gap: 1rem;
1415
+ margin-top: 1.5rem;
1416
+ }
1417
+
1418
+ .stat-item {
1419
+ display: flex;
1420
+ flex-direction: column;
1421
+ gap: 0.5rem;
1422
+ }
1423
+
1424
+ .stat-label {
1425
+ font-size: 0.8rem;
1426
+ color: var(--text-tertiary);
1427
+ display: flex;
1428
+ align-items: center;
1429
+ gap: 0.3rem;
1430
+ }
1431
+
1432
+ .stat-value {
1433
+ font-size: 0.95rem;
1434
+ font-weight: 600;
1435
+ color: var(--text-color);
1436
+ background-color: var(--bg-tertiary);
1437
+ padding: 0.5rem;
1438
+ border-radius: var(--button-radius);
1439
+ border: 1px solid var(--border-color);
1440
+ text-align: center;
1441
+ }
1442
+
1443
+ .detail-actions {
1444
+ margin-top: auto;
1445
+ display: flex;
1446
+ flex-direction: column;
1447
+ gap: 1rem;
1448
+ }
1449
+
1450
+ /* Reference Image Handling */
1451
+ .reference-image-container {
1452
+ position: relative;
1453
+ }
1454
+
1455
+ input[type="file"] {
1456
+ position: absolute;
1457
+ top: 0;
1458
+ left: 0;
1459
+ width: 100%;
1460
+ height: 100%;
1461
+ opacity: 0;
1462
+ cursor: pointer;
1463
+ z-index: 1;
1464
+ }
1465
+
1466
+ .file-upload-label {
1467
+ display: flex;
1468
+ align-items: center;
1469
+ justify-content: center;
1470
+ gap: 0.5rem;
1471
+ padding: 1rem;
1472
+ border: 2px dashed var(--border-color);
1473
+ border-radius: var(--button-radius);
1474
+ color: var(--text-secondary);
1475
+ cursor: pointer;
1476
+ transition: all var(--transition-medium);
1477
+ }
1478
+
1479
+ .file-upload-label:hover {
1480
+ border-color: var(--primary-color);
1481
+ color: var(--primary-color);
1482
+ background-color: var(--primary-light);
1483
+ }
1484
+
1485
+ .reference-preview {
1486
+ margin-top: 1rem;
1487
+ position: relative;
1488
+ border-radius: var(--button-radius);
1489
+ overflow: hidden;
1490
+ box-shadow: 0 5px 15px var(--shadow-color);
1491
+ }
1492
+
1493
+ .reference-preview img {
1494
+ width: 100%;
1495
+ display: block;
1496
+ }
1497
+
1498
+ .remove-reference {
1499
+ position: absolute;
1500
+ top: 0.5rem;
1501
+ right: 0.5rem;
1502
+ width: 32px;
1503
+ height: 32px;
1504
+ border-radius: 50%;
1505
+ background-color: rgba(0, 0, 0, 0.5);
1506
+ color: white;
1507
+ border: none;
1508
+ cursor: pointer;
1509
+ display: flex;
1510
+ align-items: center;
1511
+ justify-content: center;
1512
+ transition: background-color var(--transition-medium);
1513
+ }
1514
+
1515
+ .remove-reference:hover {
1516
+ background-color: var(--danger-color);
1517
+ }
1518
+
1519
+ /* Share Modal */
1520
+ .share-preview {
1521
+ width: 100%;
1522
+ max-height: 300px;
1523
+ overflow: hidden;
1524
+ border-radius: var(--card-radius);
1525
+ margin-bottom: 1.5rem;
1526
+ box-shadow: 0 5px 20px var(--shadow-color);
1527
+ }
1528
+
1529
+ .share-preview img {
1530
+ width: 100%;
1531
+ height: auto;
1532
+ display: block;
1533
+ }
1534
+
1535
+ .share-options {
1536
+ display: grid;
1537
+ grid-template-columns: repeat(2, 1fr);
1538
+ gap: 1rem;
1539
+ margin-bottom: 1.5rem;
1540
+ }
1541
+
1542
+ .share-btn {
1543
+ padding: 0.75rem;
1544
+ border-radius: var(--button-radius);
1545
+ border: none;
1546
+ color: white;
1547
+ font-weight: 600;
1548
+ cursor: pointer;
1549
+ transition: all var(--transition-medium);
1550
+ display: flex;
1551
+ align-items: center;
1552
+ justify-content: center;
1553
+ gap: 0.5rem;
1554
+ }
1555
+
1556
+ .share-btn:hover {
1557
+ transform: translateY(-3px);
1558
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
1559
+ }
1560
+
1561
+ .share-btn.facebook {
1562
+ background-color: #1877f2;
1563
+ }
1564
+
1565
+ .share-btn.twitter {
1566
+ background-color: #1da1f2;
1567
+ }
1568
+
1569
+ .share-btn.pinterest {
1570
+ background-color: #e60023;
1571
+ }
1572
+
1573
+ .share-btn.whatsapp {
1574
+ background-color: #25d366;
1575
+ }
1576
+
1577
+ .share-link {
1578
+ display: flex;
1579
+ border-radius: var(--button-radius);
1580
+ overflow: hidden;
1581
+ border: 1px solid var(--border-color);
1582
+ }
1583
+
1584
+ .share-link input {
1585
+ flex: 1;
1586
+ padding: 0.75rem 1rem;
1587
+ border: none;
1588
+ background-color: var(--bg-tertiary);
1589
+ color: var(--text-color);
1590
+ font-size: 0.9rem;
1591
+ }
1592
+
1593
+ .copy-btn {
1594
+ padding: 0 1rem;
1595
+ background-color: var(--primary-color);
1596
+ color: white;
1597
+ border: none;
1598
+ cursor: pointer;
1599
+ transition: background-color var(--transition-medium);
1600
+ }
1601
+
1602
+ .copy-btn:hover {
1603
+ background-color: var(--primary-hover);
1604
+ }
1605
+
1606
+ /* Toast Notifications */
1607
+ #toast-container {
1608
+ position: fixed;
1609
+ bottom: 20px;
1610
+ right: 20px;
1611
+ z-index: 9999;
1612
+ display: flex;
1613
+ flex-direction: column;
1614
+ gap: 10px;
1615
+ max-width: 350px;
1616
+ }
1617
+
1618
+ .toast {
1619
+ background-color: var(--bg-secondary);
1620
+ color: var(--text-color);
1621
+ padding: 1rem 1.5rem;
1622
+ border-radius: var(--button-radius);
1623
+ box-shadow: 0 10px 30px var(--shadow-color);
1624
+ margin-bottom: 10px;
1625
+ animation: slideLeft 0.3s forwards;
1626
+ display: flex;
1627
+ position: relative;
1628
+ overflow: hidden;
1629
+ border-left: 4px solid var(--primary-color);
1630
+ }
1631
+
1632
+ .toast.info {
1633
+ border-left-color: var(--info-color);
1634
+ }
1635
+
1636
+ .toast.success {
1637
+ border-left-color: var(--success-color);
1638
+ }
1639
+
1640
+ .toast.warning {
1641
+ border-left-color: var(--warning-color);
1642
+ }
1643
+
1644
+ .toast.error {
1645
+ border-left-color: var(--danger-color);
1646
+ }
1647
+
1648
+ .toast-content {
1649
+ flex: 1;
1650
+ }
1651
+
1652
+ .toast-title {
1653
+ font-weight: 600;
1654
+ margin-bottom: 5px;
1655
+ font-size: 1rem;
1656
+ }
1657
+
1658
+ .toast-message {
1659
+ font-size: 0.9rem;
1660
+ color: var(--text-secondary);
1661
+ }
1662
+
1663
+ .toast-close {
1664
+ background: transparent;
1665
+ border: none;
1666
+ color: var(--text-secondary);
1667
+ cursor: pointer;
1668
+ font-size: 1.2rem;
1669
+ transition: color var(--transition-medium);
1670
+ align-self: flex-start;
1671
+ margin-left: 10px;
1672
+ }
1673
+
1674
+ .toast-close:hover {
1675
+ color: var(--danger-color);
1676
+ }
1677
+
1678
+ .toast-progress {
1679
+ position: absolute;
1680
+ bottom: 0;
1681
+ left: 0;
1682
+ height: 3px;
1683
+ background-color: var(--primary-color);
1684
+ width: 100%;
1685
+ animation: toast-progress 5s linear forwards;
1686
+ }
1687
+
1688
+ @keyframes toast-progress {
1689
+ 0% { width: 100%; }
1690
+ 100% { width: 0%; }
1691
+ }
1692
+
1693
+ .toast.exit {
1694
+ animation: slideRight 0.3s forwards;
1695
+ }
1696
+
1697
+ /* Responsive Styles */
1698
+ @media screen and (max-width: 1200px) {
1699
+ .modal-columns {
1700
+ grid-template-columns: 1fr;
1701
+ gap: 1.5rem;
1702
+ }
1703
+
1704
+ .image-viewer-container {
1705
+ height: auto;
1706
+ max-height: 500px;
1707
+ }
1708
+
1709
+ .detail-section {
1710
+ margin-bottom: 1rem;
1711
+ }
1712
+ }
1713
+
1714
+ @media screen and (max-width: 1024px) {
1715
+ .main-content {
1716
+ margin-left: 0;
1717
+ padding: 1.5rem;
1718
+ }
1719
+
1720
+ .sidebar {
1721
+ transform: translateX(-100%);
1722
+ }
1723
+
1724
+ .sidebar.active {
1725
+ transform: translateX(0);
1726
+ }
1727
+
1728
+ .sidebar-close {
1729
+ display: block;
1730
+ }
1731
+
1732
+ .image-display {
1733
+ min-height: 400px;
1734
+ }
1735
+
1736
+ .placeholder-wrapper {
1737
+ min-height: 400px;
1738
+ }
1739
+ }
1740
+
1741
+ @media screen and (max-width: 768px) {
1742
+ .app-header {
1743
+ padding: 0 1rem;
1744
+ }
1745
+
1746
+ .header-logo .gradient-text {
1747
+ font-size: 1.2rem;
1748
+ }
1749
+
1750
+ .content-section {
1751
+ margin-bottom: 2rem;
1752
+ }
1753
+
1754
+ .history-gallery {
1755
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
1756
+ gap: 1rem;
1757
+ }
1758
+
1759
+ .image-controls {
1760
+ flex-direction: column;
1761
+ }
1762
+
1763
+ .modal-content {
1764
+ width: 95%;
1765
+ }
1766
+
1767
+ .modal-footer {
1768
+ flex-direction: column;
1769
+ }
1770
+
1771
+ .modal-footer .primary-btn,
1772
+ .modal-footer .secondary-btn {
1773
+ width: 100%;
1774
+ }
1775
+
1776
+ .share-options {
1777
+ grid-template-columns: 1fr;
1778
+ }
1779
+ }
1780
+
1781
+ @media screen and (max-width: 480px) {
1782
+ .header-controls {
1783
+ gap: 0.5rem;
1784
+ }
1785
+
1786
+ .menu-toggle span {
1787
+ display: none;
1788
+ }
1789
+
1790
+ .logo-text .gradient-text {
1791
+ font-size: 1.8rem;
1792
+ }
1793
+
1794
+ .form-row {
1795
+ flex-direction: column;
1796
+ }
1797
+
1798
+ .form-actions {
1799
+ gap: 0.75rem;
1800
+ }
1801
+
1802
+ .history-gallery {
1803
+ grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
1804
+ gap: 0.75rem;
1805
+ }
1806
+
1807
+ .history-actions {
1808
+ gap: 6px;
1809
+ }
1810
+
1811
+ .history-action-btn {
1812
+ width: 30px;
1813
+ height: 30px;
1814
+ font-size: 0.8rem;
1815
+ }
1816
+
1817
+ .image-display {
1818
+ min-height: 300px;
1819
+ }
1820
+
1821
+ .placeholder-wrapper {
1822
+ min-height: 300px;
1823
+ }
1824
+
1825
+ .placeholder-icon {
1826
+ font-size: 3rem;
1827
+ }
1828
+
1829
+ .detail-stats {
1830
+ grid-template-columns: 1fr 1fr;
1831
+ }
1832
+ }
static/data/history.json ADDED
@@ -0,0 +1 @@
 
 
1
+ [{"imageUrl": "https://pornlabstaskstore.s3-accelerate.amazonaws.com/709156089bd6446897b225501d8931e2.jpg", "prompt": "A cute girl playing in ground Hd 4k image", "negativePrompt": "low quality , clothes", "seed": 137000, "width": 768, "height": 768, "timestamp": "2025-03-28T13:53:35.495Z", "id": "1743170015505"}, {"imageUrl": "https://pornlabstaskstore.s3-accelerate.amazonaws.com/acf526cf7dff45bf829355d22c3c715f.jpg", "prompt": "photo of a cheerful 7 year old boy with tousled brown hair, wearing a bright red t-shirt and denim shorts, joyfully playing in a vast green field filled with vibrant, colorful wildflowers of various types, under a clear, bright blue sky dotted with fluffy white clouds, sunlight casting a warm, golden glow on the scene, several butterflies fluttering around him, playful and delighted expression on his face as he runs amidst the flowers (field:1.1), (sunlight:1.1), (wildflowers:1.1)", "negativePrompt": "", "seed": 841852, "width": 768, "height": 768, "timestamp": "2025-03-28T13:22:58.806Z", "id": "1743168178808"}, {"imageUrl": "https://pornlabstaskstore.s3-accelerate.amazonaws.com/16f9da13d1f74d169f519e6d430ad79e.jpg", "prompt": "photo of a cute 7 year old boy with tousled brown hair wearing a bright red t-shirt and denim shorts, joyfully playing in a vast green field filled with colorful wildflowers, bright blue sky with fluffy white clouds, sunlight casting a warm glow, butterflies fluttering around, playful expression on his face", "negativePrompt": "", "seed": 841852, "width": 768, "height": 768, "timestamp": "2025-03-28T13:21:44.919Z", "id": "1743168104920"}]
static/js/main.js ADDED
@@ -0,0 +1,978 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // Initialize AOS (Animate On Scroll)
3
+ AOS.init({
4
+ duration: 800,
5
+ easing: 'ease-in-out',
6
+ once: false
7
+ });
8
+
9
+ // Elements
10
+ const loadingScreen = document.getElementById('loading-screen');
11
+ const appContainer = document.getElementById('app-container');
12
+ const themeToggle = document.getElementById('theme-toggle');
13
+ const menuToggle = document.getElementById('menu-toggle');
14
+ const sidebarClose = document.getElementById('sidebar-close');
15
+ const sidebar = document.getElementById('sidebar');
16
+ const generationForm = document.getElementById('generation-form');
17
+ const promptInput = document.getElementById('prompt');
18
+ const negativePromptInput = document.getElementById('negative-prompt');
19
+ const widthSelect = document.getElementById('width');
20
+ const heightSelect = document.getElementById('height');
21
+ const seedInput = document.getElementById('seed');
22
+ const randomSeedBtn = document.getElementById('random-seed');
23
+ const tilingCheckbox = document.getElementById('tiling');
24
+ const advancedSettingsBtn = document.getElementById('advanced-settings-btn');
25
+ const upgradePromptBtn = document.getElementById('upgrade-prompt-btn');
26
+ const imageDisplay = document.getElementById('image-display');
27
+ const historyGallery = document.getElementById('history-gallery');
28
+ const latestCreation = document.getElementById('latest-creation');
29
+
30
+ // Image Controls
31
+ const downloadCurrentBtn = document.getElementById('download-current');
32
+ const shareCurrentBtn = document.getElementById('share-current');
33
+ const favoriteCurrentBtn = document.getElementById('favorite-current');
34
+
35
+ // Modals
36
+ const advancedSettingsModal = document.getElementById('advanced-settings-modal');
37
+ const promptModal = document.getElementById('prompt-modal');
38
+ const imageViewerModal = document.getElementById('image-viewer-modal');
39
+ const shareModal = document.getElementById('share-modal');
40
+
41
+ // Advanced Settings Elements
42
+ const subseedInput = document.getElementById('subseed');
43
+ const attentionInput = document.getElementById('attention');
44
+ const attentionValue = document.getElementById('attention-value');
45
+ const referenceImageInput = document.getElementById('reference-image');
46
+ const referencePreview = document.getElementById('reference-preview');
47
+ const referencePreviewImg = document.getElementById('reference-preview-img');
48
+ const removeReferenceBtn = document.getElementById('remove-reference');
49
+ const referenceTypeSelect = document.getElementById('reference-type');
50
+ const referenceStrengthInput = document.getElementById('reference-strength');
51
+ const referenceStrengthValue = document.getElementById('reference-strength-value');
52
+ const resetAdvancedSettingsBtn = document.getElementById('reset-advanced-settings');
53
+ const saveAdvancedSettingsBtn = document.getElementById('save-advanced-settings');
54
+
55
+ // Prompt Modal Elements
56
+ const originalPromptInput = document.getElementById('original-prompt');
57
+ const enhancedPromptInput = document.getElementById('enhanced-prompt');
58
+ const cancelPromptEnhancementBtn = document.getElementById('cancel-prompt-enhancement');
59
+ const useEnhancedPromptBtn = document.getElementById('use-enhanced-prompt');
60
+
61
+ // Image Viewer Modal Elements
62
+ const viewerImage = document.getElementById('viewer-image');
63
+ const viewerPrompt = document.getElementById('viewer-prompt');
64
+ const viewerNegativePrompt = document.getElementById('viewer-negative-prompt');
65
+ const viewerSize = document.getElementById('viewer-size');
66
+ const viewerSeed = document.getElementById('viewer-seed');
67
+ const viewerTimestamp = document.getElementById('viewer-timestamp');
68
+ const downloadImageBtn = document.getElementById('download-image');
69
+ const shareImageBtn = document.getElementById('share-image');
70
+ const regenerateImageBtn = document.getElementById('regenerate-image');
71
+ const editPromptImageBtn = document.getElementById('edit-prompt-image');
72
+
73
+ // Share Modal
74
+ const sharePreviewImg = document.getElementById('share-preview-img');
75
+ const shareLinkInput = document.getElementById('share-link-input');
76
+ const copyLinkBtn = document.getElementById('copy-link');
77
+ const shareBtns = document.querySelectorAll('.share-btn');
78
+
79
+ // Global variables
80
+ let currentImage = null;
81
+ let referenceImageData = null;
82
+ let isGenerating = false;
83
+ const defaultAdvancedSettings = {
84
+ subseed: 0,
85
+ attention: 0.5,
86
+ referenceImage: null,
87
+ referenceType: 'style',
88
+ referenceStrength: 0.5
89
+ };
90
+ let advancedSettings = {...defaultAdvancedSettings};
91
+ let favoriteImages = [];
92
+
93
+ // Initialize
94
+ initializeApp();
95
+
96
+ function initializeApp() {
97
+ // Slower loading screen for a better experience
98
+ setTimeout(() => {
99
+ loadingScreen.style.opacity = '0';
100
+ setTimeout(() => {
101
+ loadingScreen.style.visibility = 'hidden';
102
+ appContainer.classList.remove('hidden');
103
+
104
+ // Trigger AOS animations on initial load
105
+ AOS.refresh();
106
+ }, 800);
107
+ }, 3000);
108
+
109
+ // Set random seed initially
110
+ seedInput.value = Math.floor(Math.random() * 999999);
111
+
112
+ // Load theme preference
113
+ loadThemePreference();
114
+
115
+ // Load image history
116
+ loadImageHistory();
117
+
118
+ // Load favorites
119
+ loadFavorites();
120
+
121
+ // Event listeners
122
+ setupEventListeners();
123
+ }
124
+
125
+ function setupEventListeners() {
126
+ // Theme toggle
127
+ themeToggle.addEventListener('click', toggleTheme);
128
+
129
+ // Mobile menu
130
+ menuToggle.addEventListener('click', () => sidebar.classList.add('active'));
131
+ sidebarClose.addEventListener('click', () => sidebar.classList.remove('active'));
132
+
133
+ // Generate random seed
134
+ randomSeedBtn.addEventListener('click', generateRandomSeed);
135
+
136
+ // Advanced settings modal
137
+ advancedSettingsBtn.addEventListener('click', openAdvancedSettingsModal);
138
+
139
+ // Handle sliders in advanced settings
140
+ attentionInput.addEventListener('input', () => {
141
+ attentionValue.textContent = attentionInput.value;
142
+ advancedSettings.attention = parseFloat(attentionInput.value);
143
+ });
144
+
145
+ referenceStrengthInput.addEventListener('input', () => {
146
+ referenceStrengthValue.textContent = referenceStrengthInput.value;
147
+ advancedSettings.referenceStrength = parseFloat(referenceStrengthInput.value);
148
+ });
149
+
150
+ // Reference image handling
151
+ referenceImageInput.addEventListener('change', handleReferenceImageUpload);
152
+ removeReferenceBtn.addEventListener('click', removeReferenceImage);
153
+
154
+ referenceTypeSelect.addEventListener('change', () => {
155
+ advancedSettings.referenceType = referenceTypeSelect.value;
156
+ });
157
+
158
+ // Advanced settings buttons
159
+ resetAdvancedSettingsBtn.addEventListener('click', resetAdvancedSettings);
160
+ saveAdvancedSettingsBtn.addEventListener('click', () => closeModal(advancedSettingsModal));
161
+
162
+ // Upgrade prompt button
163
+ upgradePromptBtn.addEventListener('click', handleUpgradePrompt);
164
+
165
+ // Prompt modal buttons
166
+ cancelPromptEnhancementBtn.addEventListener('click', () => closeModal(promptModal));
167
+ useEnhancedPromptBtn.addEventListener('click', applyEnhancedPrompt);
168
+
169
+ // Image controls
170
+ downloadCurrentBtn.addEventListener('click', downloadCurrentImage);
171
+ shareCurrentBtn.addEventListener('click', openShareModal);
172
+ favoriteCurrentBtn.addEventListener('click', toggleFavoriteCurrentImage);
173
+
174
+ // Image viewer modal buttons
175
+ downloadImageBtn.addEventListener('click', downloadCurrentImage);
176
+ shareImageBtn.addEventListener('click', openShareModal);
177
+ regenerateImageBtn.addEventListener('click', regenerateSimilarImage);
178
+ editPromptImageBtn.addEventListener('click', editAndCreateNew);
179
+
180
+ // Share modal buttons
181
+ copyLinkBtn.addEventListener('click', copyShareLink);
182
+ shareBtns.forEach(btn => {
183
+ btn.addEventListener('click', function() {
184
+ shareToSocialMedia(this.className.split(' ')[1]);
185
+ });
186
+ });
187
+
188
+ // Close modals
189
+ document.querySelectorAll('.close-modal').forEach(btn => {
190
+ btn.addEventListener('click', () => {
191
+ closeAllModals();
192
+ });
193
+ });
194
+
195
+ // Form submission
196
+ generationForm.addEventListener('submit', handleImageGeneration);
197
+
198
+ // Escape key for modals
199
+ document.addEventListener('keydown', (e) => {
200
+ if (e.key === 'Escape') {
201
+ closeAllModals();
202
+ }
203
+ });
204
+
205
+ // Disable context menu on images (for better UX)
206
+ document.addEventListener('contextmenu', function(e) {
207
+ if (e.target.tagName === 'IMG') {
208
+ e.preventDefault();
209
+ return false;
210
+ }
211
+ });
212
+ }
213
+
214
+ function closeAllModals() {
215
+ closeModal(advancedSettingsModal);
216
+ closeModal(promptModal);
217
+ closeModal(imageViewerModal);
218
+ closeModal(shareModal);
219
+ }
220
+
221
+ function loadThemePreference() {
222
+ const isDarkMode = localStorage.getItem('darkMode') !== 'false'; // Default to dark
223
+ setTheme(isDarkMode);
224
+ }
225
+
226
+ function toggleTheme() {
227
+ const isDarkMode = !document.body.classList.contains('light-theme');
228
+ setTheme(!isDarkMode);
229
+ localStorage.setItem('darkMode', !isDarkMode);
230
+ }
231
+
232
+ function setTheme(isDarkMode) {
233
+ if (isDarkMode) {
234
+ document.body.classList.remove('light-theme');
235
+ themeToggle.innerHTML = '<i class="fas fa-sun"></i>';
236
+ } else {
237
+ document.body.classList.add('light-theme');
238
+ themeToggle.innerHTML = '<i class="fas fa-moon"></i>';
239
+ }
240
+ }
241
+
242
+ function generateRandomSeed() {
243
+ const newSeed = Math.floor(Math.random() * 999999);
244
+ seedInput.value = newSeed;
245
+
246
+ // Add pulse animation to seed input
247
+ seedInput.classList.add('pulse-effect');
248
+ setTimeout(() => {
249
+ seedInput.classList.remove('pulse-effect');
250
+ }, 1000);
251
+
252
+ showToast('Seed Updated', `Random seed generated: ${newSeed}`, 'info');
253
+ }
254
+
255
+ function openAdvancedSettingsModal() {
256
+ // Update modal with current settings
257
+ subseedInput.value = advancedSettings.subseed;
258
+ attentionInput.value = advancedSettings.attention;
259
+ attentionValue.textContent = advancedSettings.attention;
260
+ referenceTypeSelect.value = advancedSettings.referenceType;
261
+ referenceStrengthInput.value = advancedSettings.referenceStrength;
262
+ referenceStrengthValue.textContent = advancedSettings.referenceStrength;
263
+
264
+ // Update reference image preview if exists
265
+ if (referenceImageData) {
266
+ referencePreviewImg.src = referenceImageData;
267
+ referencePreview.classList.remove('hidden');
268
+ } else {
269
+ referencePreview.classList.add('hidden');
270
+ }
271
+
272
+ openModal(advancedSettingsModal);
273
+ }
274
+
275
+ function resetAdvancedSettings() {
276
+ advancedSettings = {...defaultAdvancedSettings};
277
+ subseedInput.value = defaultAdvancedSettings.subseed;
278
+ attentionInput.value = defaultAdvancedSettings.attention;
279
+ attentionValue.textContent = defaultAdvancedSettings.attention;
280
+ referenceTypeSelect.value = defaultAdvancedSettings.referenceType;
281
+ referenceStrengthInput.value = defaultAdvancedSettings.referenceStrength;
282
+ referenceStrengthValue.textContent = defaultAdvancedSettings.referenceStrength;
283
+
284
+ removeReferenceImage();
285
+ showToast('Settings Reset', 'Advanced settings have been reset to defaults', 'info');
286
+ }
287
+
288
+ function handleReferenceImageUpload(e) {
289
+ const file = e.target.files[0];
290
+ if (file) {
291
+ const reader = new FileReader();
292
+ reader.onload = function(event) {
293
+ referenceImageData = event.target.result;
294
+ referencePreviewImg.src = referenceImageData;
295
+ referencePreview.classList.remove('hidden');
296
+ advancedSettings.referenceImage = referenceImageData;
297
+
298
+ showToast('Reference Added', 'Reference image uploaded successfully', 'success');
299
+ };
300
+ reader.readAsDataURL(file);
301
+ }
302
+ }
303
+
304
+ function removeReferenceImage() {
305
+ referenceImageData = null;
306
+ advancedSettings.referenceImage = null;
307
+ referenceImageInput.value = '';
308
+ referencePreview.classList.add('hidden');
309
+ }
310
+
311
+ async function handleUpgradePrompt() {
312
+ const prompt = promptInput.value.trim();
313
+
314
+ if (!prompt) {
315
+ showToast('Error', 'Please enter a prompt to enhance', 'error');
316
+ return;
317
+ }
318
+
319
+ showToast('Processing', 'Enhancing your prompt with AI...', 'info');
320
+
321
+ try {
322
+ const response = await fetch('/api/upgrade-prompt', {
323
+ method: 'POST',
324
+ headers: {
325
+ 'Content-Type': 'application/json'
326
+ },
327
+ body: JSON.stringify({ prompt })
328
+ });
329
+
330
+ const data = await response.json();
331
+
332
+ if (data.error) {
333
+ throw new Error(data.error);
334
+ }
335
+
336
+ // Populate prompt modal
337
+ originalPromptInput.value = prompt;
338
+ enhancedPromptInput.value = data.upgradedPrompt || prompt;
339
+
340
+ // Open modal
341
+ openModal(promptModal);
342
+
343
+ } catch (error) {
344
+ console.error('Error upgrading prompt:', error);
345
+ showToast('Error', error.message || 'Failed to enhance prompt. Please try again.', 'error');
346
+ }
347
+ }
348
+
349
+ function applyEnhancedPrompt() {
350
+ const enhancedPrompt = enhancedPromptInput.value.trim();
351
+
352
+ if (enhancedPrompt) {
353
+ promptInput.value = enhancedPrompt;
354
+ showToast('Applied', 'Enhanced prompt applied successfully', 'success');
355
+ }
356
+
357
+ closeModal(promptModal);
358
+ }
359
+
360
+ async function handleImageGeneration(e) {
361
+ e.preventDefault();
362
+
363
+ if (isGenerating) {
364
+ showToast('In Progress', 'An image is already being generated', 'warning');
365
+ return;
366
+ }
367
+
368
+ const prompt = promptInput.value.trim();
369
+
370
+ if (!prompt) {
371
+ showToast('Error', 'Please enter a prompt', 'error');
372
+ return;
373
+ }
374
+
375
+ // Collect parameters
376
+ const params = {
377
+ prompt: prompt,
378
+ seed: parseInt(seedInput.value) || Math.floor(Math.random() * 999999),
379
+ subseed: parseInt(subseedInput.value) || 0,
380
+ attention: parseFloat(attentionInput.value) || 0.5,
381
+ width: parseInt(widthSelect.value) || 768,
382
+ height: parseInt(heightSelect.value) || 768,
383
+ tiling: tilingCheckbox.checked,
384
+ negative_prompt: negativePromptInput.value.trim(),
385
+ reference_image: referenceImageData,
386
+ reference_image_type: referenceImageData ? referenceTypeSelect.value : null,
387
+ reference_strength: referenceImageData ? parseFloat(referenceStrengthInput.value) : 0.5
388
+ };
389
+
390
+ isGenerating = true;
391
+ showGeneratingUI();
392
+
393
+ try {
394
+ // Generate the image
395
+ const taskResponse = await fetch('/api/generate', {
396
+ method: 'POST',
397
+ headers: {
398
+ 'Content-Type': 'application/json'
399
+ },
400
+ body: JSON.stringify(params)
401
+ });
402
+
403
+ const taskData = await taskResponse.json();
404
+
405
+ if (taskData.error) {
406
+ throw new Error(taskData.error);
407
+ }
408
+
409
+ const taskId = taskData.taskId;
410
+
411
+ // Poll for completion
412
+ await pollImageProgress(taskId, params);
413
+
414
+ } catch (error) {
415
+ console.error('Error generating image:', error);
416
+ showToast('Error', error.message || 'Failed to generate image. Please try again.', 'error');
417
+ hideGeneratingUI();
418
+ isGenerating = false;
419
+ }
420
+ }
421
+
422
+ async function pollImageProgress(taskId, originalParams) {
423
+ // Start polling
424
+ const maxAttempts = 30; // 60 seconds (2s per poll)
425
+ let attempt = 0;
426
+
427
+ const poll = async () => {
428
+ try {
429
+ const response = await fetch('/api/poll', {
430
+ method: 'POST',
431
+ headers: {
432
+ 'Content-Type': 'application/json'
433
+ },
434
+ body: JSON.stringify({ taskId })
435
+ });
436
+
437
+ const data = await response.json();
438
+
439
+ if (data.error) {
440
+ throw new Error(data.error);
441
+ }
442
+
443
+ if (data.final_image_url) {
444
+ // Image is ready
445
+ const imageUrl = data.final_image_url;
446
+
447
+ // Create image object
448
+ currentImage = {
449
+ imageUrl: imageUrl,
450
+ prompt: originalParams.prompt,
451
+ negativePrompt: originalParams.negative_prompt,
452
+ seed: originalParams.seed,
453
+ width: originalParams.width,
454
+ height: originalParams.height,
455
+ timestamp: new Date().toISOString()
456
+ };
457
+
458
+ // Display the image
459
+ displayGeneratedImage(currentImage);
460
+
461
+ // Save to history
462
+ saveImageToHistory(currentImage);
463
+
464
+ return;
465
+ }
466
+
467
+ if (data.status === 'done' && !data.final_image_url) {
468
+ throw new Error('Image generation completed but no image was returned.');
469
+ }
470
+
471
+ // Continue polling
472
+ if (attempt < maxAttempts) {
473
+ attempt++;
474
+ setTimeout(poll, 2000);
475
+ } else {
476
+ throw new Error('Image generation timed out. Please try again.');
477
+ }
478
+ } catch (error) {
479
+ console.error('Error polling progress:', error);
480
+ showToast('Error', error.message || 'Failed to generate image. Please try again.', 'error');
481
+ hideGeneratingUI();
482
+ isGenerating = false;
483
+ }
484
+ };
485
+
486
+ await poll();
487
+ }
488
+
489
+ function showGeneratingUI() {
490
+ // Show loading state in image display
491
+ imageDisplay.innerHTML = `
492
+ <div class="loading-shimmer"></div>
493
+ <div class="loading-indicator">
494
+ <div class="generation-status">
495
+ <div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
496
+ <p>Creating your masterpiece...</p>
497
+ </div>
498
+ </div>
499
+ `;
500
+
501
+ // Update generate button
502
+ const generateBtn = document.getElementById('generate-btn');
503
+ generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Creating...';
504
+ generateBtn.disabled = true;
505
+
506
+ // Disable sidebar controls
507
+ advancedSettingsBtn.disabled = true;
508
+ upgradePromptBtn.disabled = true;
509
+ }
510
+
511
+ function hideGeneratingUI() {
512
+ // Enable form controls
513
+ const generateBtn = document.getElementById('generate-btn');
514
+ generateBtn.innerHTML = '<i class="fas fa-bolt"></i> Create Image';
515
+ generateBtn.disabled = false;
516
+
517
+ advancedSettingsBtn.disabled = false;
518
+ upgradePromptBtn.disabled = false;
519
+
520
+ // If no image was generated, restore placeholder
521
+ if (!currentImage) {
522
+ imageDisplay.innerHTML = `
523
+ <div class="placeholder-wrapper">
524
+ <div class="placeholder-content">
525
+ <i class="fas fa-image placeholder-icon pulse-slow"></i>
526
+ <p>Your masterpiece will appear here</p>
527
+ <span class="helper-tip">Use the form to create something amazing</span>
528
+ </div>
529
+ </div>
530
+ `;
531
+ }
532
+
533
+ isGenerating = false;
534
+ }
535
+
536
+ function displayGeneratedImage(imageData) {
537
+ // Update image display
538
+ imageDisplay.innerHTML = `
539
+ <img src="${imageData.imageUrl}" alt="Generated Image" class="display-image" />
540
+ `;
541
+
542
+ // Update latest creation
543
+ updateLatestCreation(imageData);
544
+
545
+ // Enable image controls
546
+ downloadCurrentBtn.disabled = false;
547
+ shareCurrentBtn.disabled = false;
548
+ favoriteCurrentBtn.disabled = false;
549
+
550
+ // Update favorite button state
551
+ updateFavoriteButtonState();
552
+
553
+ hideGeneratingUI();
554
+
555
+ // Show success message
556
+ showToast('Success', 'Your creation is ready!', 'success');
557
+ }
558
+
559
+ function updateLatestCreation(imageData) {
560
+ latestCreation.innerHTML = `
561
+ <img src="${imageData.imageUrl}" alt="Latest Creation" />
562
+ `;
563
+
564
+ latestCreation.addEventListener('click', () => {
565
+ openImageViewer(imageData);
566
+ });
567
+ }
568
+
569
+ function openImageViewer(imageData) {
570
+ // Set current image
571
+ currentImage = imageData;
572
+
573
+ // Populate viewer details
574
+ viewerImage.src = imageData.imageUrl;
575
+ viewerPrompt.textContent = imageData.prompt;
576
+ viewerNegativePrompt.textContent = imageData.negativePrompt || 'None';
577
+ viewerSize.textContent = `${imageData.width} × ${imageData.height}`;
578
+ viewerSeed.textContent = imageData.seed;
579
+
580
+ // Format date
581
+ const date = new Date(imageData.timestamp);
582
+ const formattedDate = date.toLocaleString();
583
+ viewerTimestamp.textContent = formattedDate;
584
+
585
+ // Open modal
586
+ openModal(imageViewerModal);
587
+ }
588
+
589
+ function regenerateSimilarImage(imageData) {
590
+ // Close modal
591
+ closeModal(imageViewerModal);
592
+
593
+ // Fill form with same parameters
594
+ promptInput.value = currentImage.prompt;
595
+ negativePromptInput.value = currentImage.negativePrompt || '';
596
+
597
+ // Select closest dimensions in dropdowns
598
+ selectClosestOption(widthSelect, currentImage.width);
599
+ selectClosestOption(heightSelect, currentImage.height);
600
+
601
+ // Use same seed
602
+ seedInput.value = currentImage.seed;
603
+
604
+ // Scroll to form
605
+ sidebar.scrollIntoView({ behavior: 'smooth' });
606
+
607
+ // Show toast
608
+ showToast('Ready to Regenerate', 'Parameters loaded from selected image', 'info');
609
+ }
610
+
611
+ function editAndCreateNew() {
612
+ // Close modal
613
+ closeModal(imageViewerModal);
614
+
615
+ // Fill form with same parameters but focus on prompt
616
+ promptInput.value = currentImage.prompt;
617
+ negativePromptInput.value = currentImage.negativePrompt || '';
618
+
619
+ // Select closest dimensions in dropdowns
620
+ selectClosestOption(widthSelect, currentImage.width);
621
+ selectClosestOption(heightSelect, currentImage.height);
622
+
623
+ // Generate new seed for variation
624
+ generateRandomSeed();
625
+
626
+ // Scroll to form and focus on prompt
627
+ sidebar.scrollIntoView({ behavior: 'smooth' });
628
+ promptInput.focus();
629
+
630
+ // Show toast
631
+ showToast('Ready to Edit', 'Edit the prompt to create a new variation', 'info');
632
+ }
633
+
634
+ function selectClosestOption(selectElement, value) {
635
+ let closestOption = null;
636
+ let closestDiff = Infinity;
637
+
638
+ // Find the closest option
639
+ for (let i = 0; i < selectElement.options.length; i++) {
640
+ const optionValue = parseInt(selectElement.options[i].value);
641
+ const diff = Math.abs(optionValue - value);
642
+
643
+ if (diff < closestDiff) {
644
+ closestDiff = diff;
645
+ closestOption = i;
646
+ }
647
+ }
648
+
649
+ if (closestOption !== null) {
650
+ selectElement.selectedIndex = closestOption;
651
+ }
652
+ }
653
+
654
+ function downloadCurrentImage() {
655
+ if (currentImage) {
656
+ downloadImage(currentImage.imageUrl);
657
+ }
658
+ }
659
+
660
+ function downloadImage(url) {
661
+ // Create an anchor element
662
+ const a = document.createElement('a');
663
+ a.href = url;
664
+
665
+ // Set the download attribute with a filename
666
+ const filename = 'adarshkumar-creation-' + Date.now() + '.png';
667
+ a.download = filename;
668
+
669
+ // Append to the body, click, and remove
670
+ document.body.appendChild(a);
671
+ a.click();
672
+ document.body.removeChild(a);
673
+
674
+ showToast('Downloaded', 'Image saved to your device', 'success');
675
+ }
676
+
677
+ function openShareModal() {
678
+ if (!currentImage) return;
679
+
680
+ // Set share preview image
681
+ sharePreviewImg.src = currentImage.imageUrl;
682
+
683
+ // Set share link
684
+ shareLinkInput.value = currentImage.imageUrl;
685
+
686
+ openModal(shareModal);
687
+ }
688
+
689
+ function copyShareLink() {
690
+ shareLinkInput.select();
691
+ document.execCommand('copy');
692
+
693
+ showToast('Copied', 'Link copied to clipboard', 'success');
694
+ }
695
+
696
+ function shareToSocialMedia(platform) {
697
+ if (!currentImage) return;
698
+
699
+ const url = encodeURIComponent(currentImage.imageUrl);
700
+ const text = encodeURIComponent(`Check out this AI image I created: "${currentImage.prompt.substring(0, 50)}..."`);
701
+ let shareUrl = '';
702
+
703
+ switch (platform) {
704
+ case 'facebook':
705
+ shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${url}`;
706
+ break;
707
+ case 'twitter':
708
+ shareUrl = `https://twitter.com/intent/tweet?text=${text}&url=${url}`;
709
+ break;
710
+ case 'pinterest':
711
+ shareUrl = `https://pinterest.com/pin/create/button/?url=${url}&media=${url}&description=${text}`;
712
+ break;
713
+ case 'whatsapp':
714
+ shareUrl = `https://api.whatsapp.com/send?text=${text} ${url}`;
715
+ break;
716
+ }
717
+
718
+ if (shareUrl) {
719
+ window.open(shareUrl, '_blank');
720
+ }
721
+ }
722
+
723
+ function toggleFavoriteCurrentImage() {
724
+ if (!currentImage) return;
725
+
726
+ const index = favoriteImages.findIndex(img => img.imageUrl === currentImage.imageUrl);
727
+
728
+ if (index === -1) {
729
+ // Add to favorites
730
+ favoriteImages.push(currentImage);
731
+ showToast('Added to Favorites', 'Image saved to your favorites', 'success');
732
+ } else {
733
+ // Remove from favorites
734
+ favoriteImages.splice(index, 1);
735
+ showToast('Removed from Favorites', 'Image removed from your favorites', 'info');
736
+ }
737
+
738
+ // Save favorites to localStorage
739
+ localStorage.setItem('favoriteImages', JSON.stringify(favoriteImages));
740
+
741
+ // Update button state
742
+ updateFavoriteButtonState();
743
+ }
744
+
745
+ function updateFavoriteButtonState() {
746
+ if (!currentImage) return;
747
+
748
+ const isFavorite = favoriteImages.some(img => img.imageUrl === currentImage.imageUrl);
749
+
750
+ if (isFavorite) {
751
+ favoriteCurrentBtn.innerHTML = '<i class="fas fa-heart"></i> Favorited';
752
+ favoriteCurrentBtn.classList.add('favorited');
753
+ } else {
754
+ favoriteCurrentBtn.innerHTML = '<i class="far fa-heart"></i> Favorite';
755
+ favoriteCurrentBtn.classList.remove('favorited');
756
+ }
757
+ }
758
+
759
+ function loadFavorites() {
760
+ const storedFavorites = localStorage.getItem('favoriteImages');
761
+
762
+ if (storedFavorites) {
763
+ try {
764
+ favoriteImages = JSON.parse(storedFavorites);
765
+ } catch (e) {
766
+ favoriteImages = [];
767
+ }
768
+ }
769
+ }
770
+
771
+ async function saveImageToHistory(imageData) {
772
+ try {
773
+ // Save locally first for immediate feedback
774
+ let history = getLocalHistory();
775
+
776
+ // Set an ID if none exists
777
+ if (!imageData.id) {
778
+ imageData.id = Date.now().toString();
779
+ }
780
+
781
+ // Add to the beginning of the array
782
+ history.unshift(imageData);
783
+
784
+ // Keep only the last 20 images
785
+ history = history.slice(0, 20);
786
+
787
+ // Save to localStorage
788
+ localStorage.setItem('imageHistory', JSON.stringify(history));
789
+
790
+ // Save to server
791
+ await fetch('/api/save-history', {
792
+ method: 'POST',
793
+ headers: {
794
+ 'Content-Type': 'application/json'
795
+ },
796
+ body: JSON.stringify(imageData)
797
+ });
798
+
799
+ // Render gallery
800
+ renderHistoryGallery(history);
801
+
802
+ } catch (error) {
803
+ console.error('Error saving to history:', error);
804
+ }
805
+ }
806
+
807
+ async function loadImageHistory() {
808
+ try {
809
+ // Try to get from server first
810
+ const response = await fetch('/api/get-history');
811
+ const data = await response.json();
812
+
813
+ if (data.images && Array.isArray(data.images) && data.images.length > 0) {
814
+ // Save to localStorage for offline access
815
+ localStorage.setItem('imageHistory', JSON.stringify(data.images));
816
+ renderHistoryGallery(data.images);
817
+ } else {
818
+ // Fall back to local storage
819
+ const history = getLocalHistory();
820
+ renderHistoryGallery(history);
821
+ }
822
+
823
+ } catch (error) {
824
+ console.error('Error loading history:', error);
825
+
826
+ // Fall back to local storage
827
+ const history = getLocalHistory();
828
+ renderHistoryGallery(history);
829
+ }
830
+ }
831
+
832
+ function getLocalHistory() {
833
+ const storageData = localStorage.getItem('imageHistory');
834
+
835
+ if (storageData) {
836
+ try {
837
+ return JSON.parse(storageData);
838
+ } catch (e) {
839
+ return [];
840
+ }
841
+ }
842
+
843
+ return [];
844
+ }
845
+
846
+ function renderHistoryGallery(images) {
847
+ if (!images || !Array.isArray(images) || images.length === 0) {
848
+ showEmptyHistoryMessage();
849
+ return;
850
+ }
851
+
852
+ let galleryHTML = '';
853
+
854
+ images.forEach(image => {
855
+ galleryHTML += `
856
+ <div class="history-item" data-image-id="${image.id || ''}">
857
+ <img src="${image.imageUrl}" alt="${image.prompt}" class="history-img">
858
+ <div class="history-overlay">
859
+ <div class="history-prompt">${image.prompt}</div>
860
+ <div class="history-actions">
861
+ <button class="history-action-btn view-btn" title="View Details">
862
+ <i class="fas fa-eye"></i>
863
+ </button>
864
+ <button class="history-action-btn regenerate-btn" title="Regenerate Similar">
865
+ <i class="fas fa-sync-alt"></i>
866
+ </button>
867
+ <button class="history-action-btn download-btn" title="Download Image">
868
+ <i class="fas fa-download"></i>
869
+ </button>
870
+ </div>
871
+ </div>
872
+ </div>
873
+ `;
874
+ });
875
+
876
+ historyGallery.innerHTML = galleryHTML;
877
+
878
+ // Add click events to history items
879
+ document.querySelectorAll('.history-item').forEach(item => {
880
+ const imageId = item.getAttribute('data-image-id');
881
+ const image = images.find(img => img.id === imageId);
882
+
883
+ if (!image) return;
884
+
885
+ // View button click
886
+ const viewBtn = item.querySelector('.view-btn');
887
+ viewBtn.addEventListener('click', (e) => {
888
+ e.stopPropagation(); // Prevent triggering the parent click
889
+ openImageViewer(image);
890
+ });
891
+
892
+ // Regenerate button click
893
+ const regenerateBtn = item.querySelector('.regenerate-btn');
894
+ regenerateBtn.addEventListener('click', (e) => {
895
+ e.stopPropagation(); // Prevent triggering the parent click
896
+ regenerateSimilarImage(image);
897
+ });
898
+
899
+ // Download button click
900
+ const downloadBtn = item.querySelector('.download-btn');
901
+ downloadBtn.addEventListener('click', (e) => {
902
+ e.stopPropagation(); // Prevent triggering the parent click
903
+ downloadImage(image.imageUrl);
904
+ });
905
+
906
+ // Main image click (for viewing)
907
+ item.addEventListener('click', () => {
908
+ openImageViewer(image);
909
+ });
910
+ });
911
+ }
912
+
913
+ function showEmptyHistoryMessage() {
914
+ historyGallery.innerHTML = `
915
+ <div class="empty-history-message">
916
+ <i class="fas fa-images"></i>
917
+ <p>Your creations will be displayed here</p>
918
+ <span class="helper-tip">Get started by generating your first image!</span>
919
+ </div>
920
+ `;
921
+ }
922
+
923
+ function openModal(modal) {
924
+ modal.classList.add('active');
925
+
926
+ // Add animation to modal content for better UX
927
+ const modalContent = modal.querySelector('.modal-content');
928
+ if (modalContent) {
929
+ modalContent.style.animation = 'none';
930
+ setTimeout(() => {
931
+ modalContent.style.animation = '';
932
+ }, 10);
933
+ }
934
+ }
935
+
936
+ function closeModal(modal) {
937
+ modal.classList.remove('active');
938
+ }
939
+
940
+ function showToast(title, message, type = 'info') {
941
+ const toastContainer = document.getElementById('toast-container');
942
+
943
+ // Create toast element
944
+ const toast = document.createElement('div');
945
+ toast.className = `toast ${type}`;
946
+
947
+ // Add toast content
948
+ toast.innerHTML = `
949
+ <div class="toast-content">
950
+ <div class="toast-title">${title}</div>
951
+ <div class="toast-message">${message}</div>
952
+ </div>
953
+ <button class="toast-close"><i class="fas fa-times"></i></button>
954
+ <div class="toast-progress"></div>
955
+ `;
956
+
957
+ // Add to container
958
+ toastContainer.appendChild(toast);
959
+
960
+ // Add close functionality
961
+ toast.querySelector('.toast-close').addEventListener('click', () => {
962
+ toast.classList.add('exit');
963
+ setTimeout(() => {
964
+ toast.remove();
965
+ }, 300);
966
+ });
967
+
968
+ // Auto remove after 5 seconds
969
+ setTimeout(() => {
970
+ if (toast.parentNode) {
971
+ toast.classList.add('exit');
972
+ setTimeout(() => {
973
+ toast.remove();
974
+ }, 300);
975
+ }
976
+ }, 5000);
977
+ }
978
+ });
templates/index.html ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>ADARSH KUMAR - AI Image Generator</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
12
+ <link href="https://unpkg.com/[email protected]/dist/aos.css" rel="stylesheet">
13
+ </head>
14
+ <body>
15
+ <!-- Loading Screen -->
16
+ <div id="loading-screen">
17
+ <div class="loading-container">
18
+ <div class="loading-logo">
19
+ <div class="logo-animation">
20
+ <div class="logo-circle"></div>
21
+ <div class="logo-text">
22
+ <span class="gradient-text">ADARSH KUMAR</span>
23
+ <span class="subtext">IS MAKING IT</span>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ <div class="loading-animation">
28
+ <div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
29
+ </div>
30
+ <div class="loading-text">
31
+ <span class="animated-text">Initializing Creative Intelligence</span>
32
+ <div class="loading-dots">
33
+ <span class="dot">.</span>
34
+ <span class="dot">.</span>
35
+ <span class="dot">.</span>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Main Application -->
42
+ <div id="app-container" class="hidden">
43
+ <!-- Header Area -->
44
+ <header class="app-header">
45
+ <div class="header-logo">
46
+ <span class="gradient-text">ADARSH KUMAR</span>
47
+ <span class="header-version">AI Studio </span>
48
+ </div>
49
+
50
+ <div class="header-controls">
51
+ <button id="theme-toggle" class="theme-toggle" title="Toggle Dark/Light Mode">
52
+ <i class="fas fa-moon"></i>
53
+ </button>
54
+
55
+ <button id="menu-toggle" class="menu-toggle">
56
+ <i class="fas fa-bars"></i>
57
+ <span>Menu</span>
58
+ </button>
59
+ </div>
60
+ </header>
61
+
62
+ <!-- Main Container -->
63
+ <div class="main-container">
64
+ <!-- Sidebar -->
65
+ <div id="sidebar" class="sidebar">
66
+ <div class="sidebar-header">
67
+ <h1 class="app-title">Creator Studio</h1>
68
+ <button id="sidebar-close" class="sidebar-close">
69
+ <i class="fas fa-times"></i>
70
+ </button>
71
+ </div>
72
+
73
+ <div class="sidebar-content">
74
+ <div class="latest-creation-preview">
75
+ <h3 class="sidebar-section-title">Latest Creation</h3>
76
+ <div id="latest-creation" class="latest-creation">
77
+ <div class="latest-creation-placeholder">
78
+ <i class="fas fa-image"></i>
79
+ <p>Generate an image to see it here</p>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <form id="generation-form">
85
+ <div class="form-group">
86
+ <label for="prompt">Prompt</label>
87
+ <div class="prompt-container">
88
+ <textarea id="prompt" placeholder="Describe your vision in detail..." rows="4"></textarea>
89
+ <button type="button" id="upgrade-prompt-btn" class="upgrade-btn" title="Enhance with AI">
90
+ <i class="fas fa-wand-magic-sparkles"></i>
91
+ </button>
92
+ </div>
93
+ </div>
94
+
95
+ <div class="form-group">
96
+ <label for="negative-prompt">Negative Prompt</label>
97
+ <textarea id="negative-prompt" placeholder="Elements to avoid in your image..." rows="2"></textarea>
98
+ </div>
99
+
100
+ <div class="form-row">
101
+ <div class="form-group">
102
+ <label for="width">Width</label>
103
+ <select id="width">
104
+ <option value="512">512px</option>
105
+ <option value="640">640px</option>
106
+ <option value="768" selected>768px</option>
107
+ <option value="1024">1024px</option>
108
+ </select>
109
+ </div>
110
+ <div class="form-group">
111
+ <label for="height">Height</label>
112
+ <select id="height">
113
+ <option value="512">512px</option>
114
+ <option value="640">640px</option>
115
+ <option value="768" selected>768px</option>
116
+ <option value="1024">1024px</option>
117
+ </select>
118
+ </div>
119
+ </div>
120
+
121
+ <div class="form-group">
122
+ <label for="seed">Seed</label>
123
+ <div class="seed-container">
124
+ <input type="number" id="seed" min="0" max="999999" value="0">
125
+ <button type="button" id="random-seed" class="random-btn pulse-effect" title="Generate random seed">
126
+ <i class="fas fa-dice"></i>
127
+ </button>
128
+ </div>
129
+ </div>
130
+
131
+ <div class="form-group checkbox-group">
132
+ <input type="checkbox" id="tiling">
133
+ <label for="tiling">Seamless Tiling</label>
134
+ </div>
135
+
136
+ <div class="form-actions">
137
+ <button type="button" id="advanced-settings-btn" class="secondary-btn">
138
+ <i class="fas fa-sliders"></i> Advanced Settings
139
+ </button>
140
+
141
+ <button type="submit" id="generate-btn" class="primary-btn pulse-animation">
142
+ <i class="fas fa-bolt"></i> Create Image
143
+ </button>
144
+ </div>
145
+ </form>
146
+ </div>
147
+ </div>
148
+
149
+ <!-- Main Content -->
150
+ <div class="main-content">
151
+ <div class="content-section" data-aos="fade-up">
152
+ <h2 class="section-title">Creative Canvas</h2>
153
+ <div id="image-display" class="image-display">
154
+ <div class="placeholder-wrapper">
155
+ <div class="placeholder-content">
156
+ <i class="fas fa-image placeholder-icon pulse-slow"></i>
157
+ <p>Your masterpiece will appear here</p>
158
+ <span class="helper-tip">Use the form to create something amazing</span>
159
+ </div>
160
+ </div>
161
+ </div>
162
+
163
+ <div class="image-controls">
164
+ <button id="download-current" class="control-btn" disabled>
165
+ <i class="fas fa-download"></i> Download
166
+ </button>
167
+ <button id="share-current" class="control-btn" disabled>
168
+ <i class="fas fa-share-alt"></i> Share
169
+ </button>
170
+ <button id="favorite-current" class="control-btn" disabled>
171
+ <i class="far fa-heart"></i> Favorite
172
+ </button>
173
+ </div>
174
+ </div>
175
+
176
+ <div class="content-section" data-aos="fade-up" data-aos-delay="100">
177
+ <h2 class="section-title">Creation Gallery</h2>
178
+ <div id="history-gallery" class="history-gallery">
179
+ <!-- Will be populated by JavaScript -->
180
+ <div class="empty-history-message">
181
+ <i class="fas fa-images"></i>
182
+ <p>Your creations will be displayed here</p>
183
+ <span class="helper-tip">Get started by generating your first image!</span>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ </div>
189
+ </div>
190
+
191
+ <!-- Advanced Settings Modal -->
192
+ <div id="advanced-settings-modal" class="modal">
193
+ <div class="modal-content">
194
+ <div class="modal-header">
195
+ <h2><i class="fas fa-cog fa-spin-pulse"></i> Advanced Settings</h2>
196
+ <button class="close-modal"><i class="fas fa-times"></i></button>
197
+ </div>
198
+ <div class="modal-body">
199
+ <div class="form-group">
200
+ <label for="subseed">Subseed</label>
201
+ <input type="number" id="subseed" min="0" max="999999" value="0">
202
+ <small class="setting-hint">Variation seed for more control</small>
203
+ </div>
204
+
205
+ <div class="form-group">
206
+ <label for="attention">Attention (Guidance Scale)</label>
207
+ <div class="slider-container">
208
+ <input type="range" id="attention" min="0" max="1" step="0.01" value="0.5">
209
+ <span id="attention-value">0.5</span>
210
+ </div>
211
+ <small class="setting-hint">Higher values = more precise prompt following</small>
212
+ </div>
213
+
214
+ <div class="form-group">
215
+ <label>Reference Image</label>
216
+ <div class="reference-image-container">
217
+ <input type="file" id="reference-image" accept="image/*">
218
+ <label for="reference-image" class="file-upload-label">
219
+ <i class="fas fa-cloud-upload-alt"></i> Upload Reference
220
+ </label>
221
+ </div>
222
+ <div id="reference-preview" class="reference-preview hidden">
223
+ <img id="reference-preview-img" src="#" alt="Reference Image">
224
+ <button id="remove-reference" class="remove-reference">
225
+ <i class="fas fa-times"></i>
226
+ </button>
227
+ </div>
228
+ <small class="setting-hint">Use an existing image as reference</small>
229
+ </div>
230
+
231
+ <div class="form-group">
232
+ <label for="reference-type">Reference Type</label>
233
+ <select id="reference-type">
234
+ <option value="style">Style</option>
235
+ <option value="content">Content</option>
236
+ <option value="composition">Composition</option>
237
+ </select>
238
+ <small class="setting-hint">How to use the reference image</small>
239
+ </div>
240
+
241
+ <div class="form-group">
242
+ <label for="reference-strength">Reference Strength</label>
243
+ <div class="slider-container">
244
+ <input type="range" id="reference-strength" min="0" max="1" step="0.01" value="0.5">
245
+ <span id="reference-strength-value">0.5</span>
246
+ </div>
247
+ <small class="setting-hint">How much influence the reference has</small>
248
+ </div>
249
+ </div>
250
+ <div class="modal-footer">
251
+ <button id="reset-advanced-settings" class="secondary-btn">
252
+ <i class="fas fa-undo"></i> Reset
253
+ </button>
254
+ <button id="save-advanced-settings" class="primary-btn">
255
+ <i class="fas fa-check"></i> Apply Settings
256
+ </button>
257
+ </div>
258
+ </div>
259
+ </div>
260
+
261
+ <!-- Prompt Enhancement Modal -->
262
+ <div id="prompt-modal" class="modal">
263
+ <div class="modal-content">
264
+ <div class="modal-header">
265
+ <h2><i class="fas fa-magic"></i> AI Enhanced Prompt</h2>
266
+ <button class="close-modal"><i class="fas fa-times"></i></button>
267
+ </div>
268
+ <div class="modal-body">
269
+ <div class="form-group">
270
+ <label for="original-prompt">Your Original Prompt</label>
271
+ <textarea id="original-prompt" rows="3" readonly></textarea>
272
+ </div>
273
+
274
+ <div class="enhancement-animation">
275
+ <div class="enhancement-line"></div>
276
+ </div>
277
+
278
+ <div class="form-group">
279
+ <label for="enhanced-prompt">AI Enhanced Version</label>
280
+ <textarea id="enhanced-prompt" rows="5"></textarea>
281
+ <small class="setting-hint">Feel free to edit this enhanced prompt</small>
282
+ </div>
283
+ </div>
284
+ <div class="modal-footer">
285
+ <button id="cancel-prompt-enhancement" class="secondary-btn">
286
+ <i class="fas fa-times"></i> Keep Original
287
+ </button>
288
+ <button id="use-enhanced-prompt" class="primary-btn">
289
+ <i class="fas fa-check-double"></i> Use Enhanced
290
+ </button>
291
+ </div>
292
+ </div>
293
+ </div>
294
+
295
+ <!-- Image Viewer Modal -->
296
+ <div id="image-viewer-modal" class="modal">
297
+ <div class="modal-content fullscreen">
298
+ <div class="modal-header">
299
+ <h2><i class="fas fa-image"></i> Image Showcase</h2>
300
+ <button class="close-modal"><i class="fas fa-times"></i></button>
301
+ </div>
302
+ <div class="modal-body">
303
+ <div class="modal-columns">
304
+ <div class="image-viewer-container">
305
+ <img id="viewer-image" src="#" alt="Generated Image">
306
+ <div class="image-actions">
307
+ <button id="download-image" class="image-action-btn">
308
+ <i class="fas fa-download"></i> Download
309
+ </button>
310
+ <button id="share-image" class="image-action-btn">
311
+ <i class="fas fa-share-alt"></i> Share
312
+ </button>
313
+ </div>
314
+ </div>
315
+
316
+ <div class="image-details">
317
+ <div class="detail-section">
318
+ <h3 class="detail-section-title">Creation Details</h3>
319
+
320
+ <div class="detail-item">
321
+ <span class="detail-label"><i class="fas fa-keyboard"></i> Prompt:</span>
322
+ <span id="viewer-prompt" class="detail-value"></span>
323
+ </div>
324
+
325
+ <div class="detail-item">
326
+ <span class="detail-label"><i class="fas fa-ban"></i> Negative Prompt:</span>
327
+ <span id="viewer-negative-prompt" class="detail-value"></span>
328
+ </div>
329
+
330
+ <div class="detail-stats">
331
+ <div class="stat-item">
332
+ <span class="stat-label"><i class="fas fa-expand"></i> Size:</span>
333
+ <span id="viewer-size" class="stat-value"></span>
334
+ </div>
335
+
336
+ <div class="stat-item">
337
+ <span class="stat-label"><i class="fas fa-seedling"></i> Seed:</span>
338
+ <span id="viewer-seed" class="stat-value"></span>
339
+ </div>
340
+
341
+ <div class="stat-item">
342
+ <span class="stat-label"><i class="far fa-clock"></i> Created:</span>
343
+ <span id="viewer-timestamp" class="stat-value"></span>
344
+ </div>
345
+ </div>
346
+ </div>
347
+
348
+ <div class="detail-actions">
349
+ <button id="regenerate-image" class="primary-btn full-width">
350
+ <i class="fas fa-sync-alt fa-spin-hover"></i> Create Similar Image
351
+ </button>
352
+ <button id="edit-prompt-image" class="secondary-btn full-width">
353
+ <i class="fas fa-edit"></i> Edit & Create New
354
+ </button>
355
+ </div>
356
+ </div>
357
+ </div>
358
+ </div>
359
+ </div>
360
+ </div>
361
+
362
+ <!-- Share Modal -->
363
+ <div id="share-modal" class="modal">
364
+ <div class="modal-content">
365
+ <div class="modal-header">
366
+ <h2><i class="fas fa-share-alt"></i> Share Your Creation</h2>
367
+ <button class="close-modal"><i class="fas fa-times"></i></button>
368
+ </div>
369
+ <div class="modal-body">
370
+ <div class="share-preview">
371
+ <img id="share-preview-img" src="#" alt="Share Preview">
372
+ </div>
373
+
374
+ <div class="share-options">
375
+ <button class="share-btn facebook">
376
+ <i class="fab fa-facebook"></i> Facebook
377
+ </button>
378
+ <button class="share-btn twitter">
379
+ <i class="fab fa-twitter"></i> Twitter
380
+ </button>
381
+ <button class="share-btn pinterest">
382
+ <i class="fab fa-pinterest"></i> Pinterest
383
+ </button>
384
+ <button class="share-btn whatsapp">
385
+ <i class="fab fa-whatsapp"></i> WhatsApp
386
+ </button>
387
+ </div>
388
+
389
+ <div class="share-link">
390
+ <input type="text" id="share-link-input" readonly>
391
+ <button id="copy-link" class="copy-btn">
392
+ <i class="fas fa-copy"></i>
393
+ </button>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ </div>
398
+
399
+ <!-- Toast Notifications Container -->
400
+ <div id="toast-container"></div>
401
+
402
+ <!-- Scripts -->
403
+ <script src="https://unpkg.com/[email protected]/dist/aos.js"></script>
404
+ <script src="{{ url_for('static', filename='js/main.js') }}"></script>
405
+ </body>
406
+ </html>