Elias207 commited on
Commit
37ca648
·
verified ·
1 Parent(s): 7fecf66

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +111 -115
index.html CHANGED
@@ -75,6 +75,7 @@
75
 
76
  .form-group { margin-bottom: 2.5rem; }
77
  .form-group:last-child { margin-bottom: 0; }
 
78
  .form-label {
79
  display: flex;
80
  align-items: center;
@@ -86,11 +87,9 @@
86
  }
87
  .form-label svg { width: 24px; height: 24px; color: var(--accent-primary); }
88
 
89
- /* --- CHANGE 1: Styles for the new uploader preview --- */
90
- #upload-container.has-file #upload-area { display: none; }
91
- #upload-container:not(.has-file) #file-preview { display: none; }
92
-
93
  #upload-area {
 
94
  border: 2px dashed var(--input-border);
95
  border-radius: var(--radius-input);
96
  padding: 2.5rem;
@@ -98,52 +97,73 @@
98
  cursor: pointer;
99
  transition: var(--transition-smooth);
100
  background-color: var(--input-bg);
 
 
 
 
 
 
101
  }
102
- #upload-area.drag-over, #upload-area:hover {
103
  border-color: var(--accent-primary);
104
  background-color: #fff;
105
  box-shadow: 0 0 15px var(--accent-primary-glow);
106
  }
107
- #upload-icon svg { width: 48px; height: 48px; color: var(--accent-primary); margin-bottom: 1rem; stroke-width: 1.5; opacity: 0.8; }
108
- #upload-area p { margin: 0; color: var(--text-secondary); font-weight: 500; }
109
-
110
- #file-preview {
111
- position: relative;
112
- animation: fadeIn 0.3s;
113
  }
114
- #preview-image {
115
- display: block;
 
 
 
 
 
 
 
 
 
 
116
  width: 100%;
117
- max-height: 450px;
118
- object-fit: contain;
119
- border-radius: var(--radius-input);
120
- border: 1px solid var(--panel-border);
121
- background-color: var(--input-bg);
122
  }
123
- #remove-preview-btn {
 
124
  position: absolute;
125
- top: 1rem;
126
- left: 1rem;
127
- width: 36px;
128
- height: 36px;
129
- border-radius: 50%;
130
- border: none;
131
  background-color: rgba(26, 32, 44, 0.6);
132
  color: white;
133
- font-size: 24px;
134
- font-weight: bold;
135
- display: flex;
136
- align-items: center;
137
- justify-content: center;
138
  cursor: pointer;
139
- transition: var(--transition-smooth);
140
  line-height: 1;
141
- padding-bottom: 4px; /* Optical alignment */
 
 
142
  }
143
- #remove-preview-btn:hover {
144
  background-color: var(--danger-color);
145
  transform: scale(1.1);
146
  }
 
 
 
 
 
 
 
 
147
 
148
  textarea { width: 100%; padding: 1rem 1.2rem; border-radius: var(--radius-input); border: 1px solid var(--input-border); background-color: var(--input-bg); color: var(--text-primary); box-shadow: var(--shadow-sm) inset; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; transition: var(--transition-smooth); min-height: 90px; resize: vertical; }
149
  textarea:focus { outline: none; border-color: var(--accent-primary); box-shadow: 0 0 0 3px var(--accent-primary-glow), var(--shadow-sm) inset; background-color: var(--panel-bg); }
@@ -152,13 +172,7 @@
152
  #submit-btn:disabled { background: var(--text-tertiary); cursor: not-allowed; box-shadow: none; opacity: 0.7; }
153
  #submit-btn .spinner { width: 20px; height: 20px; border: 3px solid rgba(255, 255, 255, 0.4); border-top-color: #fff; border-radius: 50%; animation: spin 0.8s linear infinite; display: none; }
154
 
155
- #result-container {
156
- min-height: 250px; position: relative;
157
- padding: 1rem; background-color: var(--input-bg);
158
- border-radius: var(--radius-card); border: 2px dashed var(--input-border);
159
- box-shadow: var(--shadow-sm) inset; transition: var(--transition-smooth);
160
- display: flex; align-items: center; justify-content: center;
161
- }
162
  #result-container.loading, #result-container.has-content { border-style: solid; border-color: var(--panel-border); }
163
  #loading-placeholder { display: none; flex-direction: column; align-items: center; gap: 1.5rem; }
164
  #result-container.loading #loading-placeholder { display: flex; animation: fadeIn 0.5s; }
@@ -170,14 +184,11 @@
170
  .orbit:nth-child(1) .satellite { top: -5px; left: 50%; transform: translateX(-50%); }
171
  .orbit:nth-child(2) .satellite { top: 50%; left: -5px; background-color: var(--accent-secondary); transform: translateY(-50%); }
172
  .loading-text { font-weight: 500; color: var(--text-secondary); }
173
-
174
  #result-grid { display: none; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; width: 100%; }
175
  #result-container.has-content #result-grid { display: grid; }
176
  #result-grid img { width: 100%; aspect-ratio: 1/1; object-fit: cover; border-radius: var(--radius-input); cursor: pointer; transition: var(--transition-smooth); box-shadow: var(--shadow-md); border: 1px solid var(--panel-border); }
177
  #result-grid img:hover { transform: scale(1.05); box-shadow: var(--shadow-lg); z-index: 10; position: relative; }
178
  #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
179
-
180
- .gallery-section { margin-top: 3rem; animation-delay: 0.5s; }
181
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
182
  #clear-history-btn { background: none; border: 1px solid var(--panel-border); color: var(--text-secondary); padding: 0.5rem 1rem; border-radius: var(--radius-btn); cursor: pointer; display: none; align-items: center; gap: 0.5rem; font-family: var(--app-font); font-weight: 500; transition: all 0.2s; }
183
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
@@ -193,7 +204,6 @@
193
  .history-delete-btn { position: absolute; top: 1rem; left: 1rem; background: none; border: none; cursor: pointer; padding: 0.5rem; border-radius: 50%; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; color: var(--text-tertiary); transition: var(--transition-smooth); }
194
  .history-delete-btn:hover { background-color: var(--panel-border); color: var(--danger-color); }
195
  .history-delete-btn svg { width: 18px; height: 18px; }
196
-
197
  #lightbox { position: fixed; inset: 0; background-color: rgba(18, 24, 38, 0.6); backdrop-filter: blur(10px) saturate(150%); display: none; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity 0.3s; }
198
  #lightbox.visible { display: flex; opacity: 1; }
199
  #lightbox-content { position: relative; animation: fadeIn 0.3s ease; }
@@ -203,7 +213,7 @@
203
  #lightbox-close { top: -50px; right: 0; font-size: 1.8rem; }
204
  #lightbox-download { top: -50px; left: 0; }
205
  #lightbox-download svg { width: 22px; height: 22px; }
206
- /* --- CHANGE 2: Spinner CSS for download button is back --- */
207
  #lightbox-download .spinner { width: 20px; height: 20px; border: 3px solid rgba(255, 255, 255, 0.4); border-top-color: #fff; border-radius: 50%; animation: spin 0.8s linear infinite; display: none; }
208
 
209
  @media (max-width: 768px) {
@@ -226,17 +236,17 @@
226
  <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>
227
  ۱. تصویر خود را انتخاب کنید
228
  </div>
229
- <div id="upload-container">
230
- <label id="upload-area" for="file-input">
 
 
231
  <div id="upload-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg></div>
232
  <p>فایل تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</p>
233
- </label>
234
- <!-- --- CHANGE 1: HTML for the new preview is simplified --- -->
235
- <div id="file-preview">
236
- <img id="preview-image" src="" alt="Preview">
237
- <button type="button" id="remove-preview-btn" title="حذف تصویر">&times;</button>
238
  </div>
239
- </div>
 
 
 
240
  <input type="file" id="file-input" accept="image/*" hidden>
241
  </div>
242
 
@@ -287,24 +297,23 @@
287
  <div id="lightbox-content">
288
  <img id="lightbox-img" src="">
289
  <button id="lightbox-close" class="lightbox-btn">&times;</button>
290
- <!-- --- CHANGE 2: Spinner is added back for download feedback --- -->
291
- <a id="lightbox-download" title="دانلود تصویر" class="lightbox-btn">
292
  <svg fill="white" viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
293
  <div class="spinner"></div>
294
- </a>
295
  </div>
296
  </div>
297
 
298
  <script>
299
  const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
300
 
301
- const uploadContainer = document.getElementById('upload-container');
302
  const uploadArea = document.getElementById('upload-area');
303
  const fileInput = document.getElementById('file-input');
304
- const filePreview = document.getElementById('file-preview');
305
- const previewImage = document.getElementById('preview-image');
306
- // --- CHANGE 1: Old variables removed, new one added ---
307
- const removePreviewBtn = document.getElementById('remove-preview-btn');
308
  const promptInput = document.getElementById('prompt-input');
309
  const submitBtn = document.getElementById('submit-btn');
310
  const btnText = document.getElementById('btn-text');
@@ -320,16 +329,17 @@
320
  const clearHistoryBtn = document.getElementById('clear-history-btn');
321
 
322
  let uploadedFile = null;
323
- // --- CHANGE 2: Variable to store the current image URL for download ---
324
- let currentLightboxUrl = null;
325
 
326
- // --- Uploader Logic ---
327
  const resetUploader = () => {
328
  uploadedFile = null;
329
  fileInput.value = '';
330
- uploadContainer.classList.remove('has-file');
 
331
  checkFormState();
332
  };
 
333
  const handleFile = (file) => {
334
  if (!file || !file.type.startsWith('image/')) { displayError('لطفا یک فایل تصویری معتبر انتخاب کنید.'); return; }
335
  uploadedFile = file;
@@ -338,18 +348,23 @@
338
  reader.onload = (e) => { previewImage.src = e.target.result; };
339
  reader.readAsDataURL(file);
340
 
341
- uploadContainer.classList.add('has-file');
342
  checkFormState();
343
  clearResult();
344
  };
345
- uploadArea.addEventListener('click', () => fileInput.click());
 
 
 
 
 
 
 
346
  fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
347
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(e => uploadArea.addEventListener(e, p => { p.preventDefault(); p.stopPropagation(); }));
348
- ['dragenter', 'dragover'].forEach(e => uploadArea.addEventListener(e, () => uploadArea.classList.add('drag-over')));
349
  ['dragleave', 'drop'].forEach(e => uploadArea.addEventListener(e, () => uploadArea.classList.remove('drag-over')));
350
- uploadArea.addEventListener('drop', e => handleFile(e.dataTransfer.files[0]));
351
- // --- CHANGE 1: Event listener is now on the new 'X' button ---
352
- removePreviewBtn.addEventListener('click', resetUploader);
353
 
354
  // --- Form & Submission ---
355
  promptInput.addEventListener('input', checkFormState);
@@ -412,57 +427,59 @@
412
  errorMessage.style.display = 'block';
413
  }
414
 
415
- // --- CHANGE 2: Robust download and lightbox logic ---
416
  function openLightbox(url) {
417
- currentLightboxUrl = url;
418
  lightboxImg.src = url;
419
  lightbox.classList.add('visible');
420
  }
421
  function closeLightbox() {
422
  lightbox.classList.remove('visible');
423
- currentLightboxUrl = null;
424
  }
425
  async function handleDownload() {
426
  if (!currentLightboxUrl) return;
427
- const downloadBtn = lightboxDownload;
428
- const spinner = downloadBtn.querySelector('.spinner');
429
- const icon = downloadBtn.querySelector('svg');
430
 
431
  spinner.style.display = 'block';
432
  icon.style.display = 'none';
 
 
433
  try {
434
  // Fetch the image data
435
  const response = await fetch(currentLightboxUrl);
436
- // Convert it to a blob (a file-like object)
 
 
437
  const blob = await response.blob();
438
- // Create a temporary local URL for the blob
439
- const objectUrl = URL.createObjectURL(blob);
440
 
441
- // Create a temporary link element to trigger the download
 
442
  const a = document.createElement('a');
443
  a.href = objectUrl;
444
- a.download = `edited-image-${Date.now()}.png`; // Set a default filename
445
  document.body.appendChild(a);
446
- a.click(); // Programmatically click the link
447
- document.body.removeChild(a); // Clean up by removing the link
448
 
449
- // Release the memory used by the object URL
 
450
  URL.revokeObjectURL(objectUrl);
451
  } catch (e) {
452
  console.error('Download failed:', e);
453
  alert('خطا در دانلود تصویر. لطفا دوباره تلاش کنید.');
454
  } finally {
455
- // Restore the button to its normal state
456
  spinner.style.display = 'none';
457
  icon.style.display = 'block';
 
458
  }
459
  }
460
  lightboxClose.addEventListener('click', closeLightbox);
461
  lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); });
462
  lightboxDownload.addEventListener('click', handleDownload);
463
-
464
-
465
- // --- History Management (No changes here) ---
466
  const getHistory = () => JSON.parse(localStorage.getItem('aiPhotoshopHistory') || '[]');
467
  const saveHistory = (history) => {
468
  localStorage.setItem('aiPhotoshopHistory', JSON.stringify(history));
@@ -474,11 +491,7 @@
474
  if (history.length > 15) history.pop();
475
  saveHistory(history);
476
  };
477
- const handleClearHistory = () => {
478
- if (confirm('آیا از پاک کردن تمام تاریخچه مطمئن هستید؟')) {
479
- saveHistory([]);
480
- }
481
- };
482
  const handleDeleteItem = (index) => {
483
  let history = getHistory();
484
  history.splice(index, 1);
@@ -488,37 +501,20 @@
488
  const history = getHistory();
489
  historyGrid.innerHTML = '';
490
  clearHistoryBtn.style.display = history.length > 0 ? 'flex' : 'none';
491
-
492
  history.forEach((item, index) => {
493
  const card = document.createElement('div');
494
  card.className = 'history-item';
495
- const imagesHTML = item.urls.map(url =>
496
- `<img src="${url}" alt="تصویر از تاریخچه" onclick="openLightbox('${url}')">`
497
- ).join('');
498
-
499
- card.innerHTML = `
500
- <button class="history-delete-btn" data-index="${index}" title="حذف این مورد">
501
- <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>
502
- </button>
503
- <p class="history-item-prompt">${item.prompt}</p>
504
- <div class="history-item-grid">${imagesHTML}</div>
505
- `;
506
  historyGrid.appendChild(card);
507
  });
508
  }
509
  clearHistoryBtn.addEventListener('click', handleClearHistory);
510
  historyGrid.addEventListener('click', (e) => {
511
  const deleteBtn = e.target.closest('.history-delete-btn');
512
- if (deleteBtn) {
513
- const indexToDelete = parseInt(deleteBtn.dataset.index, 10);
514
- handleDeleteItem(indexToDelete);
515
- }
516
- });
517
-
518
- document.addEventListener('DOMContentLoaded', () => {
519
- renderHistory();
520
- checkFormState();
521
  });
 
522
  </script>
523
  </body>
524
  </html>
 
75
 
76
  .form-group { margin-bottom: 2.5rem; }
77
  .form-group:last-child { margin-bottom: 0; }
78
+
79
  .form-label {
80
  display: flex;
81
  align-items: center;
 
87
  }
88
  .form-label svg { width: 24px; height: 24px; color: var(--accent-primary); }
89
 
90
+ /* --- CHANGE 1: Uploader styles completely reworked for single-panel preview --- */
 
 
 
91
  #upload-area {
92
+ position: relative;
93
  border: 2px dashed var(--input-border);
94
  border-radius: var(--radius-input);
95
  padding: 2.5rem;
 
97
  cursor: pointer;
98
  transition: var(--transition-smooth);
99
  background-color: var(--input-bg);
100
+ min-height: 200px;
101
+ display: flex;
102
+ flex-direction: column;
103
+ justify-content: center;
104
+ align-items: center;
105
+ overflow: hidden; /* To keep rounded corners on the image */
106
  }
107
+ #upload-area.drag-over, #upload-area:hover:not(.has-file) {
108
  border-color: var(--accent-primary);
109
  background-color: #fff;
110
  box-shadow: 0 0 15px var(--accent-primary-glow);
111
  }
112
+ #upload-area.has-file {
113
+ border-style: solid;
114
+ border-color: var(--panel-border);
115
+ padding: 0;
116
+ cursor: default;
 
117
  }
118
+
119
+ #upload-content {
120
+ display: flex;
121
+ flex-direction: column;
122
+ align-items: center;
123
+ gap: 1rem;
124
+ }
125
+ #upload-icon svg { width: 48px; height: 48px; color: var(--accent-primary); stroke-width: 1.5; opacity: 0.8; }
126
+ #upload-area p { margin: 0; color: var(--text-secondary); font-weight: 500; }
127
+
128
+ #preview-image-main {
129
+ display: none; /* Hidden by default */
130
  width: 100%;
131
+ height: 100%;
132
+ object-fit: contain; /* Shows the full image as requested */
133
+ position: absolute;
134
+ top: 0;
135
+ left: 0;
136
  }
137
+ #remove-file-btn-main {
138
+ display: none; /* Hidden by default */
139
  position: absolute;
140
+ top: 12px;
141
+ right: 12px;
 
 
 
 
142
  background-color: rgba(26, 32, 44, 0.6);
143
  color: white;
144
+ border: none;
145
+ width: 32px;
146
+ height: 32px;
147
+ border-radius: 50%;
 
148
  cursor: pointer;
149
+ font-size: 1.2rem;
150
  line-height: 1;
151
+ z-index: 10;
152
+ transition: var(--transition-smooth);
153
+ padding: 0;
154
  }
155
+ #remove-file-btn-main:hover {
156
  background-color: var(--danger-color);
157
  transform: scale(1.1);
158
  }
159
+
160
+ /* Logic to show/hide elements based on state */
161
+ #upload-area.has-file #upload-content { display: none; }
162
+ #upload-area.has-file #preview-image-main,
163
+ #upload-area.has-file #remove-file-btn-main {
164
+ display: block;
165
+ }
166
+ /* End of CHANGE 1 */
167
 
168
  textarea { width: 100%; padding: 1rem 1.2rem; border-radius: var(--radius-input); border: 1px solid var(--input-border); background-color: var(--input-bg); color: var(--text-primary); box-shadow: var(--shadow-sm) inset; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; transition: var(--transition-smooth); min-height: 90px; resize: vertical; }
169
  textarea:focus { outline: none; border-color: var(--accent-primary); box-shadow: 0 0 0 3px var(--accent-primary-glow), var(--shadow-sm) inset; background-color: var(--panel-bg); }
 
172
  #submit-btn:disabled { background: var(--text-tertiary); cursor: not-allowed; box-shadow: none; opacity: 0.7; }
173
  #submit-btn .spinner { width: 20px; height: 20px; border: 3px solid rgba(255, 255, 255, 0.4); border-top-color: #fff; border-radius: 50%; animation: spin 0.8s linear infinite; display: none; }
174
 
175
+ #result-container { min-height: 250px; position: relative; padding: 1rem; background-color: var(--input-bg); border-radius: var(--radius-card); border: 2px dashed var(--input-border); box-shadow: var(--shadow-sm) inset; transition: var(--transition-smooth); display: flex; align-items: center; justify-content: center; }
 
 
 
 
 
 
176
  #result-container.loading, #result-container.has-content { border-style: solid; border-color: var(--panel-border); }
177
  #loading-placeholder { display: none; flex-direction: column; align-items: center; gap: 1.5rem; }
178
  #result-container.loading #loading-placeholder { display: flex; animation: fadeIn 0.5s; }
 
184
  .orbit:nth-child(1) .satellite { top: -5px; left: 50%; transform: translateX(-50%); }
185
  .orbit:nth-child(2) .satellite { top: 50%; left: -5px; background-color: var(--accent-secondary); transform: translateY(-50%); }
186
  .loading-text { font-weight: 500; color: var(--text-secondary); }
 
187
  #result-grid { display: none; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; width: 100%; }
188
  #result-container.has-content #result-grid { display: grid; }
189
  #result-grid img { width: 100%; aspect-ratio: 1/1; object-fit: cover; border-radius: var(--radius-input); cursor: pointer; transition: var(--transition-smooth); box-shadow: var(--shadow-md); border: 1px solid var(--panel-border); }
190
  #result-grid img:hover { transform: scale(1.05); box-shadow: var(--shadow-lg); z-index: 10; position: relative; }
191
  #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
 
 
192
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
193
  #clear-history-btn { background: none; border: 1px solid var(--panel-border); color: var(--text-secondary); padding: 0.5rem 1rem; border-radius: var(--radius-btn); cursor: pointer; display: none; align-items: center; gap: 0.5rem; font-family: var(--app-font); font-weight: 500; transition: all 0.2s; }
194
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
 
204
  .history-delete-btn { position: absolute; top: 1rem; left: 1rem; background: none; border: none; cursor: pointer; padding: 0.5rem; border-radius: 50%; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; color: var(--text-tertiary); transition: var(--transition-smooth); }
205
  .history-delete-btn:hover { background-color: var(--panel-border); color: var(--danger-color); }
206
  .history-delete-btn svg { width: 18px; height: 18px; }
 
207
  #lightbox { position: fixed; inset: 0; background-color: rgba(18, 24, 38, 0.6); backdrop-filter: blur(10px) saturate(150%); display: none; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity 0.3s; }
208
  #lightbox.visible { display: flex; opacity: 1; }
209
  #lightbox-content { position: relative; animation: fadeIn 0.3s ease; }
 
213
  #lightbox-close { top: -50px; right: 0; font-size: 1.8rem; }
214
  #lightbox-download { top: -50px; left: 0; }
215
  #lightbox-download svg { width: 22px; height: 22px; }
216
+ /* --- CHANGE 2: Restored spinner for download button --- */
217
  #lightbox-download .spinner { width: 20px; height: 20px; border: 3px solid rgba(255, 255, 255, 0.4); border-top-color: #fff; border-radius: 50%; animation: spin 0.8s linear infinite; display: none; }
218
 
219
  @media (max-width: 768px) {
 
236
  <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>
237
  ۱. تصویر خود را انتخاب کنید
238
  </div>
239
+ <!-- --- CHANGE 1: Reworked HTML for single-panel uploader/preview --- -->
240
+ <label id="upload-area" for="file-input">
241
+ <!-- State 1: Uploader prompt -->
242
+ <div id="upload-content">
243
  <div id="upload-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg></div>
244
  <p>فایل تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</p>
 
 
 
 
 
245
  </div>
246
+ <!-- State 2: Image Preview -->
247
+ <img id="preview-image-main" src="" alt="Preview">
248
+ <button type="button" id="remove-file-btn-main" title="حذف تصویر">&times;</button>
249
+ </label>
250
  <input type="file" id="file-input" accept="image/*" hidden>
251
  </div>
252
 
 
297
  <div id="lightbox-content">
298
  <img id="lightbox-img" src="">
299
  <button id="lightbox-close" class="lightbox-btn">&times;</button>
300
+ <!-- --- CHANGE 2: Download button is now a button, not a link, and will be handled by JS --- -->
301
+ <button id="lightbox-download" title="دانلود تصویر" class="lightbox-btn">
302
  <svg fill="white" viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
303
  <div class="spinner"></div>
304
+ </button>
305
  </div>
306
  </div>
307
 
308
  <script>
309
  const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
310
 
311
+ // --- CHANGE 1: Updated element selectors for the new uploader ---
312
  const uploadArea = document.getElementById('upload-area');
313
  const fileInput = document.getElementById('file-input');
314
+ const previewImage = document.getElementById('preview-image-main');
315
+ const removeFileBtn = document.getElementById('remove-file-btn-main');
316
+
 
317
  const promptInput = document.getElementById('prompt-input');
318
  const submitBtn = document.getElementById('submit-btn');
319
  const btnText = document.getElementById('btn-text');
 
329
  const clearHistoryBtn = document.getElementById('clear-history-btn');
330
 
331
  let uploadedFile = null;
332
+ let currentLightboxUrl = null; // --- CHANGE 2: Variable to store the current image URL for download
 
333
 
334
+ // --- Uploader Logic (Reworked for new structure) ---
335
  const resetUploader = () => {
336
  uploadedFile = null;
337
  fileInput.value = '';
338
+ previewImage.src = '';
339
+ uploadArea.classList.remove('has-file');
340
  checkFormState();
341
  };
342
+
343
  const handleFile = (file) => {
344
  if (!file || !file.type.startsWith('image/')) { displayError('لطفا یک فایل تصویری معتبر انتخاب کنید.'); return; }
345
  uploadedFile = file;
 
348
  reader.onload = (e) => { previewImage.src = e.target.result; };
349
  reader.readAsDataURL(file);
350
 
351
+ uploadArea.classList.add('has-file');
352
  checkFormState();
353
  clearResult();
354
  };
355
+
356
+ // Event listener for the new remove button
357
+ removeFileBtn.addEventListener('click', (e) => {
358
+ e.preventDefault(); // Prevent label from triggering file input
359
+ e.stopPropagation();
360
+ resetUploader();
361
+ });
362
+
363
  fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
364
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(e => uploadArea.addEventListener(e, p => { p.preventDefault(); p.stopPropagation(); }));
365
+ ['dragenter', 'dragover'].forEach(e => uploadArea.addEventListener(e, () => { if (!uploadArea.classList.contains('has-file')) uploadArea.classList.add('drag-over'); }));
366
  ['dragleave', 'drop'].forEach(e => uploadArea.addEventListener(e, () => uploadArea.classList.remove('drag-over')));
367
+ uploadArea.addEventListener('drop', e => { if (!uploadArea.classList.contains('has-file')) handleFile(e.dataTransfer.files[0]); });
 
 
368
 
369
  // --- Form & Submission ---
370
  promptInput.addEventListener('input', checkFormState);
 
427
  errorMessage.style.display = 'block';
428
  }
429
 
430
+ // --- CHANGE 2: Re-implemented robust download and lightbox logic ---
431
  function openLightbox(url) {
432
+ currentLightboxUrl = url; // Store the URL
433
  lightboxImg.src = url;
434
  lightbox.classList.add('visible');
435
  }
436
  function closeLightbox() {
437
  lightbox.classList.remove('visible');
438
+ currentLightboxUrl = null; // Clear the URL
439
  }
440
  async function handleDownload() {
441
  if (!currentLightboxUrl) return;
442
+
443
+ const spinner = lightboxDownload.querySelector('.spinner');
444
+ const icon = lightboxDownload.querySelector('svg');
445
 
446
  spinner.style.display = 'block';
447
  icon.style.display = 'none';
448
+ lightboxDownload.disabled = true;
449
+
450
  try {
451
  // Fetch the image data
452
  const response = await fetch(currentLightboxUrl);
453
+ if (!response.ok) throw new Error('Network response was not ok.');
454
+
455
+ // Convert it to a blob
456
  const blob = await response.blob();
 
 
457
 
458
+ // Create a temporary link to trigger the download
459
+ const objectUrl = URL.createObjectURL(blob);
460
  const a = document.createElement('a');
461
  a.href = objectUrl;
462
+ a.download = `ai-edited-image-${Date.now()}.png`; // Set a filename
463
  document.body.appendChild(a);
464
+ a.click();
 
465
 
466
+ // Clean up
467
+ document.body.removeChild(a);
468
  URL.revokeObjectURL(objectUrl);
469
  } catch (e) {
470
  console.error('Download failed:', e);
471
  alert('خطا در دانلود تصویر. لطفا دوباره تلاش کنید.');
472
  } finally {
 
473
  spinner.style.display = 'none';
474
  icon.style.display = 'block';
475
+ lightboxDownload.disabled = false;
476
  }
477
  }
478
  lightboxClose.addEventListener('click', closeLightbox);
479
  lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); });
480
  lightboxDownload.addEventListener('click', handleDownload);
481
+
482
+ // --- History Management (No changes needed here) ---
 
483
  const getHistory = () => JSON.parse(localStorage.getItem('aiPhotoshopHistory') || '[]');
484
  const saveHistory = (history) => {
485
  localStorage.setItem('aiPhotoshopHistory', JSON.stringify(history));
 
491
  if (history.length > 15) history.pop();
492
  saveHistory(history);
493
  };
494
+ const handleClearHistory = () => { if (confirm('آیا از پاک کردن تمام تاریخچه مطمئن هستید؟')) { saveHistory([]); } };
 
 
 
 
495
  const handleDeleteItem = (index) => {
496
  let history = getHistory();
497
  history.splice(index, 1);
 
501
  const history = getHistory();
502
  historyGrid.innerHTML = '';
503
  clearHistoryBtn.style.display = history.length > 0 ? 'flex' : 'none';
 
504
  history.forEach((item, index) => {
505
  const card = document.createElement('div');
506
  card.className = 'history-item';
507
+ const imagesHTML = item.urls.map(url => `<img src="${url}" alt="تصویر از تاریخچه" onclick="openLightbox('${url}')">`).join('');
508
+ card.innerHTML = `<button class="history-delete-btn" data-index="${index}" title="حذف این مورد"><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></button><p class="history-item-prompt">${item.prompt}</p><div class="history-item-grid">${imagesHTML}</div>`;
 
 
 
 
 
 
 
 
 
509
  historyGrid.appendChild(card);
510
  });
511
  }
512
  clearHistoryBtn.addEventListener('click', handleClearHistory);
513
  historyGrid.addEventListener('click', (e) => {
514
  const deleteBtn = e.target.closest('.history-delete-btn');
515
+ if (deleteBtn) { handleDeleteItem(parseInt(deleteBtn.dataset.index, 10)); }
 
 
 
 
 
 
 
 
516
  });
517
+ document.addEventListener('DOMContentLoaded', () => { renderHistory(); checkFormState(); });
518
  </script>
519
  </body>
520
  </html>