Spaces:
Running
Running
Update index.html
Browse files- index.html +93 -25
index.html
CHANGED
|
@@ -34,6 +34,18 @@
|
|
| 34 |
|
| 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 |
body {
|
| 39 |
font-family: var(--app-font);
|
|
@@ -127,7 +139,6 @@
|
|
| 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;
|
|
@@ -150,10 +161,17 @@
|
|
| 150 |
#submit-btn:hover:not(:disabled) svg {
|
| 151 |
transform: scale(1.1) rotate(-15deg);
|
| 152 |
}
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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; }
|
|
@@ -344,6 +362,30 @@
|
|
| 344 |
.modal-btn.cancel-btn { background-color: var(--input-bg); color: var(--text-secondary); border: 1px solid var(--input-border); }
|
| 345 |
.modal-btn.cancel-btn:hover { background-color: var(--panel-border); color: var(--text-primary); }
|
| 346 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
@media (max-width: 768px) {
|
| 348 |
main, .gallery-section { padding: 1.5rem; } h1 { font-size: 2.2rem; } .gallery-section { padding: 1rem; }
|
| 349 |
#history-grid { gap: 0.75rem; } .history-item { padding: 0.75rem; }
|
|
@@ -452,6 +494,10 @@
|
|
| 452 |
</div>
|
| 453 |
</div>
|
| 454 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
<script>
|
| 456 |
// --- START OF MODIFIED APPLICATION LOGIC SCRIPT ---
|
| 457 |
const API_BASE_URL = "https://ginigen-nano-banana-video.hf.space/gradio_api/";
|
|
@@ -512,45 +558,47 @@
|
|
| 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:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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();
|
| 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 |
|
|
@@ -614,7 +662,7 @@
|
|
| 614 |
}
|
| 615 |
|
| 616 |
if (resultUrl) {
|
| 617 |
-
displayResult([resultUrl]);
|
| 618 |
addToHistory({ prompt: promptInput.value.trim(), urls: [resultUrl] });
|
| 619 |
} else {
|
| 620 |
throw new Error('نتیجه دریافت شد اما لینک تصویر معتبر نبود.');
|
|
@@ -641,11 +689,30 @@
|
|
| 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 |
-
// ---
|
| 645 |
submitBtn.addEventListener('click', async () => {
|
| 646 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 647 |
setLoading(true);
|
| 648 |
-
|
| 649 |
try {
|
| 650 |
const sessionHash = Math.random().toString(36).substring(7);
|
| 651 |
const textOverlay = document.querySelector('.text-overlay');
|
|
@@ -663,9 +730,10 @@
|
|
| 663 |
} catch (error) {
|
| 664 |
console.error('An error occurred:', error);
|
| 665 |
displayError(error.message);
|
| 666 |
-
setLoading(false);
|
| 667 |
}
|
| 668 |
});
|
|
|
|
| 669 |
|
| 670 |
lightboxClose.addEventListener('click', closeLightbox); lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); }); lightboxDownload.addEventListener('click', handleDownload); lightboxNext.addEventListener('click', () => { currentLightboxIndex = (currentLightboxIndex + 1) % currentLightboxGroup.length; updateLightboxImage(); }); clearHistoryBtn.addEventListener('click', handleClearHistory);
|
| 671 |
</script>
|
|
|
|
| 34 |
|
| 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 |
+
/* --- MODIFICATION START: Shake Animation --- */
|
| 39 |
+
@keyframes shake-error {
|
| 40 |
+
10%, 90% { transform: translateX(-2px); }
|
| 41 |
+
20%, 80% { transform: translateX(4px); }
|
| 42 |
+
30%, 50%, 70% { transform: translateX(-6px); }
|
| 43 |
+
40%, 60% { transform: translateX(6px); }
|
| 44 |
+
}
|
| 45 |
+
.shake-error {
|
| 46 |
+
animation: shake-error 0.7s cubic-bezier(.36,.07,.19,.97) both;
|
| 47 |
+
}
|
| 48 |
+
/* --- MODIFICATION END --- */
|
| 49 |
|
| 50 |
body {
|
| 51 |
font-family: var(--app-font);
|
|
|
|
| 139 |
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; }
|
| 140 |
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); }
|
| 141 |
|
|
|
|
| 142 |
#submit-btn {
|
| 143 |
display: flex; align-items: center; justify-content: center; gap: 0.75rem; width: 100%; padding: 1.1rem;
|
| 144 |
font-size: 1.2rem; font-weight: 700;
|
|
|
|
| 161 |
#submit-btn:hover:not(:disabled) svg {
|
| 162 |
transform: scale(1.1) rotate(-15deg);
|
| 163 |
}
|
| 164 |
+
|
| 165 |
+
/* --- MODIFICATION START: Disabled Button Style --- */
|
| 166 |
+
#submit-btn:disabled {
|
| 167 |
+
cursor: not-allowed;
|
| 168 |
+
box-shadow: none;
|
| 169 |
+
opacity: 0.65;
|
| 170 |
+
}
|
| 171 |
/* --- MODIFICATION END --- */
|
| 172 |
|
| 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: 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; }
|
| 176 |
#result-container.loading, #result-container.has-content { border-style: solid; border-color: var(--panel-border); }
|
| 177 |
#loading-placeholder { display: none; }
|
|
|
|
| 362 |
.modal-btn.cancel-btn { background-color: var(--input-bg); color: var(--text-secondary); border: 1px solid var(--input-border); }
|
| 363 |
.modal-btn.cancel-btn:hover { background-color: var(--panel-border); color: var(--text-primary); }
|
| 364 |
|
| 365 |
+
/* --- MODIFICATION START: Toast Notification Style --- */
|
| 366 |
+
#toast-notification {
|
| 367 |
+
position: fixed;
|
| 368 |
+
bottom: 20px;
|
| 369 |
+
left: 50%;
|
| 370 |
+
transform: translate(-50%, 20px);
|
| 371 |
+
background-color: var(--danger-color);
|
| 372 |
+
color: white;
|
| 373 |
+
padding: 1rem 1.5rem;
|
| 374 |
+
border-radius: var(--radius-btn);
|
| 375 |
+
box-shadow: var(--shadow-lg);
|
| 376 |
+
font-weight: 600;
|
| 377 |
+
z-index: 3000;
|
| 378 |
+
opacity: 0;
|
| 379 |
+
transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
| 380 |
+
pointer-events: none;
|
| 381 |
+
}
|
| 382 |
+
#toast-notification.visible {
|
| 383 |
+
opacity: 1;
|
| 384 |
+
transform: translate(-50%, 0);
|
| 385 |
+
pointer-events: auto;
|
| 386 |
+
}
|
| 387 |
+
/* --- MODIFICATION END --- */
|
| 388 |
+
|
| 389 |
@media (max-width: 768px) {
|
| 390 |
main, .gallery-section { padding: 1.5rem; } h1 { font-size: 2.2rem; } .gallery-section { padding: 1rem; }
|
| 391 |
#history-grid { gap: 0.75rem; } .history-item { padding: 0.75rem; }
|
|
|
|
| 494 |
</div>
|
| 495 |
</div>
|
| 496 |
|
| 497 |
+
<!-- MODIFICATION START: Toast Notification HTML -->
|
| 498 |
+
<div id="toast-notification"></div>
|
| 499 |
+
<!-- MODIFICATION END -->
|
| 500 |
+
|
| 501 |
<script>
|
| 502 |
// --- START OF MODIFIED APPLICATION LOGIC SCRIPT ---
|
| 503 |
const API_BASE_URL = "https://ginigen-nano-banana-video.hf.space/gradio_api/";
|
|
|
|
| 558 |
};
|
| 559 |
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; } };
|
| 560 |
|
| 561 |
+
// --- MODIFICATION START: Toast Notification Logic ---
|
| 562 |
+
let toastTimeout;
|
| 563 |
+
const showToast = (message) => {
|
| 564 |
+
const toast = document.getElementById('toast-notification');
|
| 565 |
+
if (!toast) return;
|
| 566 |
+
|
| 567 |
+
clearTimeout(toastTimeout);
|
| 568 |
+
toast.textContent = message;
|
| 569 |
+
toast.classList.add('visible');
|
| 570 |
+
|
| 571 |
+
toastTimeout = setTimeout(() => {
|
| 572 |
+
toast.classList.remove('visible');
|
| 573 |
+
}, 3500);
|
| 574 |
+
};
|
| 575 |
+
// --- MODIFICATION END ---
|
| 576 |
+
|
| 577 |
const getHistory = () => {
|
| 578 |
const historyFromStorage = JSON.parse(localStorage.getItem('aiPhotoshopHistory') || '[]');
|
| 579 |
if (!Array.isArray(historyFromStorage)) return [];
|
| 580 |
|
| 581 |
const FIVE_DAYS_IN_MS = 5 * 24 * 60 * 60 * 1000;
|
| 582 |
const now = Date.now();
|
|
|
|
|
|
|
| 583 |
const freshHistory = historyFromStorage.filter(item => {
|
| 584 |
+
if (typeof item.createdAt !== 'number') { return true; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
return (now - item.createdAt) < FIVE_DAYS_IN_MS;
|
| 586 |
});
|
|
|
|
|
|
|
| 587 |
if (freshHistory.length < historyFromStorage.length) {
|
| 588 |
localStorage.setItem('aiPhotoshopHistory', JSON.stringify(freshHistory));
|
| 589 |
}
|
|
|
|
| 590 |
return freshHistory;
|
| 591 |
};
|
| 592 |
|
| 593 |
const saveHistory = (history) => { localStorage.setItem('aiPhotoshopHistory', JSON.stringify(history)); renderHistory(); };
|
| 594 |
|
| 595 |
const addToHistory = (item) => {
|
| 596 |
+
let history = getHistory();
|
| 597 |
+
const newItem = { ...item, createdAt: Date.now() };
|
|
|
|
|
|
|
|
|
|
| 598 |
history.unshift(newItem);
|
| 599 |
if (history.length > 15) history.pop();
|
| 600 |
saveHistory(history);
|
| 601 |
};
|
|
|
|
| 602 |
|
| 603 |
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); }); };
|
| 604 |
|
|
|
|
| 662 |
}
|
| 663 |
|
| 664 |
if (resultUrl) {
|
| 665 |
+
displayResult([resultUrl]);
|
| 666 |
addToHistory({ prompt: promptInput.value.trim(), urls: [resultUrl] });
|
| 667 |
} else {
|
| 668 |
throw new Error('نتیجه دریافت شد اما لینک تصویر معتبر نبود.');
|
|
|
|
| 689 |
document.addEventListener('DOMContentLoaded', () => { renderHistory(); checkFormState(); });
|
| 690 |
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);
|
| 691 |
|
| 692 |
+
// --- MODIFICATION START: New Submit Button Logic ---
|
| 693 |
submitBtn.addEventListener('click', async () => {
|
| 694 |
+
const isFormInvalid = !uploadedFile || promptInput.value.trim() === '';
|
| 695 |
+
|
| 696 |
+
if (isFormInvalid) {
|
| 697 |
+
submitBtn.classList.add('shake-error');
|
| 698 |
+
submitBtn.addEventListener('animationend', () => {
|
| 699 |
+
submitBtn.classList.remove('shake-error');
|
| 700 |
+
}, { once: true });
|
| 701 |
+
|
| 702 |
+
let message = '';
|
| 703 |
+
if (!uploadedFile && promptInput.value.trim() === '') {
|
| 704 |
+
message = 'لطفا ابتدا تصویر و دستور ویرایش را وارد کنید.';
|
| 705 |
+
} else if (!uploadedFile) {
|
| 706 |
+
message = 'لطفا ابتدا یک تصویر انتخاب کنید.';
|
| 707 |
+
} else {
|
| 708 |
+
message = 'لطفا دستور ویرایش را بنویسید.';
|
| 709 |
+
}
|
| 710 |
+
showToast(message);
|
| 711 |
+
return;
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
// This part only runs if the form is valid
|
| 715 |
setLoading(true);
|
|
|
|
| 716 |
try {
|
| 717 |
const sessionHash = Math.random().toString(36).substring(7);
|
| 718 |
const textOverlay = document.querySelector('.text-overlay');
|
|
|
|
| 730 |
} catch (error) {
|
| 731 |
console.error('An error occurred:', error);
|
| 732 |
displayError(error.message);
|
| 733 |
+
setLoading(false);
|
| 734 |
}
|
| 735 |
});
|
| 736 |
+
// --- MODIFICATION END ---
|
| 737 |
|
| 738 |
lightboxClose.addEventListener('click', closeLightbox); lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); }); lightboxDownload.addEventListener('click', handleDownload); lightboxNext.addEventListener('click', () => { currentLightboxIndex = (currentLightboxIndex + 1) % currentLightboxGroup.length; updateLightboxImage(); }); clearHistoryBtn.addEventListener('click', handleClearHistory);
|
| 739 |
</script>
|