Spaces:
Running
Running
Update index.html
Browse files- 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.
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 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.
|
| 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);
|
| 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 |
-
// ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
| 555 |
return;
|
| 556 |
}
|
| 557 |
if (promptInput.value.trim() === '') {
|
| 558 |
triggerButtonShake();
|
| 559 |
-
|
|
|
|
| 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 |
|