Elias207 commited on
Commit
bc75202
·
verified ·
1 Parent(s): 760160a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +69 -14
index.html CHANGED
@@ -126,17 +126,34 @@
126
 
127
  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; }
128
  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); }
 
 
129
  #submit-btn {
130
  display: flex; align-items: center; justify-content: center; gap: 0.75rem; width: 100%; padding: 1.1rem;
131
- font-size: 1.2rem; font-weight: 700; background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%);
132
- color: #fff; border: none; border-radius: var(--radius-btn); cursor: pointer; transition: all 0.3s ease;
133
- box-shadow: 0 6px 12px -3px var(--accent-primary-glow), 0 6px 12px -3px var(--accent-secondary-glow); margin-top: 1.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  }
135
- #submit-btn svg { width: 24px; height: 24px; margin-left: 4px; filter: drop-shadow(0 0 5px rgba(255,255,255,0.5)); }
136
- #submit-btn:hover:not(:disabled) { transform: translateY(-5px) scale(1.02); box-shadow: 0 8px 20px -4px var(--accent-primary-glow), 0 8px 20px -4px var(--accent-secondary-glow); }
137
  #submit-btn:disabled { background: var(--text-tertiary); cursor: not-allowed; box-shadow: none; opacity: 0.7; }
138
  #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; }
139
-
 
140
  #result-container { min-height: 350px; 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; }
141
  #result-container.loading, #result-container.has-content { border-style: solid; border-color: var(--panel-border); }
142
  #loading-placeholder { display: none; }
@@ -252,10 +269,6 @@
252
  background: linear-gradient(to right, #38bdf8, #bb86fc, #facc15);
253
  animation: progress 7s infinite linear;
254
  }
255
- @keyframes progress {
256
- 0% { width: 0%; }
257
- 100% { width: 100%; }
258
- }
259
 
260
  #result-grid { display: none; grid-template-columns: 1fr; /* Changed for single result */ gap: 1.5rem; width: 100%; justify-items: center; }
261
  #result-container.has-content #result-grid { display: grid; }
@@ -446,7 +459,7 @@
446
  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');
447
  let uploadedFile = null; let currentLightboxUrl = null; let currentLightboxGroup = []; let currentLightboxIndex = 0;
448
 
449
- // --- ORIGINAL HELPER FUNCTIONS (UNCHANGED) ---
450
  const convertToPNG = (file) => {
451
  return new Promise((resolve, reject) => {
452
  const image = new Image();
@@ -497,9 +510,51 @@
497
  checkFormState();
498
  clearResult();
499
  };
500
- 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'); checkFormState(); }; const checkFormState = () => { submitBtn.disabled = !uploadedFile || promptInput.value.trim() === ''; }; const setLoading = (isLoading) => { const btnSpinner = submitBtn.querySelector('.spinner'); const btnText = document.getElementById('btn-text'); const btnIcon = submitBtn.querySelector('svg'); if (isLoading) { resultContainer.classList.remove('has-content'); resultContainer.classList.add('loading'); btnSpinner.style.display = 'inline-block'; btnIcon.style.display = 'none'; btnText.textContent = 'در حال پردازش...'; submitBtn.disabled = true; resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; } else { resultContainer.classList.remove('loading'); btnSpinner.style.display = 'none'; btnIcon.style.display = 'inline-block'; btnText.textContent = 'ایجاد کن'; checkFormState(); } }; const displayResult = (imageUrls) => { resultGrid.innerHTML = ''; imageUrls.forEach((url) => { const img = document.createElement('img'); img.src = url; img.alt = 'تصویر ویرایش شده'; img.addEventListener('click', () => openLightbox(url, imageUrls)); resultGrid.appendChild(img); }); resultContainer.classList.add('has-content'); }; const clearResult = () => { resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; resultContainer.classList.remove('has-content'); }; const displayError = (message) => { errorMessage.textContent = message; errorMessage.style.display = 'block'; }; const updateLightboxImage = () => { const newUrl = currentLightboxGroup[currentLightboxIndex]; lightboxImg.src = newUrl; currentLightboxUrl = newUrl; }; const openLightbox = (clickedUrl, urlGroup) => { currentLightboxGroup = urlGroup; currentLightboxIndex = urlGroup.indexOf(clickedUrl); updateLightboxImage(); lightboxNext.style.display = urlGroup.length > 1 ? 'flex' : 'none'; lightbox.classList.add('visible'); }; const closeLightbox = () => { lightbox.classList.remove('visible'); currentLightboxGroup = []; }; const handleDownload = async () => { if (!currentLightboxUrl) return; const spinner = lightboxDownload.querySelector('.spinner'); const icon = lightboxDownload.querySelector('svg'); spinner.style.display = 'block'; icon.style.display = 'none'; lightboxDownload.disabled = true; try { const response = await fetch(currentLightboxUrl); if (!response.ok) throw new Error('Network response was not ok.'); const blob = await response.blob(); const objectUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = objectUrl; a.download = `ai-edited-image-${Date.now()}.png`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(objectUrl); } catch (e) { console.error('Download failed:', e); alert('خطا در دانلود تصویر. لطفا دوباره تلاش کنید.'); } finally { spinner.style.display = 'none'; icon.style.display = 'block'; lightboxDownload.disabled = false; } }; const getHistory = () => JSON.parse(localStorage.getItem('aiPhotoshopHistory') || '[]'); const saveHistory = (history) => { localStorage.setItem('aiPhotoshopHistory', JSON.stringify(history)); renderHistory(); }; const addToHistory = (item) => { let history = getHistory(); history.unshift(item); if (history.length > 15) history.pop(); saveHistory(history); }; 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); }); };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
 
502
- // --- NEW GRADIO API HELPER FUNCTIONS ---
503
  async function uploadFileToGradio(file) {
504
  const formData = new FormData();
505
  formData.append('files', file);
@@ -586,7 +641,7 @@
586
  document.addEventListener('DOMContentLoaded', () => { renderHistory(); checkFormState(); });
587
  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]); }); promptInput.addEventListener('input', checkFormState);
588
 
589
- // --- MODIFIED SUBMIT BUTTON LOGIC ---
590
  submitBtn.addEventListener('click', async () => {
591
  if (submitBtn.disabled) return;
592
  setLoading(true);
 
126
 
127
  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; }
128
  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); }
129
+
130
+ /* --- MODIFICATION START: Improved Button Style --- */
131
  #submit-btn {
132
  display: flex; align-items: center; justify-content: center; gap: 0.75rem; width: 100%; padding: 1.1rem;
133
+ font-size: 1.2rem; font-weight: 700;
134
+ background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 50%, var(--accent-secondary) 100%);
135
+ background-size: 200% auto;
136
+ color: #fff; border: none; border-radius: var(--radius-btn); cursor: pointer;
137
+ transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
138
+ box-shadow: 0 6px 12px -3px var(--accent-primary-glow), 0 6px 12px -3px var(--accent-secondary-glow);
139
+ margin-top: 1.5rem;
140
+ position: relative;
141
+ z-index: 1;
142
+ overflow: hidden;
143
+ }
144
+ #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; }
145
+ #submit-btn:hover:not(:disabled) {
146
+ background-position: right center;
147
+ transform: translateY(-4px);
148
+ box-shadow: 0 10px 20px -4px var(--accent-primary-glow), 0 10px 20px -4px var(--accent-secondary-glow);
149
+ }
150
+ #submit-btn:hover:not(:disabled) svg {
151
+ transform: scale(1.1) rotate(-15deg);
152
  }
 
 
153
  #submit-btn:disabled { background: var(--text-tertiary); cursor: not-allowed; box-shadow: none; opacity: 0.7; }
154
  #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; }
155
+ /* --- MODIFICATION END --- */
156
+
157
  #result-container { min-height: 350px; 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; }
158
  #result-container.loading, #result-container.has-content { border-style: solid; border-color: var(--panel-border); }
159
  #loading-placeholder { display: none; }
 
269
  background: linear-gradient(to right, #38bdf8, #bb86fc, #facc15);
270
  animation: progress 7s infinite linear;
271
  }
 
 
 
 
272
 
273
  #result-grid { display: none; grid-template-columns: 1fr; /* Changed for single result */ gap: 1.5rem; width: 100%; justify-items: center; }
274
  #result-container.has-content #result-grid { display: grid; }
 
459
  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');
460
  let uploadedFile = null; let currentLightboxUrl = null; let currentLightboxGroup = []; let currentLightboxIndex = 0;
461
 
462
+ // --- HELPER FUNCTIONS ---
463
  const convertToPNG = (file) => {
464
  return new Promise((resolve, reject) => {
465
  const image = new Image();
 
510
  checkFormState();
511
  clearResult();
512
  };
513
+ 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'); checkFormState(); }; const checkFormState = () => { submitBtn.disabled = !uploadedFile || promptInput.value.trim() === ''; }; const setLoading = (isLoading) => { const btnSpinner = submitBtn.querySelector('.spinner'); const btnText = document.getElementById('btn-text'); const btnIcon = submitBtn.querySelector('svg'); if (isLoading) { resultContainer.classList.remove('has-content'); resultContainer.classList.add('loading'); btnSpinner.style.display = 'inline-block'; btnIcon.style.display = 'none'; btnText.textContent = 'در حال پردازش...'; submitBtn.disabled = true; resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; } else { resultContainer.classList.remove('loading'); btnSpinner.style.display = 'none'; btnIcon.style.display = 'inline-block'; btnText.textContent = 'ایجاد کن'; checkFormState(); } }; const displayResult = (imageUrls) => { resultGrid.innerHTML = ''; imageUrls.forEach((url) => { const img = document.createElement('img'); img.src = url; img.alt = 'تصویر ویرایش شده'; img.addEventListener('click', () => openLightbox(url, imageUrls)); resultGrid.appendChild(img); }); resultContainer.classList.add('has-content'); }; const clearResult = () => { resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; resultContainer.classList.remove('has-content'); }; const displayError = (message) => { errorMessage.textContent = message; errorMessage.style.display = 'block'; }; const updateLightboxImage = () => { const newUrl = currentLightboxGroup[currentLightboxIndex]; lightboxImg.src = newUrl; currentLightboxUrl = newUrl; }; const openLightbox = (clickedUrl, urlGroup) => { currentLightboxGroup = urlGroup; currentLightboxIndex = urlGroup.indexOf(clickedUrl); updateLightboxImage(); lightboxNext.style.display = urlGroup.length > 1 ? 'flex' : 'none'; lightbox.classList.add('visible'); }; const closeLightbox = () => { lightbox.classList.remove('visible'); currentLightboxGroup = []; }; const handleDownload = async () => { if (!currentLightboxUrl) return; const spinner = lightboxDownload.querySelector('.spinner'); const icon = lightboxDownload.querySelector('svg'); spinner.style.display = 'block'; icon.style.display = 'none'; lightboxDownload.disabled = true; try { const response = await fetch(currentLightboxUrl); if (!response.ok) throw new Error('Network response was not ok.'); const blob = await response.blob(); const objectUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = objectUrl; a.download = `ai-edited-image-${Date.now()}.png`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(objectUrl); } catch (e) { console.error('Download failed:', e); alert('خطا در دانلود تصویر. لطفا دوباره تلاش کنید.'); } finally { spinner.style.display = 'none'; icon.style.display = 'block'; lightboxDownload.disabled = false; } };
514
+
515
+ // --- MODIFICATION START: History Management with Expiration ---
516
+ const getHistory = () => {
517
+ const historyFromStorage = JSON.parse(localStorage.getItem('aiPhotoshopHistory') || '[]');
518
+ if (!Array.isArray(historyFromStorage)) return [];
519
+
520
+ const FIVE_DAYS_IN_MS = 5 * 24 * 60 * 60 * 1000;
521
+ const now = Date.now();
522
+
523
+ // Filter out items older than 5 days
524
+ const freshHistory = historyFromStorage.filter(item => {
525
+ // For backward compatibility, if 'createdAt' timestamp doesn't exist, we keep the item.
526
+ // This prevents deleting old history items that were created before this feature was added.
527
+ if (typeof item.createdAt !== 'number') {
528
+ return true;
529
+ }
530
+ return (now - item.createdAt) < FIVE_DAYS_IN_MS;
531
+ });
532
+
533
+ // If the filtering process removed any items, we update localStorage to clean it up.
534
+ if (freshHistory.length < historyFromStorage.length) {
535
+ localStorage.setItem('aiPhotoshopHistory', JSON.stringify(freshHistory));
536
+ }
537
+
538
+ return freshHistory;
539
+ };
540
+
541
+ const saveHistory = (history) => { localStorage.setItem('aiPhotoshopHistory', JSON.stringify(history)); renderHistory(); };
542
+
543
+ const addToHistory = (item) => {
544
+ let history = getHistory(); // getHistory now filters expired items
545
+ const newItem = {
546
+ ...item,
547
+ createdAt: Date.now() // Add timestamp when creating a history item
548
+ };
549
+ history.unshift(newItem);
550
+ if (history.length > 15) history.pop();
551
+ saveHistory(history);
552
+ };
553
+ // --- MODIFICATION END ---
554
+
555
+ 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); }); };
556
 
557
+ // --- GRADIO API HELPER FUNCTIONS ---
558
  async function uploadFileToGradio(file) {
559
  const formData = new FormData();
560
  formData.append('files', file);
 
641
  document.addEventListener('DOMContentLoaded', () => { renderHistory(); checkFormState(); });
642
  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]); }); promptInput.addEventListener('input', checkFormState);
643
 
644
+ // --- SUBMIT BUTTON LOGIC ---
645
  submitBtn.addEventListener('click', async () => {
646
  if (submitBtn.disabled) return;
647
  setLoading(true);