Elias207 commited on
Commit
c72546d
·
verified ·
1 Parent(s): 3e263f8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +27 -77
index.html CHANGED
@@ -22,7 +22,6 @@
22
  --accent-secondary: #0FD4A8;
23
  --danger-color: #e53e3e;
24
  --danger-color-hover: #c53030;
25
- --success-color: #38a169;
26
  --shadow-sm: 0 1px 2px 0 rgba(26, 32, 44, 0.03);
27
  --shadow-md: 0 4px 6px -1px rgba(26, 32, 44, 0.05), 0 2px 4px -2px rgba(26, 32, 44, 0.04);
28
  --shadow-lg: 0 10px 15px -3px rgba(26, 32, 44, 0.06), 0 4px 6px -4px rgba(26, 32, 44, 0.05);
@@ -36,7 +35,6 @@
36
  @keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
37
  @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
38
 
39
- /* --- MODIFICATION START: Shake Animation & Toast Notification --- */
40
  @keyframes shake {
41
  10%, 90% { transform: translate3d(-1px, 0, 0); }
42
  20%, 80% { transform: translate3d(2px, 0, 0); }
@@ -47,44 +45,6 @@
47
  animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
48
  }
49
 
50
- #toast-container {
51
- position: fixed;
52
- bottom: 2rem;
53
- left: 50%;
54
- transform: translateX(-50%);
55
- z-index: 9999;
56
- display: flex;
57
- flex-direction: column-reverse;
58
- gap: 0.75rem;
59
- }
60
- .toast {
61
- padding: 1rem 1.5rem;
62
- border-radius: var(--radius-btn);
63
- color: white;
64
- font-weight: 600;
65
- box-shadow: var(--shadow-xl);
66
- animation: toast-in 0.5s cubic-bezier(0.25, 1, 0.5, 1);
67
- opacity: 0;
68
- }
69
- .toast.show {
70
- opacity: 1;
71
- }
72
- .toast.hide {
73
- animation: toast-out 0.5s forwards cubic-bezier(0.5, 0, 0.75, 0);
74
- }
75
- .toast.error {
76
- background-color: var(--danger-color);
77
- }
78
- @keyframes toast-in {
79
- from { opacity: 0; transform: translateY(30px) scale(0.9); }
80
- to { opacity: 1; transform: translateY(0) scale(1); }
81
- }
82
- @keyframes toast-out {
83
- from { opacity: 1; transform: translateY(0) scale(1); }
84
- to { opacity: 0; transform: translateY(30px) scale(0.9); }
85
- }
86
- /* --- MODIFICATION END --- */
87
-
88
  body {
89
  font-family: var(--app-font);
90
  background-color: var(--app-bg);
@@ -192,8 +152,6 @@
192
  }
193
  #submit-btn svg { width: 24px; height: 24px; margin-left: 4px; filter: drop-shadow(0 0 5px rgba(255,255,255,0.5)); transition: transform 0.3s ease; }
194
 
195
- /* --- MODIFICATION START: Shake Animation & Toast Notification --- */
196
- /* Changed hover to only apply when not in loading state */
197
  #submit-btn:hover:not(.loading) {
198
  background-position: right center;
199
  transform: translateY(-4px);
@@ -205,7 +163,6 @@
205
  #submit-btn.loading {
206
  cursor: wait;
207
  }
208
- /* --- MODIFICATION END --- */
209
 
210
  #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; }
211
 
@@ -217,7 +174,7 @@
217
  .generator-container { position: relative; width: 400px; max-width: 100%; height: 300px; border: 2px solid #38bdf8; border-radius: 20px; overflow: hidden; box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); animation: pulse 5s infinite cubic-bezier(0.4, 0, 0.6, 1); background-color: #161b22; color: #f0f6fc; }
218
  @keyframes pulse { 0% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } 50% { box-shadow: 0 0 60px rgba(56, 189, 248, 0.7); } 100% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } }
219
  .noise-layer, .sketch-layer, .building-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
220
- .noise-layer { background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w.org/2000/svg" width="100" height="100"><rect width="100" height="100" fill="none"/><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.5" numOctaves="4" stitchTiles="stitch"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.6"/></svg>') repeat; opacity: 1; animation: fade-noise 7s infinite ease-in-out; }
221
  @keyframes fade-noise { 0% { opacity: 1; filter: blur(5px); } 30% { opacity: 0.8; filter: blur(2px); } 100% { opacity: 0; filter: blur(0px); } }
222
  .sketch-layer { filter: grayscale(1) contrast(1.5) blur(3px); opacity: 0; animation: reveal-sketch 7s infinite ease-in-out; }
223
  @keyframes reveal-sketch { 0% { opacity: 0; filter: grayscale(1) contrast(1.5) blur(3px); } 20% { opacity: 1; filter: grayscale(1) contrast(1.2) blur(1px); } 60% { opacity: 0.5; filter: grayscale(0.5) contrast(1) blur(0px); } 100% { opacity: 0; } }
@@ -234,7 +191,16 @@
234
  #result-container.has-content #result-grid { display: grid; }
235
  #result-grid img { max-width: 100%; max-height: 400px; width: auto; height: auto; object-fit: contain; border-radius: var(--radius-input); cursor: pointer; transition: var(--transition-smooth); box-shadow: var(--shadow-md); border: 1px solid var(--panel-border); }
236
  #result-grid img:hover { transform: scale(1.05); box-shadow: var(--shadow-lg); z-index: 10; position: relative; }
237
- #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
 
 
 
 
 
 
 
 
 
238
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
239
  #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; }
240
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
@@ -286,7 +252,7 @@
286
  <main>
287
  <div class="form-group">
288
  <div class="form-label">
289
- <svg xmlns="http://www.w.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>
290
  ۱. تصویر خود را انتخاب کنید
291
  </div>
292
  <label id="upload-area" for="file-input">
@@ -305,7 +271,6 @@
305
  ۲. دستور ویرایش را بنویسید
306
  </label>
307
  <textarea id="prompt-input" rows="3" placeholder="مثال: پس‌زمینه را حذف کن و یک ساحل آفتابی قرار بده"></textarea>
308
- <!-- MODIFICATION: Removed 'disabled' attribute -->
309
  <button id="submit-btn">
310
  <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="M10 3L12 8L17 10L12 12L10 17L8 12L3 10L8 8L10 3z"/></svg>
311
  <span id="btn-text">ایجاد کن</span>
@@ -315,7 +280,7 @@
315
  </div>
316
  <div class="form-group">
317
  <div class="form-label">
318
- <svg xmlns="http://www.w.org/2000/svg" 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>
319
  ۳. نتیجه را ببینید
320
  </div>
321
  <div id="result-container">
@@ -377,9 +342,6 @@
377
  </div>
378
  </div>
379
 
380
- <!-- MODIFICATION: Toast Notification Container -->
381
- <div id="toast-container"></div>
382
-
383
  <script>
384
  // --- START OF MODIFIED APPLICATION LOGIC SCRIPT ---
385
  const API_BASE_URL = "https://ginigen-nano-banana-video.hf.space/gradio_api/";
@@ -387,31 +349,12 @@
387
  const uploadArea = document.getElementById('upload-area'); const fileInput = document.getElementById('file-input'); const previewImage = document.getElementById('preview-image-main'); const removeFileBtn = document.getElementById('remove-file-btn-main'); const promptInput = document.getElementById('prompt-input'); const submitBtn = document.getElementById('submit-btn'); const resultContainer = document.getElementById('result-container'); const resultGrid = document.getElementById('result-grid'); const errorMessage = document.getElementById('error-message'); const lightbox = document.getElementById('lightbox'); const lightboxImg = document.getElementById('lightbox-img'); const lightboxClose = document.getElementById('lightbox-close'); const lightboxDownload = document.getElementById('lightbox-download'); const lightboxNext = document.getElementById('lightbox-next'); const historyGrid = document.getElementById('history-grid'); const clearHistoryBtn = document.getElementById('clear-history-btn'); const confirmationModal = document.getElementById('confirmation-modal'); const modalMessageText = document.getElementById('modal-message-text'); const modalConfirmBtn = document.getElementById('modal-confirm-btn'); const modalCancelBtn = document.getElementById('modal-cancel-btn');
388
  let uploadedFile = null; let currentLightboxUrl = null; let currentLightboxGroup = []; let currentLightboxIndex = 0;
389
 
390
- // --- MODIFICATION START: Shake Animation & Toast Notification ---
391
- const showToast = (message, type = 'error') => {
392
- const toastContainer = document.getElementById('toast-container');
393
- const toast = document.createElement('div');
394
- toast.className = `toast ${type}`;
395
- toast.textContent = message;
396
- toastContainer.appendChild(toast);
397
-
398
- // Trigger the animation
399
- setTimeout(() => toast.classList.add('show'), 10);
400
-
401
- // Hide after 3 seconds
402
- setTimeout(() => {
403
- toast.classList.add('hide');
404
- // Remove from DOM after animation finishes
405
- toast.addEventListener('animationend', () => toast.remove());
406
- }, 3000);
407
- };
408
  const triggerButtonShake = () => {
409
  submitBtn.classList.add('shake-error');
410
  setTimeout(() => {
411
  submitBtn.classList.remove('shake-error');
412
- }, 820); // Corresponds to animation duration
413
  };
414
- // --- MODIFICATION END ---
415
 
416
  // --- HELPER FUNCTIONS ---
417
  const convertToPNG = (file) => {
@@ -462,6 +405,7 @@
462
  reader.readAsDataURL(file);
463
  uploadArea.classList.add('has-file');
464
  clearResult();
 
465
  };
466
  const showConfirmationModal = (message, onConfirm) => { modalMessageText.textContent = message; confirmationModal.classList.add('visible'); modalConfirmBtn.onclick = () => { onConfirm(); hideConfirmationModal(); }; modalCancelBtn.onclick = () => { hideConfirmationModal(); }; }; const hideConfirmationModal = () => { confirmationModal.classList.remove('visible'); modalConfirmBtn.onclick = null; modalCancelBtn.onclick = null; }; const resetUploader = () => { uploadedFile = null; fileInput.value = ''; previewImage.src = ''; uploadArea.classList.remove('has-file'); };
467
 
@@ -496,7 +440,6 @@
496
 
497
  const handleClearHistory = () => { showConfirmationModal('آیا از پاک کردن تمام تاریخچه مطمئن هستید؟ این عمل غیرقابل بازگشت است.', () => { saveHistory([]); }); }; const handleDeleteItem = (index) => { showConfirmationModal('آیا از حذف این مورد از تاریخچه مطمئن هستید؟', () => { let history = getHistory(); history.splice(index, 1); saveHistory(history); }); }; const renderHistory = () => { const history = getHistory(); historyGrid.innerHTML = ''; clearHistoryBtn.style.display = history.length > 0 ? 'flex' : 'none'; history.forEach((item, index) => { const card = document.createElement('div'); card.className = 'history-item'; const deleteBtn = document.createElement('button'); deleteBtn.className = 'history-delete-btn'; deleteBtn.title = 'حذف این مورد'; deleteBtn.innerHTML = `<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>`; deleteBtn.onclick = () => handleDeleteItem(index); card.appendChild(deleteBtn); const imageGrid = document.createElement('div'); imageGrid.className = 'history-item-grid'; item.urls.forEach(url => { const img = document.createElement('img'); img.src = url; img.alt = 'تصویر از تاریخچه'; img.addEventListener('click', () => openLightbox(url, item.urls)); imageGrid.appendChild(img); }); card.appendChild(imageGrid); historyGrid.appendChild(card); }); };
498
 
499
- // --- GRADIO API HELPER FUNCTIONS ---
500
  async function uploadFileToGradio(file) {
501
  const formData = new FormData();
502
  formData.append('files', file);
@@ -543,20 +486,27 @@
543
  document.addEventListener('DOMContentLoaded', renderHistory);
544
  removeFileBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); resetUploader(); }); fileInput.addEventListener('change', (e) => handleFile(e.target.files[0])); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(e => { uploadArea.addEventListener(e, p => { p.preventDefault(); p.stopPropagation(); }); }); ['dragenter', 'dragover'].forEach(e => { uploadArea.addEventListener(e, () => { if (!uploadArea.classList.contains('has-file')) uploadArea.classList.add('drag-over'); }); }); ['dragleave', 'drop'].forEach(e => { uploadArea.addEventListener(e, () => uploadArea.classList.remove('drag-over')); }); uploadArea.addEventListener('drop', e => { if (!uploadArea.classList.contains('has-file')) handleFile(e.dataTransfer.files[0]); });
545
 
546
- // --- MODIFIED SUBMIT BUTTON LOGIC ---
 
 
 
 
 
 
547
  submitBtn.addEventListener('click', async () => {
548
- // Prevent multiple submissions while loading
549
  if (submitBtn.classList.contains('loading')) return;
550
 
551
- // --- MODIFICATION: Validation Check ---
552
  if (!uploadedFile) {
553
  triggerButtonShake();
554
- showToast('لطفا ابتدا یک تصویر انتخاب کنید.');
 
555
  return;
556
  }
557
  if (promptInput.value.trim() === '') {
558
  triggerButtonShake();
559
- showToast('لطفا دستور ویرایش را بنویسید.');
 
560
  return;
561
  }
562
 
 
22
  --accent-secondary: #0FD4A8;
23
  --danger-color: #e53e3e;
24
  --danger-color-hover: #c53030;
 
25
  --shadow-sm: 0 1px 2px 0 rgba(26, 32, 44, 0.03);
26
  --shadow-md: 0 4px 6px -1px rgba(26, 32, 44, 0.05), 0 2px 4px -2px rgba(26, 32, 44, 0.04);
27
  --shadow-lg: 0 10px 15px -3px rgba(26, 32, 44, 0.06), 0 4px 6px -4px rgba(26, 32, 44, 0.05);
 
35
  @keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
36
  @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
37
 
 
38
  @keyframes shake {
39
  10%, 90% { transform: translate3d(-1px, 0, 0); }
40
  20%, 80% { transform: translate3d(2px, 0, 0); }
 
45
  animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
46
  }
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  body {
49
  font-family: var(--app-font);
50
  background-color: var(--app-bg);
 
152
  }
153
  #submit-btn svg { width: 24px; height: 24px; margin-left: 4px; filter: drop-shadow(0 0 5px rgba(255,255,255,0.5)); transition: transform 0.3s ease; }
154
 
 
 
155
  #submit-btn:hover:not(.loading) {
156
  background-position: right center;
157
  transform: translateY(-4px);
 
163
  #submit-btn.loading {
164
  cursor: wait;
165
  }
 
166
 
167
  #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; }
168
 
 
174
  .generator-container { position: relative; width: 400px; max-width: 100%; height: 300px; border: 2px solid #38bdf8; border-radius: 20px; overflow: hidden; box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); animation: pulse 5s infinite cubic-bezier(0.4, 0, 0.6, 1); background-color: #161b22; color: #f0f6fc; }
175
  @keyframes pulse { 0% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } 50% { box-shadow: 0 0 60px rgba(56, 189, 248, 0.7); } 100% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } }
176
  .noise-layer, .sketch-layer, .building-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
177
+ .noise-layer { background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><rect width="100" height="100" fill="none"/><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.5" numOctaves="4" stitchTiles="stitch"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.6"/></svg>') repeat; opacity: 1; animation: fade-noise 7s infinite ease-in-out; }
178
  @keyframes fade-noise { 0% { opacity: 1; filter: blur(5px); } 30% { opacity: 0.8; filter: blur(2px); } 100% { opacity: 0; filter: blur(0px); } }
179
  .sketch-layer { filter: grayscale(1) contrast(1.5) blur(3px); opacity: 0; animation: reveal-sketch 7s infinite ease-in-out; }
180
  @keyframes reveal-sketch { 0% { opacity: 0; filter: grayscale(1) contrast(1.5) blur(3px); } 20% { opacity: 1; filter: grayscale(1) contrast(1.2) blur(1px); } 60% { opacity: 0.5; filter: grayscale(0.5) contrast(1) blur(0px); } 100% { opacity: 0; } }
 
191
  #result-container.has-content #result-grid { display: grid; }
192
  #result-grid img { max-width: 100%; max-height: 400px; width: auto; height: auto; object-fit: contain; border-radius: var(--radius-input); cursor: pointer; transition: var(--transition-smooth); box-shadow: var(--shadow-md); border: 1px solid var(--panel-border); }
193
  #result-grid img:hover { transform: scale(1.05); box-shadow: var(--shadow-lg); z-index: 10; position: relative; }
194
+
195
+ /* --- MODIFICATION: Updated error message style for validation --- */
196
+ #error-message {
197
+ color: var(--danger-color); /* Kept red for error indication */
198
+ text-align: center;
199
+ margin-top: 1rem;
200
+ display: none;
201
+ font-weight: 500;
202
+ }
203
+
204
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
205
  #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; }
206
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
 
252
  <main>
253
  <div class="form-group">
254
  <div class="form-label">
255
+ <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>
256
  ۱. تصویر خود را انتخاب کنید
257
  </div>
258
  <label id="upload-area" for="file-input">
 
271
  ۲. دستور ویرایش را بنویسید
272
  </label>
273
  <textarea id="prompt-input" rows="3" placeholder="مثال: پس‌زمینه را حذف کن و یک ساحل آفتابی قرار بده"></textarea>
 
274
  <button id="submit-btn">
275
  <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="M10 3L12 8L17 10L12 12L10 17L8 12L3 10L8 8L10 3z"/></svg>
276
  <span id="btn-text">ایجاد کن</span>
 
280
  </div>
281
  <div class="form-group">
282
  <div class="form-label">
283
+ <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="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>
284
  ۳. نتیجه را ببینید
285
  </div>
286
  <div id="result-container">
 
342
  </div>
343
  </div>
344
 
 
 
 
345
  <script>
346
  // --- START OF MODIFIED APPLICATION LOGIC SCRIPT ---
347
  const API_BASE_URL = "https://ginigen-nano-banana-video.hf.space/gradio_api/";
 
349
  const uploadArea = document.getElementById('upload-area'); const fileInput = document.getElementById('file-input'); const previewImage = document.getElementById('preview-image-main'); const removeFileBtn = document.getElementById('remove-file-btn-main'); const promptInput = document.getElementById('prompt-input'); const submitBtn = document.getElementById('submit-btn'); const resultContainer = document.getElementById('result-container'); const resultGrid = document.getElementById('result-grid'); const errorMessage = document.getElementById('error-message'); const lightbox = document.getElementById('lightbox'); const lightboxImg = document.getElementById('lightbox-img'); const lightboxClose = document.getElementById('lightbox-close'); const lightboxDownload = document.getElementById('lightbox-download'); const lightboxNext = document.getElementById('lightbox-next'); const historyGrid = document.getElementById('history-grid'); const clearHistoryBtn = document.getElementById('clear-history-btn'); const confirmationModal = document.getElementById('confirmation-modal'); const modalMessageText = document.getElementById('modal-message-text'); const modalConfirmBtn = document.getElementById('modal-confirm-btn'); const modalCancelBtn = document.getElementById('modal-cancel-btn');
350
  let uploadedFile = null; let currentLightboxUrl = null; let currentLightboxGroup = []; let currentLightboxIndex = 0;
351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  const triggerButtonShake = () => {
353
  submitBtn.classList.add('shake-error');
354
  setTimeout(() => {
355
  submitBtn.classList.remove('shake-error');
356
+ }, 820);
357
  };
 
358
 
359
  // --- HELPER FUNCTIONS ---
360
  const convertToPNG = (file) => {
 
405
  reader.readAsDataURL(file);
406
  uploadArea.classList.add('has-file');
407
  clearResult();
408
+ errorMessage.style.display = 'none'; // Hide error message on file upload
409
  };
410
  const showConfirmationModal = (message, onConfirm) => { modalMessageText.textContent = message; confirmationModal.classList.add('visible'); modalConfirmBtn.onclick = () => { onConfirm(); hideConfirmationModal(); }; modalCancelBtn.onclick = () => { hideConfirmationModal(); }; }; const hideConfirmationModal = () => { confirmationModal.classList.remove('visible'); modalConfirmBtn.onclick = null; modalCancelBtn.onclick = null; }; const resetUploader = () => { uploadedFile = null; fileInput.value = ''; previewImage.src = ''; uploadArea.classList.remove('has-file'); };
411
 
 
440
 
441
  const handleClearHistory = () => { showConfirmationModal('آیا از پاک کردن تمام تاریخچه مطمئن هستید؟ این عمل غیرقابل بازگشت است.', () => { saveHistory([]); }); }; const handleDeleteItem = (index) => { showConfirmationModal('آیا از حذف این مورد از تاریخچه مطمئن هستید؟', () => { let history = getHistory(); history.splice(index, 1); saveHistory(history); }); }; const renderHistory = () => { const history = getHistory(); historyGrid.innerHTML = ''; clearHistoryBtn.style.display = history.length > 0 ? 'flex' : 'none'; history.forEach((item, index) => { const card = document.createElement('div'); card.className = 'history-item'; const deleteBtn = document.createElement('button'); deleteBtn.className = 'history-delete-btn'; deleteBtn.title = 'حذف این مورد'; deleteBtn.innerHTML = `<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>`; deleteBtn.onclick = () => handleDeleteItem(index); card.appendChild(deleteBtn); const imageGrid = document.createElement('div'); imageGrid.className = 'history-item-grid'; item.urls.forEach(url => { const img = document.createElement('img'); img.src = url; img.alt = 'تصویر از تاریخچه'; img.addEventListener('click', () => openLightbox(url, item.urls)); imageGrid.appendChild(img); }); card.appendChild(imageGrid); historyGrid.appendChild(card); }); };
442
 
 
443
  async function uploadFileToGradio(file) {
444
  const formData = new FormData();
445
  formData.append('files', file);
 
486
  document.addEventListener('DOMContentLoaded', renderHistory);
487
  removeFileBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); resetUploader(); }); fileInput.addEventListener('change', (e) => handleFile(e.target.files[0])); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(e => { uploadArea.addEventListener(e, p => { p.preventDefault(); p.stopPropagation(); }); }); ['dragenter', 'dragover'].forEach(e => { uploadArea.addEventListener(e, () => { if (!uploadArea.classList.contains('has-file')) uploadArea.classList.add('drag-over'); }); }); ['dragleave', 'drop'].forEach(e => { uploadArea.addEventListener(e, () => uploadArea.classList.remove('drag-over')); }); uploadArea.addEventListener('drop', e => { if (!uploadArea.classList.contains('has-file')) handleFile(e.dataTransfer.files[0]); });
488
 
489
+ // --- MODIFICATION: Hide error message on input ---
490
+ promptInput.addEventListener('input', () => {
491
+ if (errorMessage.style.display === 'block') {
492
+ errorMessage.style.display = 'none';
493
+ }
494
+ });
495
+
496
  submitBtn.addEventListener('click', async () => {
 
497
  if (submitBtn.classList.contains('loading')) return;
498
 
499
+ // --- MODIFICATION: Validation Check using the <p> tag ---
500
  if (!uploadedFile) {
501
  triggerButtonShake();
502
+ errorMessage.textContent = 'لطفا ابتدا یک تصویر انتخاب کنید.';
503
+ errorMessage.style.display = 'block';
504
  return;
505
  }
506
  if (promptInput.value.trim() === '') {
507
  triggerButtonShake();
508
+ errorMessage.textContent = 'لطفا دستور ویرایش را بنویسید.';
509
+ errorMessage.style.display = 'block';
510
  return;
511
  }
512