Elias207 commited on
Commit
279dfb8
·
verified ·
1 Parent(s): 3333238

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +205 -434
index.html CHANGED
@@ -7,151 +7,67 @@
7
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;600;700;800&display=swap" rel="stylesheet">
8
  <style>
9
  :root {
10
- --bg-start: #f5f7ff; --bg-end: #eef2ff; --text-color: #1e293b;
11
- --card-bg: #ffffff; --border-color: #e2e8f0;
12
- --primary-color-start: #4f46e5; --primary-color-end: #a855f7;
 
 
 
 
13
  --shadow-color-light: rgba(79, 70, 229, 0.08);
14
  --shadow-color-dark: rgba(30, 41, 59, 0.05);
15
- --drop-zone-bg: #f8fafc; --danger-color: #ef4444;
 
16
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
17
  }
18
- .dark {
19
- --bg-start: #0f172a; --bg-end: #0b1120; --text-color: #e2e8f0;
20
- --card-bg: #1e293b; --border-color: #334155;
21
- --shadow-color-light: rgba(0, 0, 0, 0.2); --shadow-color-dark: rgba(0, 0, 0, 0.25);
22
- --drop-zone-bg: #111827;
23
- }
24
  @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
25
- @keyframes popIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
26
-
27
- body {
28
- font-family: 'Vazirmatn', sans-serif; background-color: var(--bg-start);
29
- background-image: radial-gradient(circle at top left, var(--bg-start), var(--bg-end));
30
- color: var(--text-color); margin: 0; padding: 3rem 1rem;
31
- display: flex; justify-content: center; align-items: flex-start;
32
- min-height: 100vh; transition: background-color 0.4s, color 0.4s;
33
- }
34
  .container { max-width: 750px; width: 100%; }
35
  header { text-align: center; margin-bottom: 3rem; animation: fadeInUp 0.8s var(--ease-out-expo) forwards; }
36
- h1 {
37
- font-size: 3rem; font-weight: 800; letter-spacing: -1px; margin: 0;
38
- background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end));
39
- -webkit-background-clip: text; -webkit-text-fill-color: transparent;
40
- }
41
  p.subtitle { font-size: 1.2rem; color: #64748b; margin-top: 0.75rem; }
42
- .dark p.subtitle { color: #94a3b8; }
43
-
44
- main, .gallery-section {
45
- background-color: var(--card-bg); border-radius: 2rem;
46
- border: 1px solid var(--border-color);
47
- box-shadow: 0 4px 10px var(--shadow-color-dark), 0 20px 40px var(--shadow-color-light);
48
- padding: 3rem; transition: all 0.4s; animation: fadeInUp 0.8s var(--ease-out-expo) forwards; opacity: 0;
49
- }
50
  main { animation-delay: 0.2s; }
51
  .gallery-section { margin-top: 3rem; animation-delay: 0.4s; }
52
-
53
- .panel, .controls { margin-bottom: 3rem; }
54
  .panel:last-child { margin-bottom: 0; }
55
- .controls { margin-bottom: 2.5rem; }
56
-
57
- h2 {
58
- font-size: 1.4rem; font-weight: 700; margin: 0 0 1.5rem 0; padding-bottom: 1rem;
59
- display: flex; align-items: center; gap: 0.75rem; border-bottom: 1px solid var(--border-color);
60
- }
61
  h2 svg { color: var(--primary-color-start); }
62
 
63
- /* --- NEW: Improved Uploader Styles --- */
64
- #drop-zone {
65
- border: 2px dashed var(--border-color); border-radius: 1.5rem; text-align: center;
66
- transition: all 0.3s var(--ease-out-expo); position: relative; overflow: hidden;
67
- background-color: var(--drop-zone-bg); min-height: 280px;
68
- }
69
- #drop-zone-content {
70
- display: flex; flex-direction: column; justify-content: center;
71
- align-items: center; cursor: pointer; position: absolute; inset: 0;
72
- transition: opacity 0.4s ease, transform 0.4s ease;
73
- }
74
- #drop-zone.has-file #drop-zone-content { opacity: 0; pointer-events: none; transform: scale(0.95); }
75
  #drop-zone-icon { width: 50px; height: 50px; margin-bottom: 1rem; color: #94a3b8; }
76
  #drop-zone-text { color: #475569; font-weight: 500; }
77
- .dark #drop-zone-text { color: #94a3b8; }
78
-
79
- #image-preview-container {
80
- position: absolute; inset: 0; opacity: 0; pointer-events: none;
81
- transform: scale(0.95); transition: opacity 0.4s ease, transform 0.4s ease;
82
- }
83
- #drop-zone.has-file #image-preview-container { opacity: 1; pointer-events: auto; transform: scale(1); }
84
-
85
- #image-preview {
86
- width: 100%; height: 100%; padding: 1rem; box-sizing: border-box;
87
- border-radius: 1.5rem; object-fit: contain;
88
- }
89
- #change-image-btn {
90
- position: absolute; bottom: 1.5rem; left: 50%; transform: translateX(-50%);
91
- background-color: rgba(30, 41, 59, 0.7); backdrop-filter: blur(5px);
92
- border: 1px solid rgba(255, 255, 255, 0.1); color: white;
93
- padding: 0.75rem 1.5rem; border-radius: 2rem; cursor: pointer;
94
- font-family: 'Vazirmatn', sans-serif; font-weight: 600; display: flex;
95
- align-items: center; gap: 0.5rem; transition: all 0.2s ease;
96
- box-shadow: 0 4px 15px rgba(0,0,0,0.2);
97
- }
98
- #change-image-btn:hover { background-color: rgba(30, 41, 59, 0.9); transform: translateX(-50%) translateY(-2px); }
99
- #change-image-btn svg { width: 20px; height: 20px; }
100
 
101
- textarea { /* ... (styles unchanged) ... */ }
102
- button#submit-btn { /* ... (styles unchanged) ... */ }
103
- #result-container { /* ... (styles unchanged) ... */ }
104
- #loading-placeholder { /* ... (styles unchanged) ... */ }
105
- #result-grid { /* ... (styles unchanged) ... */ }
106
- #error-message { /* ... (styles unchanged) ... */ }
107
- textarea:focus {
108
- outline: none; border-color: var(--primary-color-start);
109
- box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary-color-start) 15%, transparent);
110
- }
111
- button#submit-btn {
112
- position: relative; overflow: hidden;
113
- width: 100%; padding: 1.1rem; border: none; border-radius: 1rem;
114
- background-image: linear-gradient(45deg, var(--primary-color-start) 0%, var(--primary-color-end) 100%);
115
- background-size: 200% auto; color: white;
116
- font-size: 1.1rem; font-weight: 700; cursor: pointer;
117
- margin-top: 1rem; transition: all 0.4s var(--ease-out-expo);
118
- box-shadow: 0 4px 15px var(--shadow-color-light);
119
- }
120
- button#submit-btn:hover:not(:disabled) {
121
- transform: translateY(-4px);
122
- box-shadow: 0 10px 25px -5px var(--shadow-color-light);
123
- background-position: right center;
124
- }
125
  button#submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
126
- .loader-dots {
127
- display: flex; justify-content: center; align-items: center;
128
- position: absolute; inset: 0;
129
- }
130
- .loader-dots div {
131
- width: 8px; height: 8px; margin: 0 4px; background-color: white; border-radius: 50%;
132
- animation: bounce 1.4s infinite ease-in-out both;
133
- }
134
- .loader-dots .dot1 { animation-delay: -0.32s; }
135
- .loader-dots .dot2 { animation-delay: -0.16s; }
136
- @keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1.0); } }
137
- #btn-text.hidden { visibility: hidden; }
138
-
139
- #result-container {
140
- border: 2px solid var(--border-color); border-radius: 1.5rem;
141
- min-height: 300px;
142
- position: relative;
143
- background-color: var(--drop-zone-bg); overflow: hidden;
144
- padding: 0.5rem; transition: border-color 0.3s ease;
145
- }
146
 
147
- #loading-placeholder {
148
- display: none; flex-direction: column; align-items: center; justify-content: center;
149
- gap: 1.5rem; opacity: 0; animation: fadeIn 0.5s ease forwards;
150
- position: absolute; inset: 0;
151
- }
152
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
153
  #result-container.loading #loading-placeholder { display: flex; }
154
-
155
  .creative-loader { width: 100px; height: 100px; position: relative; }
156
  .creative-loader svg { width: 100%; height: 100%; transform-origin: center; }
157
  .creative-loader .arc { fill: none; stroke-width: 4; stroke-linecap: round; transform-origin: 50% 50%; animation: spin-loader 2s linear infinite; }
@@ -161,136 +77,43 @@
161
  .creative-loader .star { fill: var(--primary-color-end); transform-origin: center; animation: pulse-star 1.5s ease-in-out infinite; }
162
  @keyframes spin-loader { to { transform: rotate(360deg); } }
163
  @keyframes pulse-star { 0%, 100% { transform: scale(0.9); opacity: 0.8; } 50% { transform: scale(1.1); opacity: 1; } }
164
-
165
  .loading-text { font-weight: 500; color: #64748b; }
166
- .dark .loading-text { color: #94a3b8; }
167
-
168
- #result-grid {
169
- display: grid; grid-template-columns: repeat(2, 1fr);
170
- gap: 0.75rem; width: 100%; height: 100%;
171
- }
172
- @keyframes popIn-spring { 0% { opacity: 0; transform: scale(0.8) translateY(20px); } 80% { opacity: 1; transform: scale(1.05) translateY(-5px); } 100% { opacity: 1; transform: scale(1) translateY(0); } }
173
- #result-grid img {
174
- width: 100%; height: 100%; object-fit: cover; border-radius: 1rem;
175
- cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease;
176
- opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards;
177
- }
178
- #result-grid img:hover { transform: scale(1.05) rotate(1deg); box-shadow: 0 10px 30px -5px var(--shadow-color-dark); }
179
 
180
- #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
181
-
182
- /* --- NEW: Lightbox with Gallery Controls --- */
183
- #lightbox {
184
- position: fixed; top: 0; left: 0; width: 100%; height: 100%;
185
- background-color: rgba(10, 20, 40, 0.85); z-index: 1000; display: none;
186
- justify-content: center; align-items: center; padding: 1rem;
187
- box-sizing: border-box; backdrop-filter: blur(8px);
188
- }
189
- #lightbox-img {
190
- max-width: 90vw; max-height: 80vh; object-fit: contain; border-radius: 1rem;
191
- box-shadow: 0 20px 50px rgba(0,0,0,0.5); animation: popIn 0.3s ease;
192
- }
193
- .lightbox-nav {
194
- position: absolute; top: 50%; transform: translateY(-50%);
195
- background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2);
196
- width: 44px; height: 44px; border-radius: 50%; cursor: pointer; display: none;
197
- justify-content: center; align-items: center; transition: all 0.2s; font-size: 2rem;
198
- }
199
- .lightbox-nav.visible { display: flex; }
200
- #lightbox-prev { left: 20px; }
201
- #lightbox-next { right: 20px; }
202
- .lightbox-nav:hover { background-color: rgba(255, 255, 255, 0.2); transform: translateY(-50%) scale(1.1); }
203
-
204
- #lightbox-controls { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; gap: 1rem; }
205
- .lightbox-btn {
206
- background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2);
207
- width: 50px; height: 50px; border-radius: 50%; cursor: pointer; display: flex;
208
- justify-content: center; align-items: center; transition: all 0.2s; text-decoration: none;
209
- }
210
- .lightbox-btn:hover { background-color: rgba(255, 255, 255, 0.2); transform: scale(1.1); }
211
- #lightbox-close { position: absolute; top: 20px; right: 20px; }
212
-
213
- /* --- History Gallery Styles --- */
214
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
215
- #clear-history-btn { /* ... (styles unchanged) ... */ }
216
- #history-grid { /* ... (styles unchanged) ... */ }
217
- .history-item { position: relative; /* ... (other styles unchanged) ... */ }
218
- .history-item-img-wrapper { position: relative; }
219
- .history-item-count {
220
- position: absolute; top: 1rem; left: 1rem;
221
- background: rgba(30, 41, 59, 0.7); backdrop-filter: blur(4px);
222
- color: white; font-size: 0.8rem; font-weight: 600;
223
- padding: 0.25rem 0.6rem; border-radius: 2rem;
224
- animation: popIn 0.5s 0.8s backwards;
225
- }
226
- #clear-history-btn {
227
- background: none; border: 1px solid var(--border-color); color: #64748b;
228
- padding: 0.5rem 1rem; border-radius: 0.75rem; cursor: pointer;
229
- display: flex; align-items: center; gap: 0.5rem;
230
- font-family: 'Vazirmatn', sans-serif; font-weight: 500; transition: all 0.2s;
231
- }
232
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
233
- #clear-history-btn svg { width: 18px; height: 18px; }
234
-
235
- #history-grid {
236
- display: grid;
237
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
238
- gap: 1.5rem;
239
- margin-top: 1.5rem;
240
- }
241
- #history-grid:empty::before {
242
- content: 'هنوز تصویری خلق نکرده‌اید. اولین ویرایش شما اینجا ذخیره خواهد شد.';
243
- color: #64748b;
244
- .dark & { color: #94a3b8; }
245
- grid-column: 1 / -1;
246
- text-align: center;
247
- padding: 3rem 1rem;
248
- background-color: var(--drop-zone-bg);
249
- border-radius: 1rem;
250
- }
251
- .history-item {
252
- background-color: var(--drop-zone-bg);
253
- border-radius: 1.25rem;
254
- border: 1px solid var(--border-color);
255
- overflow: hidden;
256
- box-shadow: 0 2px 8px var(--shadow-color-dark);
257
- transition: transform 0.3s ease, box-shadow 0.3s ease;
258
- opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards;
259
- }
260
  .history-item:hover { transform: translateY(-8px); box-shadow: 0 10px 20px var(--shadow-color-dark); }
261
- .history-item-img {
262
- width: 100%;
263
- height: 180px;
264
- object-fit: cover;
265
- display: block;
266
- }
267
- .history-item-content { padding: 1rem; }
268
- .history-item-prompt {
269
- font-size: 0.9rem; font-weight: 500;
270
- margin: 0 0 1rem 0; height: 4.5em; /* 3 lines with line-height */
271
- line-height: 1.5em; overflow: hidden;
272
- display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
273
- }
274
- .history-item-actions { display: flex; gap: 0.5rem; }
275
- .history-btn {
276
- flex-grow: 1; text-align: center;
277
- padding: 0.6rem; border-radius: 0.75rem;
278
- border: 1px solid var(--border-color); background-color: var(--card-bg);
279
- color: var(--text-color); font-weight: 600;
280
- display: flex; justify-content: center; align-items: center; gap: 0.5rem;
281
- cursor: pointer; transition: all 0.2s; text-decoration: none;
282
- }
283
- .history-btn svg { width: 18px; height: 18px; }
284
  .history-btn:hover { background-color: var(--primary-color-start); color: white; border-color: var(--primary-color-start); }
285
 
286
- @media (max-width: 600px) {
287
- body { padding: 1.5rem 1rem; }
288
- main, .gallery-section { padding: 1.5rem; }
289
- h1 { font-size: 2.5rem; }
290
- .lightbox-nav { width: 40px; height: 40px; }
291
- #lightbox-prev { left: 10px; }
292
- #lightbox-next { right: 10px; }
293
- }
 
 
 
 
 
 
294
  </style>
295
  </head>
296
  <body>
@@ -301,22 +124,21 @@
301
  </header>
302
 
303
  <main>
304
- <div class="panel">
305
  <h2>
306
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4Z"/></svg>
307
  ۱. تصویر خود را انتخاب کنید
308
  </h2>
309
  <div id="drop-zone">
310
- <div id="drop-zone-content" role="button" tabindex="0">
311
  <svg id="drop-zone-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
312
  <span id="drop-zone-text">تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</span>
313
  </div>
314
- <div id="image-preview-container">
315
- <img id="image-preview" src="#" alt="پیش‌نمایش تصویر" />
316
- <button id="change-image-btn">
317
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4Z"/></svg>
318
- <span>تغییر تصویر</span>
319
- </button>
320
  </div>
321
  </div>
322
  <input type="file" id="file-input" accept="image/*" hidden>
@@ -327,247 +149,196 @@
327
  <div class="panel"> ... </div>
328
  </main>
329
 
330
- <section class="gallery-section">
331
- <!-- Gallery content remains the same -->
332
- </section>
333
  </div>
334
 
335
- <!-- Lightbox Structure with Navigation -->
336
  <div id="lightbox">
337
- <button id="lightbox-prev" class="lightbox-nav" title="قبلی">&#10094;</button>
 
338
  <img id="lightbox-img" src="">
339
- <button id="lightbox-next" class="lightbox-nav" title="بعدی">&#10095;</button>
340
-
341
  <div id="lightbox-controls">
 
342
  <a id="lightbox-download" href="#" download="edited-image.png" title="دانلود تصویر" class="lightbox-btn">
343
- <svg fill="white" viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
344
  </a>
345
  </div>
346
- <button id="lightbox-close" class="lightbox-btn">&times;</button>
347
  </div>
348
 
349
- <!-- (Your full main and gallery section HTML from the previous step goes here to avoid repetition) -->
350
- <!-- Just make sure the new #drop-zone and #lightbox structures are used -->
351
-
352
  <script>
353
- // ... (All const declarations are the same, plus new ones)
354
- const changeImageBtn = document.getElementById('change-image-btn');
 
 
 
 
 
 
 
 
 
 
 
355
  const lightboxPrev = document.getElementById('lightbox-prev');
356
  const lightboxNext = document.getElementById('lightbox-next');
357
- let currentGalleryUrls = [];
358
- let currentGalleryIndex = 0;
 
 
 
 
 
 
 
 
 
359
 
360
- // --- Event Listeners Setup ---
361
- dropZone.addEventListener('click', (e) => {
362
- // Only trigger if the preview is not active
363
- if (!dropZone.classList.contains('has-file')) {
364
- fileInput.click();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  }
366
  });
367
- changeImageBtn.addEventListener('click', () => fileInput.click());
368
 
369
  // --- Core Functions ---
370
  function handleFile(file) {
371
- if (!file || !file.type.startsWith('image/')) {
372
- displayError('لطفا یک فایل تصویری انتخاب کنید.');
373
- return;
374
- }
375
  uploadedFile = file;
376
  const reader = new FileReader();
377
  reader.onload = (e) => {
378
  imagePreview.src = e.target.result;
379
- dropZone.classList.add('has-file'); // This triggers the new UI state
 
 
380
  checkFormState();
381
  clearResult();
382
  };
383
  reader.readAsDataURL(file);
384
  }
 
 
 
 
 
 
 
 
 
385
 
386
- function displayResult(imageUrls) {
 
387
  resultGrid.innerHTML = '';
388
  imageUrls.forEach((url, index) => {
389
  const img = document.createElement('img');
390
  img.src = url;
391
- img.alt = `تصویر ویرایش شده ${index + 1}`;
392
  img.style.animationDelay = `${index * 120}ms`;
393
- // IMPORTANT: Pass the array and index to the gallery opener
394
- img.addEventListener('click', () => openGalleryLightbox(imageUrls, index));
395
  resultGrid.appendChild(img);
396
  });
397
  }
398
-
399
- // --- Lightbox with Gallery Functions ---
400
- function openGalleryLightbox(urls, startIndex = 0) {
401
- currentGalleryUrls = urls;
402
- currentGalleryIndex = startIndex;
403
-
404
- // Show/hide nav buttons based on number of images
405
- if (urls.length > 1) {
406
- lightboxPrev.classList.add('visible');
407
- lightboxNext.classList.add('visible');
408
- } else {
409
- lightboxPrev.classList.remove('visible');
410
- lightboxNext.classList.remove('visible');
411
- }
412
-
413
- updateLightboxImage();
414
  lightbox.style.display = 'flex';
 
415
  }
416
-
 
 
 
 
417
  function updateLightboxImage() {
418
- if (currentGalleryUrls.length === 0) return;
419
- lightboxImg.src = currentGalleryUrls[currentGalleryIndex];
420
- lightboxDownload.href = currentGalleryUrls[currentGalleryIndex];
421
- // Add a unique name for each downloaded image in the gallery
422
- lightboxDownload.download = `ai-edit-${Date.now()}-${currentGalleryIndex + 1}.png`;
 
 
 
423
  }
424
 
425
  function showNextImage() {
426
- currentGalleryIndex = (currentGalleryIndex + 1) % currentGalleryUrls.length;
427
  updateLightboxImage();
428
  }
429
 
430
  function showPrevImage() {
431
- currentGalleryIndex = (currentGalleryIndex - 1 + currentGalleryUrls.length) % currentGalleryUrls.length;
432
  updateLightboxImage();
433
  }
434
-
435
- lightboxPrev.addEventListener('click', showPrevImage);
436
- lightboxNext.addEventListener('click', showNextImage);
437
 
438
- // This function replaces the old openLightbox
439
- function closeLightbox() {
440
- lightbox.style.display = 'none';
441
- currentGalleryUrls = []; // Clear gallery on close
442
- }
443
 
444
- // --- History Management Functions ---
445
  function renderHistory() {
446
  const history = getHistory();
447
- historyGrid.innerHTML = '';
448
 
449
- if (history.length > 0) {
450
- clearHistoryBtn.style.display = 'flex';
451
- history.forEach((item, index) => {
452
- const card = document.createElement('div');
453
- card.className = 'history-item';
454
- card.style.animationDelay = `${index * 80}ms`;
455
 
456
- // NEW: Added image count badge
457
- const imageCountBadge = item.urls.length > 1 ? `<div class="history-item-count">${item.urls.length}x</div>` : '';
 
 
458
 
459
- card.innerHTML = `
460
- <div class="history-item-img-wrapper">
461
- <img src="${item.urls[0]}" alt="پیش‌نمایش تاریخچه" class="history-item-img">
462
- ${imageCountBadge}
463
- </div>
464
- <div class="history-item-content">
465
- <p class="history-item-prompt">${item.prompt}</p>
466
- <div class="history-item-actions">
467
- <button class="history-btn view-all-btn">
468
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
469
- مشاهده همه
470
- </button>
471
- <a href="${item.urls[0]}" download="ai-edit-${Date.now()}.png" class="history-btn">
472
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17v-10"/><path d="m15 14-3 3-3-3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
473
- دانلود اولین
474
- </a>
475
- </div>
476
  </div>
477
- `;
478
- // Use the new gallery lightbox function
479
- card.querySelector('.view-all-btn').addEventListener('click', () => openGalleryLightbox(item.urls, 0));
480
- historyGrid.appendChild(card);
481
- });
482
- } else {
483
- clearHistoryBtn.style.display = 'none';
484
- }
485
  }
486
 
487
- // --- The rest of your JavaScript remains the same ---
488
- // (handleSubmit, setLoading, clearResult, displayError, getHistory, saveHistory, addToHistory, handleClearHistory, etc.)
489
- // Add the script content from the previous step here, but use the modified functions from this step.
490
- const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
491
- const dropZone = document.getElementById('drop-zone');
492
- const dropZoneContent = document.getElementById('drop-zone-content');
493
- const fileInput = document.getElementById('file-input');
494
- const imagePreview = document.getElementById('image-preview');
495
- const promptInput = document.getElementById('prompt-input');
496
- const submitBtn = document.getElementById('submit-btn');
497
- const btnText = document.getElementById('btn-text');
498
- const resultContainer = document.getElementById('result-container');
499
- const resultGrid = document.getElementById('result-grid');
500
- const errorMessage = document.getElementById('error-message');
501
- const lightbox = document.getElementById('lightbox');
502
- const lightboxImg = document.getElementById('lightbox-img');
503
- const lightboxClose = document.getElementById('lightbox-close');
504
- const lightboxDownload = document.getElementById('lightbox-download');
505
- const historyGrid = document.getElementById('history-grid');
506
- const clearHistoryBtn = document.getElementById('clear-history-btn');
507
- let uploadedFile = null;
508
-
509
- fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
510
- ['dragenter', 'dragover'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); }));
511
- ['dragleave', 'drop'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); }));
512
- dropZone.addEventListener('drop', (e) => handleFile(e.dataTransfer.files[0]));
513
- promptInput.addEventListener('input', checkFormState);
514
- submitBtn.addEventListener('click', handleSubmit);
515
- lightboxClose.addEventListener('click', closeLightbox);
516
- lightbox.addEventListener('click', (e) => { if (e.target.id === 'lightbox') closeLightbox(); });
517
- clearHistoryBtn.addEventListener('click', handleClearHistory);
518
- document.addEventListener('DOMContentLoaded', renderHistory);
519
-
520
  function checkFormState() { submitBtn.disabled = !uploadedFile || promptInput.value.trim() === ''; }
521
-
522
- async function handleSubmit() {
523
- if (submitBtn.disabled) return;
524
- setLoading(true);
525
- const formData = new FormData();
526
- formData.append('image', uploadedFile);
527
- formData.append('prompt', promptInput.value.trim());
528
- try {
529
- const response = await fetch(API_URL, { method: 'POST', body: formData });
530
- if (!response.ok) {
531
- const errorData = await response.json().catch(() => ({ error: `خطای سرور: ${response.statusText}` }));
532
- throw new Error(errorData.error || `یک خطای ناشناخته رخ داد.`);
533
- }
534
- const result = await response.json();
535
- if (result.image_urls && Array.isArray(result.image_urls) && result.image_urls.length > 0) {
536
- displayResult(result.image_urls);
537
- addToHistory({ prompt: promptInput.value.trim(), urls: result.image_urls });
538
- } else { throw new Error('پاسخ معتبری از سرور دریافت نشد.'); }
539
- } catch (error) { displayError(error.message); } finally { setLoading(false); }
540
- }
541
-
542
- function setLoading(isLoading) {
543
- if (isLoading) {
544
- resultContainer.classList.add('loading');
545
- btnText.classList.add('hidden');
546
- if (!submitBtn.querySelector('.loader-dots')) {
547
- const loader = document.createElement('div');
548
- loader.className = 'loader-dots';
549
- loader.innerHTML = '<div class="dot1"></div><div class="dot2"></div><div class="dot3"></div>';
550
- submitBtn.appendChild(loader);
551
- }
552
- submitBtn.disabled = true;
553
- resultGrid.innerHTML = '';
554
- errorMessage.style.display = 'none';
555
- } else {
556
- resultContainer.classList.remove('loading');
557
- btnText.classList.remove('hidden');
558
- const loader = submitBtn.querySelector('.loader-dots');
559
- if (loader) submitBtn.removeChild(loader);
560
- checkFormState();
561
- }
562
- }
563
-
564
  function clearResult() { resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; }
565
  function displayError(message) { errorMessage.textContent = message; errorMessage.style.display = 'block'; }
566
 
567
- function getHistory() { try { const history = localStorage.getItem('aiPhotoshopHistory'); return history ? JSON.parse(history) : []; } catch (e) { console.error("Failed to parse history", e); return []; } }
568
- function saveHistory(history) { localStorage.setItem('aiPhotoshopHistory', JSON.stringify(history)); renderHistory(); }
569
- function addToHistory(item) { const history = getHistory(); history.unshift(item); if (history.length > 20) { history.pop(); } saveHistory(history); }
570
- function handleClearHistory() { if (confirm('آیا از پاک کردن تمام تاریخچه مطمئن هستید؟')) { localStorage.removeItem('aiPhotoshopHistory'); renderHistory(); } }
571
  checkFormState();
572
  </script>
573
  </body>
 
7
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;600;700;800&display=swap" rel="stylesheet">
8
  <style>
9
  :root {
10
+ --bg-start: #f5f7ff;
11
+ --bg-end: #eef2ff;
12
+ --text-color: #1e293b;
13
+ --card-bg: #ffffff;
14
+ --border-color: #e2e8f0;
15
+ --primary-color-start: #4f46e5;
16
+ --primary-color-end: #a855f7;
17
  --shadow-color-light: rgba(79, 70, 229, 0.08);
18
  --shadow-color-dark: rgba(30, 41, 59, 0.05);
19
+ --drop-zone-bg: #f8fafc;
20
+ --danger-color: #ef4444;
21
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
22
  }
23
+
24
+ /* --- Base & Animation Styles --- */
 
 
 
 
25
  @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
26
+ body { font-family: 'Vazirmatn', sans-serif; background-color: var(--bg-start); background-image: radial-gradient(circle at top left, var(--bg-start), var(--bg-end)); color: var(--text-color); margin: 0; padding: 3rem 1rem; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; transition: background-color 0.4s, color 0.4s; }
 
 
 
 
 
 
 
 
27
  .container { max-width: 750px; width: 100%; }
28
  header { text-align: center; margin-bottom: 3rem; animation: fadeInUp 0.8s var(--ease-out-expo) forwards; }
29
+ h1 { font-size: 3rem; font-weight: 800; letter-spacing: -1px; margin: 0; background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
 
 
 
 
30
  p.subtitle { font-size: 1.2rem; color: #64748b; margin-top: 0.75rem; }
31
+ main, .gallery-section { background-color: var(--card-bg); border-radius: 2rem; border: 1px solid var(--border-color); box-shadow: 0 4px 10px var(--shadow-color-dark), 0 20px 40px var(--shadow-color-light); padding: 3rem; transition: all 0.4s; animation: fadeInUp 0.8s var(--ease-out-expo) forwards; opacity: 0; }
 
 
 
 
 
 
 
32
  main { animation-delay: 0.2s; }
33
  .gallery-section { margin-top: 3rem; animation-delay: 0.4s; }
34
+ .panel { margin-bottom: 3rem; }
 
35
  .panel:last-child { margin-bottom: 0; }
36
+ h2 { font-size: 1.4rem; font-weight: 700; margin: 0 0 1.5rem 0; padding-bottom: 1rem; display: flex; align-items: center; gap: 0.75rem; border-bottom: 1px solid var(--border-color); }
 
 
 
 
 
37
  h2 svg { color: var(--primary-color-start); }
38
 
39
+ /* --- IMPROVED: Drop Zone Styles --- */
40
+ #drop-zone { border: 2px dashed var(--border-color); border-radius: 1.5rem; padding: 1rem; text-align: center; cursor: pointer; transition: all 0.3s var(--ease-out-expo); position: relative; overflow: hidden; background-color: var(--drop-zone-bg); display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 280px; }
41
+ #drop-zone::before { content: ''; position: absolute; inset: -2px; border-radius: 1.5rem; background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end)) border-box; -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); -webkit-mask-composite: destination-out; mask-composite: subtract; opacity: 0; transition: opacity 0.3s ease; }
42
+ #drop-zone.dragover, #drop-zone:not(.has-image):hover { border-color: transparent; transform: translateY(-5px); box-shadow: 0 10px 20px -5px var(--shadow-color-light); }
43
+ #drop-zone.dragover::before, #drop-zone:not(.has-image):hover::before { opacity: 1; }
44
+ #drop-zone-content { transition: opacity 0.3s ease, transform 0.3s ease; }
45
+ #drop-zone:not(.has-image):hover #drop-zone-content { transform: translateY(-5px); }
 
 
 
 
 
46
  #drop-zone-icon { width: 50px; height: 50px; margin-bottom: 1rem; color: #94a3b8; }
47
  #drop-zone-text { color: #475569; font-weight: 500; }
48
+ #image-preview { position: absolute; inset: 0; width: 100%; height: 100%; padding: 1rem; box-sizing: border-box; border-radius: 1.5rem; object-fit: contain; opacity: 0; transform: scale(0.95); transition: opacity 0.4s ease, transform 0.4s ease; pointer-events: none; }
49
+ #image-preview.visible { opacity: 1; transform: scale(1); }
50
+ #change-image-overlay { position: absolute; inset: 0; background-color: rgba(30, 41, 59, 0.7); display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; opacity: 0; transition: opacity 0.3s ease; pointer-events: none; }
51
+ #drop-zone.has-image:hover #change-image-overlay { opacity: 1; pointer-events: auto; }
52
+ #change-image-overlay svg { width: 40px; height: 40px; margin-bottom: 0.75rem; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ /* --- Form & Result Styles --- */
55
+ textarea { width: 100%; padding: 1rem; border: 2px solid var(--border-color); border-radius: 1rem; background-color: var(--drop-zone-bg); color: var(--text-color); font-family: 'Vazirmatn', sans-serif; font-size: 1rem; resize: vertical; min-height: 80px; box-sizing: border-box; transition: all 0.3s ease; }
56
+ textarea:focus { outline: none; border-color: var(--primary-color-start); box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary-color-start) 15%, transparent); }
57
+ button#submit-btn { position: relative; overflow: hidden; width: 100%; padding: 1.1rem; border: none; border-radius: 1rem; background-image: linear-gradient(45deg, var(--primary-color-start) 0%, var(--primary-color-end) 100%); background-size: 200% auto; color: white; font-size: 1.1rem; font-weight: 700; cursor: pointer; margin-top: 1rem; transition: all 0.4s var(--ease-out-expo); box-shadow: 0 4px 15px var(--shadow-color-light); }
58
+ button#submit-btn:hover:not(:disabled) { transform: translateY(-4px); box-shadow: 0 10px 25px -5px var(--shadow-color-light); background-position: right center; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  button#submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
60
+ #result-container { border: 2px solid var(--border-color); border-radius: 1.5rem; min-height: 300px; position: relative; background-color: var(--drop-zone-bg); overflow: hidden; padding: 0.5rem; transition: border-color 0.3s ease; }
61
+ #result-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem; width: 100%; height: 100%; }
62
+ @keyframes popIn-spring { 0% { opacity: 0; transform: scale(0.8) translateY(20px); } 80% { opacity: 1; transform: scale(1.05) translateY(-5px); } 100% { opacity: 1; transform: scale(1) translateY(0); } }
63
+ #result-grid img { width: 100%; height: 100%; object-fit: cover; border-radius: 1rem; cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease; opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards; }
64
+ #result-grid img:hover { transform: scale(1.05) rotate(1deg); box-shadow: 0 10px 30px -5px var(--shadow-color-dark); }
65
+ #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ /* --- Loading Styles --- */
68
+ #loading-placeholder { display: none; flex-direction: column; align-items: center; justify-content: center; gap: 1.5rem; opacity: 0; animation: fadeIn 0.5s ease forwards; position: absolute; inset: 0; }
 
 
 
69
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
70
  #result-container.loading #loading-placeholder { display: flex; }
 
71
  .creative-loader { width: 100px; height: 100px; position: relative; }
72
  .creative-loader svg { width: 100%; height: 100%; transform-origin: center; }
73
  .creative-loader .arc { fill: none; stroke-width: 4; stroke-linecap: round; transform-origin: 50% 50%; animation: spin-loader 2s linear infinite; }
 
77
  .creative-loader .star { fill: var(--primary-color-end); transform-origin: center; animation: pulse-star 1.5s ease-in-out infinite; }
78
  @keyframes spin-loader { to { transform: rotate(360deg); } }
79
  @keyframes pulse-star { 0%, 100% { transform: scale(0.9); opacity: 0.8; } 50% { transform: scale(1.1); opacity: 1; } }
 
80
  .loading-text { font-weight: 500; color: #64748b; }
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ /* --- IMPROVED: History Gallery Styles --- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
84
+ #clear-history-btn { background: none; border: 1px solid var(--border-color); color: #64748b; padding: 0.5rem 1rem; border-radius: 0.75rem; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; font-family: 'Vazirmatn', sans-serif; font-weight: 500; transition: all 0.2s; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
86
+ #history-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.5rem; margin-top: 1.5rem; }
87
+ #history-grid:empty::before { content: 'هنوز تصویری خلق نکرده‌اید. اولین ویرایش شما اینجا ذخیره خواهد شد.'; color: #64748b; grid-column: 1 / -1; text-align: center; padding: 3rem 1rem; background-color: var(--drop-zone-bg); border-radius: 1rem; }
88
+ .history-item { background-color: var(--drop-zone-bg); border-radius: 1.25rem; border: 1px solid var(--border-color); overflow: hidden; box-shadow: 0 2px 8px var(--shadow-color-dark); transition: transform 0.3s ease, box-shadow 0.3s ease; opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards; display: flex; flex-direction: column; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  .history-item:hover { transform: translateY(-8px); box-shadow: 0 10px 20px var(--shadow-color-dark); }
90
+ .history-item-img-stack { position: relative; width: 100%; height: 180px; background-color: #e2e8f0; cursor: pointer; }
91
+ .history-item-img-stack img { position: absolute; width: 100%; height: 100%; object-fit: cover; border-radius: 0.5rem; box-shadow: 0 4px 15px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
92
+ .history-item-img-stack:hover img { transform: scale(1.05) !important; }
93
+ .history-item-img-stack img:nth-child(1) { z-index: 4; transform: rotate(2deg) scale(0.95); }
94
+ .history-item-img-stack img:nth-child(2) { z-index: 3; transform: rotate(-2deg) scale(0.9); }
95
+ .history-item-img-stack img:nth-child(3) { z-index: 2; transform: rotate(4deg) scale(0.85); }
96
+ .history-item-img-stack img:nth-child(4) { z-index: 1; transform: rotate(-4deg) scale(0.8); }
97
+ .history-item-content { padding: 1rem; display: flex; flex-direction: column; flex-grow: 1; }
98
+ .history-item-prompt { font-size: 0.9rem; font-weight: 500; margin: 0 0 1rem 0; min-height: 4.5em; line-height: 1.5em; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; flex-grow: 1; }
99
+ .history-item-actions { display: flex; gap: 0.5rem; margin-top: auto; }
100
+ .history-btn { flex-grow: 1; text-align: center; padding: 0.6rem; border-radius: 0.75rem; border: 1px solid var(--border-color); background-color: var(--card-bg); color: var(--text-color); font-weight: 600; display: flex; justify-content: center; align-items: center; gap: 0.5rem; cursor: pointer; transition: all 0.2s; text-decoration: none; }
 
 
 
 
 
 
 
 
 
 
 
 
101
  .history-btn:hover { background-color: var(--primary-color-start); color: white; border-color: var(--primary-color-start); }
102
 
103
+ /* --- IMPROVED: Lightbox with Navigation --- */
104
+ #lightbox { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(10, 20, 40, 0.9); z-index: 1000; display: none; justify-content: center; align-items: center; padding: 1rem; box-sizing: border-box; backdrop-filter: blur(8px); }
105
+ #lightbox-img { max-width: 90vw; max-height: 80vh; object-fit: contain; border-radius: 1rem; box-shadow: 0 20px 50px rgba(0,0,0,0.5); }
106
+ .lightbox-nav { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2); width: 44px; height: 44px; border-radius: 50%; cursor: pointer; display: flex; justify-content: center; align-items: center; transition: all 0.2s; font-size: 2rem; user-select: none; }
107
+ .lightbox-nav:hover { background-color: rgba(255, 255, 255, 0.2); transform: translateY(-50%) scale(1.1); }
108
+ #lightbox-prev { right: 20px; }
109
+ #lightbox-next { left: 20px; }
110
+ #lightbox-controls { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 1rem; }
111
+ #lightbox-counter { background-color: rgba(0,0,0,0.4); color: white; padding: 0.25rem 0.75rem; border-radius: 1rem; font-size: 0.9rem; font-weight: 500; }
112
+ #lightbox-close { position: absolute; top: 20px; right: 20px; font-size: 2rem; }
113
+ .lightbox-btn { background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2); width: 50px; height: 50px; border-radius: 50%; cursor: pointer; display: flex; justify-content: center; align-items: center; transition: all 0.2s; text-decoration: none; }
114
+ .lightbox-btn:hover { background-color: rgba(255, 255, 255, 0.2); transform: scale(1.1); }
115
+
116
+ @media (max-width: 600px) { body { padding: 1.5rem 1rem; } main, .gallery-section { padding: 1.5rem; } h1 { font-size: 2.5rem; } .lightbox-nav { display: none; } }
117
  </style>
118
  </head>
119
  <body>
 
124
  </header>
125
 
126
  <main>
127
+ <div class="panel">
128
  <h2>
129
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4Z"/></svg>
130
  ۱. تصویر خود را انتخاب کنید
131
  </h2>
132
  <div id="drop-zone">
133
+ <div id="drop-zone-content">
134
  <svg id="drop-zone-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
135
  <span id="drop-zone-text">تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</span>
136
  </div>
137
+ <img id="image-preview" src="#" alt="پیش‌نمایش تصویر" />
138
+ <!-- NEW: Change Image Overlay -->
139
+ <div id="change-image-overlay">
140
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4Z"/></svg>
141
+ <span>تغییر تصویر</span>
 
142
  </div>
143
  </div>
144
  <input type="file" id="file-input" accept="image/*" hidden>
 
149
  <div class="panel"> ... </div>
150
  </main>
151
 
152
+ <section class="gallery-section"> ... </section>
 
 
153
  </div>
154
 
155
+ <!-- IMPROVED: Lightbox with Navigation Controls -->
156
  <div id="lightbox">
157
+ <button id="lightbox-prev" class="lightbox-nav">&rsaquo;</button>
158
+ <button id="lightbox-next" class="lightbox-nav">&lsaquo;</button>
159
  <img id="lightbox-img" src="">
 
 
160
  <div id="lightbox-controls">
161
+ <span id="lightbox-counter"></span>
162
  <a id="lightbox-download" href="#" download="edited-image.png" title="دانلود تصویر" class="lightbox-btn">
163
+ <svg fill="white" viewBox="0 0 24 24" width="24" height="24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
164
  </a>
165
  </div>
166
+ <button id="lightbox-close" class="lightbox-btn" style="position: absolute; top: 20px; left: 20px;">&times;</button>
167
  </div>
168
 
 
 
 
169
  <script>
170
+ // --- Element Selection ---
171
+ const dropZone = document.getElementById('drop-zone');
172
+ const fileInput = document.getElementById('file-input');
173
+ const imagePreview = document.getElementById('image-preview');
174
+ const promptInput = document.getElementById('prompt-input');
175
+ const submitBtn = document.getElementById('submit-btn');
176
+ const resultGrid = document.getElementById('result-grid');
177
+ const historyGrid = document.getElementById('history-grid');
178
+ const clearHistoryBtn = document.getElementById('clear-history-btn');
179
+ const lightbox = document.getElementById('lightbox');
180
+ const lightboxImg = document.getElementById('lightbox-img');
181
+ const lightboxClose = document.getElementById('lightbox-close');
182
+ const lightboxDownload = document.getElementById('lightbox-download');
183
  const lightboxPrev = document.getElementById('lightbox-prev');
184
  const lightboxNext = document.getElementById('lightbox-next');
185
+ const lightboxCounter = document.getElementById('lightbox-counter');
186
+ // ... (other element selections remain the same)
187
+ const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
188
+ const dropZoneContent = document.getElementById('drop-zone-content');
189
+ const btnText = document.getElementById('btn-text');
190
+ const resultContainer = document.getElementById('result-container');
191
+ const errorMessage = document.getElementById('error-message');
192
+
193
+ let uploadedFile = null;
194
+ let lightboxImageUrls = [];
195
+ let currentLightboxIndex = 0;
196
 
197
+ // --- Event Listeners ---
198
+ dropZone.addEventListener('click', () => fileInput.click());
199
+ fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
200
+ ['dragenter', 'dragover'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.add('dragover'); }));
201
+ ['dragleave', 'drop'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); }));
202
+ dropZone.addEventListener('drop', (e) => handleFile(e.dataTransfer.files[0]));
203
+ promptInput.addEventListener('input', checkFormState);
204
+ submitBtn.addEventListener('click', handleSubmit);
205
+ clearHistoryBtn.addEventListener('click', handleClearHistory);
206
+ document.addEventListener('DOMContentLoaded', renderHistory);
207
+
208
+ // Lightbox Listeners
209
+ lightboxClose.addEventListener('click', closeLightbox);
210
+ lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); });
211
+ lightboxNext.addEventListener('click', showNextImage);
212
+ lightboxPrev.addEventListener('click', showPrevImage);
213
+ document.addEventListener('keydown', (e) => {
214
+ if (lightbox.style.display === 'flex') {
215
+ if (e.key === 'ArrowRight') showPrevImage(); // RTL, so Right arrow is previous
216
+ if (e.key === 'ArrowLeft') showNextImage(); // Left arrow is next
217
+ if (e.key === 'Escape') closeLightbox();
218
  }
219
  });
 
220
 
221
  // --- Core Functions ---
222
  function handleFile(file) {
223
+ if (!file || !file.type.startsWith('image/')) { displayError('لطفا یک فایل تصویری انتخاب کنید.'); return; }
 
 
 
224
  uploadedFile = file;
225
  const reader = new FileReader();
226
  reader.onload = (e) => {
227
  imagePreview.src = e.target.result;
228
+ imagePreview.classList.add('visible');
229
+ dropZone.classList.add('has-image'); // NEW: Add class to control overlay visibility
230
+ dropZoneContent.style.opacity = '0';
231
  checkFormState();
232
  clearResult();
233
  };
234
  reader.readAsDataURL(file);
235
  }
236
+
237
+ // ... (handleSubmit, setLoading, checkFormState, displayError, clearResult remain mostly the same)
238
+ async function handleSubmit() {
239
+ if (submitBtn.disabled) return;
240
+ // ... (rest of the submit logic is the same)
241
+ // ... on success:
242
+ displayResult(result.image_urls, true); // Pass true to show in result grid
243
+ addToHistory({ prompt: promptInput.value.trim(), urls: result.image_urls });
244
+ }
245
 
246
+ function displayResult(imageUrls, isLiveResult = false) {
247
+ if (!isLiveResult) return; // Only populate the main result grid for live results
248
  resultGrid.innerHTML = '';
249
  imageUrls.forEach((url, index) => {
250
  const img = document.createElement('img');
251
  img.src = url;
252
+ img.alt = 'تصویر ویرایش شده';
253
  img.style.animationDelay = `${index * 120}ms`;
254
+ img.addEventListener('click', () => openLightbox(imageUrls, index));
 
255
  resultGrid.appendChild(img);
256
  });
257
  }
258
+
259
+ // --- IMPROVED Lightbox Functions with Navigation ---
260
+ function openLightbox(urls, index = 0) {
261
+ lightboxImageUrls = urls;
262
+ currentLightboxIndex = index;
 
 
 
 
 
 
 
 
 
 
 
263
  lightbox.style.display = 'flex';
264
+ updateLightboxImage();
265
  }
266
+
267
+ function closeLightbox() {
268
+ lightbox.style.display = 'none';
269
+ }
270
+
271
  function updateLightboxImage() {
272
+ if (lightboxImageUrls.length === 0) return;
273
+ const currentUrl = lightboxImageUrls[currentLightboxIndex];
274
+ lightboxImg.src = currentUrl;
275
+ lightboxDownload.href = currentUrl;
276
+ lightboxCounter.textContent = `${currentLightboxIndex + 1} / ${lightboxImageUrls.length}`;
277
+ // Preload next and previous images for smoother navigation
278
+ if(currentLightboxIndex < lightboxImageUrls.length - 1) new Image().src = lightboxImageUrls[currentLightboxIndex + 1];
279
+ if(currentLightboxIndex > 0) new Image().src = lightboxImageUrls[currentLightboxIndex - 1];
280
  }
281
 
282
  function showNextImage() {
283
+ currentLightboxIndex = (currentLightboxIndex + 1) % lightboxImageUrls.length;
284
  updateLightboxImage();
285
  }
286
 
287
  function showPrevImage() {
288
+ currentLightboxIndex = (currentLightboxIndex - 1 + lightboxImageUrls.length) % lightboxImageUrls.length;
289
  updateLightboxImage();
290
  }
 
 
 
291
 
292
+ // --- IMPROVED History Functions ---
293
+ function getHistory() { /* ... (same) ... */ }
294
+ function saveHistory(history) { /* ... (same) ... */ }
295
+ function addToHistory(item) { /* ... (same) ... */ }
296
+ function handleClearHistory() { /* ... (same) ... */ }
297
 
 
298
  function renderHistory() {
299
  const history = getHistory();
300
+ historyGrid.innerHTML = '';
301
 
302
+ clearHistoryBtn.style.display = history.length > 0 ? 'flex' : 'none';
 
 
 
 
 
303
 
304
+ history.forEach((item, index) => {
305
+ const card = document.createElement('div');
306
+ card.className = 'history-item';
307
+ card.style.animationDelay = `${index * 80}ms`;
308
 
309
+ const imageStackHTML = item.urls.slice(0, 4).map(url =>
310
+ `<img src="${url}" alt="پیش‌نمایش تاریخچه">`
311
+ ).join('');
312
+
313
+ card.innerHTML = `
314
+ <div class="history-item-img-stack">
315
+ ${imageStackHTML}
316
+ </div>
317
+ <div class="history-item-content">
318
+ <p class="history-item-prompt">${item.prompt}</p>
319
+ <div class="history-item-actions">
320
+ <a href="${item.urls[0]}" download="ai-edit-${Date.now()}.png" class="history-btn">
321
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17v-10"/><path d="m15 14-3 3-3-3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
322
+ دانلود
323
+ </a>
 
 
324
  </div>
325
+ </div>
326
+ `;
327
+
328
+ // Event listener for the entire image stack
329
+ card.querySelector('.history-item-img-stack').addEventListener('click', () => openLightbox(item.urls, 0));
330
+
331
+ historyGrid.appendChild(card);
332
+ });
333
  }
334
 
335
+ // (Helper functions like checkFormState, setLoading etc. are assumed to be here and unchanged)
336
+ // Make sure to copy them from the previous version if they are missing.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  function checkFormState() { submitBtn.disabled = !uploadedFile || promptInput.value.trim() === ''; }
338
+ function setLoading(isLoading) { if (isLoading) { resultContainer.classList.add('loading'); btnText.classList.add('hidden'); const loader = document.createElement('div'); loader.className = 'loader-dots'; loader.innerHTML = '<div class="dot1"></div><div class="dot2"></div><div class="dot3"></div>'; submitBtn.appendChild(loader); submitBtn.disabled = true; resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; } else { resultContainer.classList.remove('loading'); btnText.classList.remove('hidden'); const loader = submitBtn.querySelector('.loader-dots'); if (loader) submitBtn.removeChild(loader); checkFormState(); } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  function clearResult() { resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; }
340
  function displayError(message) { errorMessage.textContent = message; errorMessage.style.display = 'block'; }
341
 
 
 
 
 
342
  checkFormState();
343
  </script>
344
  </body>