Elias207 commited on
Commit
48f64b5
·
verified ·
1 Parent(s): 340bea4

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +199 -90
index.html CHANGED
@@ -17,6 +17,7 @@
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
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
21
  }
22
 
@@ -78,16 +79,18 @@
78
  }
79
  .dark p.subtitle { color: #94a3b8; }
80
 
81
- main {
82
  background-color: var(--card-bg);
83
  border-radius: 2rem;
84
  border: 1px solid var(--border-color);
85
  box-shadow: 0 4px 10px var(--shadow-color-dark), 0 20px 40px var(--shadow-color-light);
86
  padding: 3rem;
87
  transition: all 0.4s;
88
- animation: fadeInUp 0.8s var(--ease-out-expo) 0.2s forwards;
89
  opacity: 0;
90
  }
 
 
91
 
92
  .panel, .controls { margin-bottom: 3rem; }
93
  .panel:last-child { margin-bottom: 0; }
@@ -113,7 +116,6 @@
113
  display: flex; flex-direction: column; justify-content: center;
114
  align-items: center; min-height: 280px;
115
  }
116
- /* --- NEW: Cool gradient border on hover --- */
117
  #drop-zone::before {
118
  content: ''; position: absolute; inset: -2px; border-radius: 1.5rem;
119
  background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end)) border-box;
@@ -165,7 +167,7 @@
165
  button#submit-btn:hover:not(:disabled) {
166
  transform: translateY(-4px);
167
  box-shadow: 0 10px 25px -5px var(--shadow-color-light);
168
- background-position: right center; /* --- NEW: Gradient slide effect --- */
169
  }
170
  button#submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
171
  .loader-dots {
@@ -190,46 +192,22 @@
190
  }
191
 
192
  #loading-placeholder {
193
- display: none;
194
- flex-direction: column; align-items: center; justify-content: center;
195
- gap: 1.5rem; opacity: 0;
196
- animation: fadeIn 0.5s ease forwards;
197
  position: absolute; inset: 0;
198
  }
199
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
200
  #result-container.loading #loading-placeholder { display: flex; }
201
 
202
- /* --- NEW & IMPROVED: Creative Loading Spinner --- */
203
- .creative-loader {
204
- width: 100px;
205
- height: 100px;
206
- position: relative;
207
- }
208
- .creative-loader svg {
209
- width: 100%;
210
- height: 100%;
211
- transform-origin: center;
212
- }
213
- .creative-loader .arc {
214
- fill: none;
215
- stroke-width: 4;
216
- stroke-linecap: round;
217
- transform-origin: 50% 50%;
218
- animation: spin-loader 2s linear infinite;
219
- }
220
  .creative-loader .arc1 { stroke: var(--primary-color-start); animation-duration: 2.2s; }
221
  .creative-loader .arc2 { stroke: var(--primary-color-end); animation-duration: 1.8s; animation-direction: reverse; }
222
  .creative-loader .arc3 { stroke: #8b5cf6; animation-duration: 2s; }
223
- .creative-loader .star {
224
- fill: var(--primary-color-end);
225
- transform-origin: center;
226
- animation: pulse-star 1.5s ease-in-out infinite;
227
- }
228
  @keyframes spin-loader { to { transform: rotate(360deg); } }
229
- @keyframes pulse-star {
230
- 0%, 100% { transform: scale(0.9); opacity: 0.8; }
231
- 50% { transform: scale(1.1); opacity: 1; }
232
- }
233
 
234
  .loading-text { font-weight: 500; color: #64748b; }
235
  .dark .loading-text { color: #94a3b8; }
@@ -238,25 +216,16 @@
238
  display: grid; grid-template-columns: repeat(2, 1fr);
239
  gap: 0.75rem; width: 100%; height: 100%;
240
  }
241
- /* --- NEW: Satisfying pop-in animation for results --- */
242
- @keyframes popIn-spring {
243
- 0% { opacity: 0; transform: scale(0.8) translateY(20px); }
244
- 80% { opacity: 1; transform: scale(1.05) translateY(-5px); }
245
- 100% { opacity: 1; transform: scale(1) translateY(0); }
246
- }
247
  #result-grid img {
248
  width: 100%; height: 100%; object-fit: cover; border-radius: 1rem;
249
  cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease;
250
  opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards;
251
  }
252
- #result-grid img:hover {
253
- transform: scale(1.05) rotate(1deg);
254
- box-shadow: 0 10px 30px -5px var(--shadow-color-dark);
255
- }
256
 
257
- #error-message { color: #ef4444; text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
258
 
259
- /* --- NEW: Improved Lightbox styles --- */
260
  #lightbox {
261
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
262
  background-color: rgba(10, 20, 40, 0.85); z-index: 1000; display: none;
@@ -264,20 +233,82 @@
264
  box-sizing: border-box; backdrop-filter: blur(8px);
265
  }
266
  #lightbox-img { max-width: 90vw; max-height: 85vh; object-fit: contain; border-radius: 1rem; box-shadow: 0 20px 50px rgba(0,0,0,0.5); }
267
- #lightbox-content { position: relative; }
268
- #lightbox-close, #lightbox-download {
269
- position: absolute; top: -50px; background-color: rgba(255, 255, 255, 0.1);
270
- color: white; border: 1px solid rgba(255,255,255,0.2); width: 40px; height: 40px; border-radius: 50%;
271
- cursor: pointer; font-size: 1.5rem; display: flex; justify-content: center; align-items: center; transition: all 0.2s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  }
273
- #lightbox-close { right: 60px; /* Positioned to the right of download */ }
274
- #lightbox-download { right: 10px; }
275
- #lightbox-close:hover, #lightbox-download:hover { background-color: rgba(255, 255, 255, 0.2); transform: scale(1.1); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
 
278
  @media (max-width: 600px) {
279
  body { padding: 1.5rem 1rem; }
280
- main { padding: 1.5rem; }
281
  h1 { font-size: 2.5rem; }
282
  }
283
  </style>
@@ -293,14 +324,12 @@
293
  <main>
294
  <div class="panel">
295
  <h2>
296
- <!-- NEW SVG ICON -->
297
- <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.12 5.57a2.25 2.25 0 0 0-2.24-2.24l-1.13.3a2.25 2.25 0 0 1-2.61-2.61l.3-1.13a2.25 2.25 0 0 0-2.24-2.24l-.3 1.13a2.25 2.25 0 0 1-2.61-2.61l1.13-.3A2.25 2.25 0 0 0 8.43.37l-1.13.3a2.25 2.25 0 0 1-2.61-2.61l.3-1.13A2.25 2.25 0 0 0 .37 2.88l.3 1.13a2.25 2.25 0 0 1 2.61 2.61l-1.13.3a2.25 2.25 0 0 0 2.24 2.24l1.13-.3a2.25 2.25 0 0 1 2.61 2.61l-.3 1.13a2.25 2.25 0 0 0 2.24 2.24l.3-1.13a2.25 2.25 0 0 1 2.61 2.61l-1.13.3a2.25 2.25 0 0 0 2.24 2.24l1.13-.3a2.25 2.25 0 0 1 2.61 2.61l-.3 1.13a2.25 2.25 0 0 0 2.24 2.24l.3-1.13a2.25 2.25 0 0 1 2.61-2.61l-1.13-.3a2.25 2.25 0 0 0-2.24-2.24Z"/><path d="m12 12.5 2.5 2.5"/><path d="M12 12.5 9.5 9.5"/><circle cx="12" cy="12" r="5"/><path d="M2.88 15.57a2.25 2.25 0 0 0 2.24 2.24l.3-1.13a2.25 2.25 0 0 1 2.61-2.61l-1.13-.3A2.25 2.25 0 0 0 4.58 12l-1.13.3a2.25 2.25 0 0 1-2.61 2.61Z"/></svg>
298
  ۱. تصویر خود را انتخاب کنید
299
  </h2>
300
  <div id="drop-zone">
301
  <div id="drop-zone-content">
302
- <!-- NEW SVG ICON -->
303
- <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 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>
304
  <span id="drop-zone-text">تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</span>
305
  </div>
306
  <img id="image-preview" src="#" alt="پیش‌نمایش تصویر" />
@@ -310,8 +339,7 @@
310
 
311
  <div class="controls">
312
  <h2>
313
- <!-- NEW SVG ICON -->
314
- <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 22h6a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v10"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10.4 12.6a2 2 0 1 1 3 3L8 21l-4 1 1-4Z"/></svg>
315
  ۲. دستور ویرایش را بنویسید
316
  </h2>
317
  <textarea id="prompt-input" rows="3" placeholder="مثال: پس‌زمینه را حذف کن و یک ساحل آفتابی قرار بده"></textarea>
@@ -323,21 +351,13 @@
323
 
324
  <div class="panel">
325
  <h2>
326
- <!-- NEW SVG ICON -->
327
- <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 3-1.9 5.8-5.9 1.2 4.4 4.3-1 6.1L12 17l5.4 3.5-1-6.1 4.4-4.3-5.9-1.2L12 3z"/><path d="M22 19l-1.9 2.8-2.9.2 2.1 2.3-1 3.1L22 25l2.4 1.5-1-3.1 2.1-2.3-2.9-.2L22 19z"/><path d="M5 3 3.1 5.8 0 7l2.1 2.3-1 3.1L5 11l2.4 1.5-1-3.1L8.5 7 5.6 5.8 5 3z"/></svg>
328
  ۳. نتیجه را ببینید
329
  </h2>
330
  <div id="result-container">
331
  <div id="loading-placeholder">
332
  <div class="creative-loader">
333
- <svg viewBox="0 0 50 50">
334
- <g>
335
- <circle class="arc arc1" cx="25" cy="25" r="22" stroke-dasharray="60 150"></circle>
336
- <circle class="arc arc2" cx="25" cy="25" r="18" stroke-dasharray="60 150"></circle>
337
- <circle class="arc arc3" cx="25" cy="25" r="14" stroke-dasharray="60 150"></circle>
338
- <path class="star" d="M25,21 L26.9,24.1 L30,25 L26.9,25.9 L25,29 L23.1,25.9 L20,25 L23.1,24.1 Z"></path>
339
- </g>
340
- </svg>
341
  </div>
342
  <p class="loading-text">هوش مصنوعی در حال خلق اثر شماست...</p>
343
  </div>
@@ -345,24 +365,35 @@
345
  </div>
346
  </div>
347
  </main>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  </div>
349
 
350
  <!-- Lightbox Structure -->
351
  <div id="lightbox">
352
- <div id="lightbox-content">
353
- <img id="lightbox-img" src="">
354
- <!-- NEW Buttons -->
355
- <button id="lightbox-close" title="بستن">
356
- <svg fill="white" width="20px" height="20px" viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
357
- </button>
358
- <a id="lightbox-download" href="#" download="edited-image.png" title="دانلود تصویر">
359
- <svg fill="white" width="20px" height="20px" viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
360
- </a>
361
- </div>
362
  </div>
363
 
364
  <script>
365
- // The JavaScript logic remains unchanged, but now it powers a much more dynamic UI!
366
  const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
367
  const dropZone = document.getElementById('drop-zone');
368
  const dropZoneContent = document.getElementById('drop-zone-content');
@@ -378,15 +409,25 @@
378
  const lightboxImg = document.getElementById('lightbox-img');
379
  const lightboxClose = document.getElementById('lightbox-close');
380
  const lightboxDownload = document.getElementById('lightbox-download');
 
 
 
381
  let uploadedFile = null;
382
 
 
383
  dropZone.addEventListener('click', () => fileInput.click());
384
  fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
385
  ['dragenter', 'dragover'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.add('dragover'); }));
386
  ['dragleave', 'drop'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); }));
387
  dropZone.addEventListener('drop', (e) => handleFile(e.dataTransfer.files[0]));
388
  promptInput.addEventListener('input', checkFormState);
 
 
 
 
 
389
 
 
390
  function handleFile(file) {
391
  if (!file || !file.type.startsWith('image/')) { displayError('لطفا یک فایل تصویری انتخاب کنید.'); return; }
392
  uploadedFile = file;
@@ -405,7 +446,7 @@
405
  submitBtn.disabled = !uploadedFile || promptInput.value.trim() === '';
406
  }
407
 
408
- submitBtn.addEventListener('click', async () => {
409
  if (submitBtn.disabled) return;
410
  setLoading(true);
411
  const formData = new FormData();
@@ -420,19 +461,21 @@
420
  const result = await response.json();
421
  if (result.image_urls && Array.isArray(result.image_urls) && result.image_urls.length > 0) {
422
  displayResult(result.image_urls);
 
 
423
  } else {
424
- throw new Error('پاسخ معتبری از سرور دریافت نشد. ممکن است سرور موقتا با مشکل مواجه شده باشد.');
425
  }
426
  } catch (error) {
427
  displayError(error.message);
428
  } finally {
429
  setLoading(false);
430
  }
431
- });
432
 
433
  function setLoading(isLoading) {
434
  if (isLoading) {
435
- resultContainer.classList.add('loading'); // This now triggers the creative loader
436
  btnText.classList.add('hidden');
437
  const loader = document.createElement('div');
438
  loader.className = 'loader-dots';
@@ -456,7 +499,6 @@
456
  const img = document.createElement('img');
457
  img.src = url;
458
  img.alt = 'تصویر ویرایش شده';
459
- // NEW: Add staggered animation delay
460
  img.style.animationDelay = `${index * 120}ms`;
461
  img.addEventListener('click', () => openLightbox(url));
462
  resultGrid.appendChild(img);
@@ -473,15 +515,82 @@
473
  errorMessage.style.display = 'block';
474
  }
475
 
 
476
  function openLightbox(url) {
477
  lightboxImg.src = url;
478
  lightboxDownload.href = url;
479
  lightbox.style.display = 'flex';
480
  }
481
  function closeLightbox() { lightbox.style.display = 'none'; }
482
- lightboxClose.addEventListener('click', closeLightbox);
483
- lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); });
484
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
  checkFormState();
486
  </script>
487
  </body>
 
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
 
 
79
  }
80
  .dark p.subtitle { color: #94a3b8; }
81
 
82
+ main, .gallery-section {
83
  background-color: var(--card-bg);
84
  border-radius: 2rem;
85
  border: 1px solid var(--border-color);
86
  box-shadow: 0 4px 10px var(--shadow-color-dark), 0 20px 40px var(--shadow-color-light);
87
  padding: 3rem;
88
  transition: all 0.4s;
89
+ animation: fadeInUp 0.8s var(--ease-out-expo) forwards;
90
  opacity: 0;
91
  }
92
+ main { animation-delay: 0.2s; }
93
+ .gallery-section { margin-top: 3rem; animation-delay: 0.4s; }
94
 
95
  .panel, .controls { margin-bottom: 3rem; }
96
  .panel:last-child { margin-bottom: 0; }
 
116
  display: flex; flex-direction: column; justify-content: center;
117
  align-items: center; min-height: 280px;
118
  }
 
119
  #drop-zone::before {
120
  content: ''; position: absolute; inset: -2px; border-radius: 1.5rem;
121
  background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end)) border-box;
 
167
  button#submit-btn:hover:not(:disabled) {
168
  transform: translateY(-4px);
169
  box-shadow: 0 10px 25px -5px var(--shadow-color-light);
170
+ background-position: right center;
171
  }
172
  button#submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
173
  .loader-dots {
 
192
  }
193
 
194
  #loading-placeholder {
195
+ display: none; flex-direction: column; align-items: center; justify-content: center;
196
+ gap: 1.5rem; opacity: 0; animation: fadeIn 0.5s ease forwards;
 
 
197
  position: absolute; inset: 0;
198
  }
199
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
200
  #result-container.loading #loading-placeholder { display: flex; }
201
 
202
+ .creative-loader { width: 100px; height: 100px; position: relative; }
203
+ .creative-loader svg { width: 100%; height: 100%; transform-origin: center; }
204
+ .creative-loader .arc { fill: none; stroke-width: 4; stroke-linecap: round; transform-origin: 50% 50%; animation: spin-loader 2s linear infinite; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  .creative-loader .arc1 { stroke: var(--primary-color-start); animation-duration: 2.2s; }
206
  .creative-loader .arc2 { stroke: var(--primary-color-end); animation-duration: 1.8s; animation-direction: reverse; }
207
  .creative-loader .arc3 { stroke: #8b5cf6; animation-duration: 2s; }
208
+ .creative-loader .star { fill: var(--primary-color-end); transform-origin: center; animation: pulse-star 1.5s ease-in-out infinite; }
 
 
 
 
209
  @keyframes spin-loader { to { transform: rotate(360deg); } }
210
+ @keyframes pulse-star { 0%, 100% { transform: scale(0.9); opacity: 0.8; } 50% { transform: scale(1.1); opacity: 1; } }
 
 
 
211
 
212
  .loading-text { font-weight: 500; color: #64748b; }
213
  .dark .loading-text { color: #94a3b8; }
 
216
  display: grid; grid-template-columns: repeat(2, 1fr);
217
  gap: 0.75rem; width: 100%; height: 100%;
218
  }
219
+ @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); } }
 
 
 
 
 
220
  #result-grid img {
221
  width: 100%; height: 100%; object-fit: cover; border-radius: 1rem;
222
  cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease;
223
  opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards;
224
  }
225
+ #result-grid img:hover { transform: scale(1.05) rotate(1deg); box-shadow: 0 10px 30px -5px var(--shadow-color-dark); }
 
 
 
226
 
227
+ #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
228
 
 
229
  #lightbox {
230
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
231
  background-color: rgba(10, 20, 40, 0.85); z-index: 1000; display: none;
 
233
  box-sizing: border-box; backdrop-filter: blur(8px);
234
  }
235
  #lightbox-img { max-width: 90vw; max-height: 85vh; object-fit: contain; border-radius: 1rem; box-shadow: 0 20px 50px rgba(0,0,0,0.5); }
236
+ #lightbox-controls { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; gap: 1rem; }
237
+ .lightbox-btn {
238
+ background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2);
239
+ width: 50px; height: 50px; border-radius: 50%; cursor: pointer; display: flex;
240
+ justify-content: center; align-items: center; transition: all 0.2s; text-decoration: none;
241
+ }
242
+ .lightbox-btn svg { width: 24px; height: 24px; }
243
+ .lightbox-btn:hover { background-color: rgba(255, 255, 255, 0.2); transform: scale(1.1); }
244
+ #lightbox-close { position: absolute; top: 20px; right: 20px; font-size: 2rem; }
245
+
246
+ /* --- NEW: History Gallery Styles --- */
247
+ .gallery-header { display: flex; justify-content: space-between; align-items: center; }
248
+ #clear-history-btn {
249
+ background: none; border: 1px solid var(--border-color); color: #64748b;
250
+ padding: 0.5rem 1rem; border-radius: 0.75rem; cursor: pointer;
251
+ display: flex; align-items: center; gap: 0.5rem;
252
+ font-family: 'Vazirmatn', sans-serif; font-weight: 500; transition: all 0.2s;
253
+ }
254
+ #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
255
+ #clear-history-btn svg { width: 18px; height: 18px; }
256
+
257
+ #history-grid {
258
+ display: grid;
259
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
260
+ gap: 1.5rem;
261
+ margin-top: 1.5rem;
262
+ }
263
+ #history-grid:empty::before {
264
+ content: 'هنوز تصویری خلق نکرده‌اید. اولین ویرایش شما اینجا ذخیره خواهد شد.';
265
+ color: #64748b;
266
+ .dark & { color: #94a3b8; }
267
+ grid-column: 1 / -1;
268
+ text-align: center;
269
+ padding: 3rem 1rem;
270
+ background-color: var(--drop-zone-bg);
271
+ border-radius: 1rem;
272
+ }
273
+ .history-item {
274
+ background-color: var(--drop-zone-bg);
275
+ border-radius: 1.25rem;
276
+ border: 1px solid var(--border-color);
277
+ overflow: hidden;
278
+ box-shadow: 0 2px 8px var(--shadow-color-dark);
279
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
280
+ opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards;
281
  }
282
+ .history-item:hover { transform: translateY(-8px); box-shadow: 0 10px 20px var(--shadow-color-dark); }
283
+ .history-item-img {
284
+ width: 100%;
285
+ height: 180px;
286
+ object-fit: cover;
287
+ display: block;
288
+ }
289
+ .history-item-content { padding: 1rem; }
290
+ .history-item-prompt {
291
+ font-size: 0.9rem; font-weight: 500;
292
+ margin: 0 0 1rem 0; height: 4.5em; /* 3 lines with line-height */
293
+ line-height: 1.5em; overflow: hidden;
294
+ display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
295
+ }
296
+ .history-item-actions { display: flex; gap: 0.5rem; }
297
+ .history-btn {
298
+ flex-grow: 1; text-align: center;
299
+ padding: 0.6rem; border-radius: 0.75rem;
300
+ border: 1px solid var(--border-color); background-color: var(--card-bg);
301
+ color: var(--text-color); font-weight: 600;
302
+ display: flex; justify-content: center; align-items: center; gap: 0.5rem;
303
+ cursor: pointer; transition: all 0.2s; text-decoration: none;
304
+ }
305
+ .history-btn svg { width: 18px; height: 18px; }
306
+ .history-btn:hover { background-color: var(--primary-color-start); color: white; border-color: var(--primary-color-start); }
307
 
308
 
309
  @media (max-width: 600px) {
310
  body { padding: 1.5rem 1rem; }
311
+ main, .gallery-section { padding: 1.5rem; }
312
  h1 { font-size: 2.5rem; }
313
  }
314
  </style>
 
324
  <main>
325
  <div class="panel">
326
  <h2>
327
+ <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>
 
328
  ۱. تصویر خود را انتخاب کنید
329
  </h2>
330
  <div id="drop-zone">
331
  <div id="drop-zone-content">
332
+ <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>
 
333
  <span id="drop-zone-text">تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</span>
334
  </div>
335
  <img id="image-preview" src="#" alt="پیش‌نمایش تصویر" />
 
339
 
340
  <div class="controls">
341
  <h2>
342
+ <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="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
 
343
  ۲. دستور ویرایش را بنویسید
344
  </h2>
345
  <textarea id="prompt-input" rows="3" placeholder="مثال: پس‌زمینه را حذف کن و یک ساحل آفتابی قرار بده"></textarea>
 
351
 
352
  <div class="panel">
353
  <h2>
354
+ <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="M10 3L12 8L17 10L12 12L10 17L8 12L3 10L8 8L10 3z"/><path d="M21 14l-1.5 3-3-1.5 3-3 1.5 3z"/><path d="M19.5 2.5l-3 1.5 1.5 3 3-1.5-1.5-3z"/></svg>
 
355
  ۳. نتیجه را ببینید
356
  </h2>
357
  <div id="result-container">
358
  <div id="loading-placeholder">
359
  <div class="creative-loader">
360
+ <svg viewBox="0 0 50 50"><g><circle class="arc arc1" cx="25" cy="25" r="22" stroke-dasharray="60 150"></circle><circle class="arc arc2" cx="25" cy="25" r="18" stroke-dasharray="60 150"></circle><circle class="arc arc3" cx="25" cy="25" r="14" stroke-dasharray="60 150"></circle><path class="star" d="M25,21 L26.9,24.1 L30,25 L26.9,25.9 L25,29 L23.1,25.9 L20,25 L23.1,24.1 Z"></path></g></svg>
 
 
 
 
 
 
 
361
  </div>
362
  <p class="loading-text">هوش مصنوعی در حال خلق اثر شماست...</p>
363
  </div>
 
365
  </div>
366
  </div>
367
  </main>
368
+
369
+ <!-- NEW: History Gallery Section -->
370
+ <section class="gallery-section">
371
+ <div class="gallery-header">
372
+ <h2>
373
+ <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"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></svg>
374
+ گالری و تاریخچه شما
375
+ </h2>
376
+ <button id="clear-history-btn" style="display: none;">
377
+ <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="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
378
+ <span>پاک کردن تاریخچه</span>
379
+ </button>
380
+ </div>
381
+ <div id="history-grid"></div>
382
+ </section>
383
  </div>
384
 
385
  <!-- Lightbox Structure -->
386
  <div id="lightbox">
387
+ <img id="lightbox-img" src="">
388
+ <div id="lightbox-controls">
389
+ <a id="lightbox-download" href="#" download="edited-image.png" title="دانلود تصویر" class="lightbox-btn">
390
+ <svg fill="white" viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
391
+ </a>
392
+ </div>
393
+ <button id="lightbox-close" class="lightbox-btn">&times;</button>
 
 
 
394
  </div>
395
 
396
  <script>
 
397
  const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
398
  const dropZone = document.getElementById('drop-zone');
399
  const dropZoneContent = document.getElementById('drop-zone-content');
 
409
  const lightboxImg = document.getElementById('lightbox-img');
410
  const lightboxClose = document.getElementById('lightbox-close');
411
  const lightboxDownload = document.getElementById('lightbox-download');
412
+ // New History Gallery Elements
413
+ const historyGrid = document.getElementById('history-grid');
414
+ const clearHistoryBtn = document.getElementById('clear-history-btn');
415
  let uploadedFile = null;
416
 
417
+ // --- Event Listeners Setup ---
418
  dropZone.addEventListener('click', () => fileInput.click());
419
  fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
420
  ['dragenter', 'dragover'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.add('dragover'); }));
421
  ['dragleave', 'drop'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); }));
422
  dropZone.addEventListener('drop', (e) => handleFile(e.dataTransfer.files[0]));
423
  promptInput.addEventListener('input', checkFormState);
424
+ submitBtn.addEventListener('click', handleSubmit);
425
+ lightboxClose.addEventListener('click', closeLightbox);
426
+ lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); });
427
+ clearHistoryBtn.addEventListener('click', handleClearHistory);
428
+ document.addEventListener('DOMContentLoaded', renderHistory); // Load history on page load
429
 
430
+ // --- Core Functions ---
431
  function handleFile(file) {
432
  if (!file || !file.type.startsWith('image/')) { displayError('لطفا یک فایل تصویری انتخاب کنید.'); return; }
433
  uploadedFile = file;
 
446
  submitBtn.disabled = !uploadedFile || promptInput.value.trim() === '';
447
  }
448
 
449
+ async function handleSubmit() {
450
  if (submitBtn.disabled) return;
451
  setLoading(true);
452
  const formData = new FormData();
 
461
  const result = await response.json();
462
  if (result.image_urls && Array.isArray(result.image_urls) && result.image_urls.length > 0) {
463
  displayResult(result.image_urls);
464
+ // NEW: Save to history
465
+ addToHistory({ prompt: promptInput.value.trim(), urls: result.image_urls });
466
  } else {
467
+ throw new Error('پاسخ معتبری از سرور دریافت نشد.');
468
  }
469
  } catch (error) {
470
  displayError(error.message);
471
  } finally {
472
  setLoading(false);
473
  }
474
+ }
475
 
476
  function setLoading(isLoading) {
477
  if (isLoading) {
478
+ resultContainer.classList.add('loading');
479
  btnText.classList.add('hidden');
480
  const loader = document.createElement('div');
481
  loader.className = 'loader-dots';
 
499
  const img = document.createElement('img');
500
  img.src = url;
501
  img.alt = 'تصویر ویرایش شده';
 
502
  img.style.animationDelay = `${index * 120}ms`;
503
  img.addEventListener('click', () => openLightbox(url));
504
  resultGrid.appendChild(img);
 
515
  errorMessage.style.display = 'block';
516
  }
517
 
518
+ // --- Lightbox Functions ---
519
  function openLightbox(url) {
520
  lightboxImg.src = url;
521
  lightboxDownload.href = url;
522
  lightbox.style.display = 'flex';
523
  }
524
  function closeLightbox() { lightbox.style.display = 'none'; }
 
 
525
 
526
+ // --- NEW: History Management Functions ---
527
+ function getHistory() {
528
+ try {
529
+ const history = localStorage.getItem('aiPhotoshopHistory');
530
+ return history ? JSON.parse(history) : [];
531
+ } catch (e) {
532
+ console.error("Failed to parse history from localStorage", e);
533
+ return [];
534
+ }
535
+ }
536
+
537
+ function saveHistory(history) {
538
+ localStorage.setItem('aiPhotoshopHistory', JSON.stringify(history));
539
+ renderHistory();
540
+ }
541
+
542
+ function addToHistory(item) {
543
+ const history = getHistory();
544
+ history.unshift(item); // Add to the beginning (newest first)
545
+ if (history.length > 20) { // Keep history size manageable
546
+ history.pop();
547
+ }
548
+ saveHistory(history);
549
+ }
550
+
551
+ function handleClearHistory() {
552
+ if (confirm('آیا از پاک کردن تمام تاریخچه ویرایش‌ها مطمئن هستید؟')) {
553
+ localStorage.removeItem('aiPhotoshopHistory');
554
+ renderHistory();
555
+ }
556
+ }
557
+
558
+ function renderHistory() {
559
+ const history = getHistory();
560
+ historyGrid.innerHTML = ''; // Clear current grid
561
+
562
+ if (history.length > 0) {
563
+ clearHistoryBtn.style.display = 'flex';
564
+ history.forEach((item, index) => {
565
+ const card = document.createElement('div');
566
+ card.className = 'history-item';
567
+ card.style.animationDelay = `${index * 80}ms`;
568
+
569
+ card.innerHTML = `
570
+ <img src="${item.urls[0]}" alt="پیش‌نمایش تاریخچه" class="history-item-img">
571
+ <div class="history-item-content">
572
+ <p class="history-item-prompt">${item.prompt}</p>
573
+ <div class="history-item-actions">
574
+ <button class="history-btn view-all-btn">
575
+ <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>
576
+ مشاهده
577
+ </button>
578
+ <a href="${item.urls[0]}" download="ai-edit-${Date.now()}.png" class="history-btn">
579
+ <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>
580
+ دانلود
581
+ </a>
582
+ </div>
583
+ </div>
584
+ `;
585
+ // Add event listener to the "View All" button to open the first image in lightbox
586
+ card.querySelector('.view-all-btn').addEventListener('click', () => openLightbox(item.urls[0]));
587
+ historyGrid.appendChild(card);
588
+ });
589
+ } else {
590
+ clearHistoryBtn.style.display = 'none';
591
+ }
592
+ }
593
+
594
  checkFormState();
595
  </script>
596
  </body>