Elias207 commited on
Commit
ccaf87d
·
verified ·
1 Parent(s): 279dfb8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +464 -176
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>فتوشاپ هوش مصنوعی (نسخه گالری پیشرفته)</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;600;700;800&display=swap" rel="stylesheet">
8
  <style>
9
  :root {
@@ -21,53 +21,194 @@
21
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
22
  }
23
 
24
- /* --- Base & Animation Styles --- */
25
- @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
26
- body { font-family: 'Vazirmatn', sans-serif; background-color: var(--bg-start); background-image: radial-gradient(circle at top left, var(--bg-start), var(--bg-end)); color: var(--text-color); margin: 0; padding: 3rem 1rem; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; transition: background-color 0.4s, color 0.4s; }
27
- .container { max-width: 750px; width: 100%; }
28
- header { text-align: center; margin-bottom: 3rem; animation: fadeInUp 0.8s var(--ease-out-expo) forwards; }
29
- h1 { font-size: 3rem; font-weight: 800; letter-spacing: -1px; margin: 0; background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
30
- p.subtitle { font-size: 1.2rem; color: #64748b; margin-top: 0.75rem; }
31
- main, .gallery-section { background-color: var(--card-bg); border-radius: 2rem; border: 1px solid var(--border-color); box-shadow: 0 4px 10px var(--shadow-color-dark), 0 20px 40px var(--shadow-color-light); padding: 3rem; transition: all 0.4s; animation: fadeInUp 0.8s var(--ease-out-expo) forwards; opacity: 0; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  main { animation-delay: 0.2s; }
33
  .gallery-section { margin-top: 3rem; animation-delay: 0.4s; }
34
- .panel { margin-bottom: 3rem; }
 
35
  .panel:last-child { margin-bottom: 0; }
36
- h2 { font-size: 1.4rem; font-weight: 700; margin: 0 0 1.5rem 0; padding-bottom: 1rem; display: flex; align-items: center; gap: 0.75rem; border-bottom: 1px solid var(--border-color); }
 
 
 
 
 
 
 
 
 
 
 
37
  h2 svg { color: var(--primary-color-start); }
38
 
39
- /* --- IMPROVED: Drop Zone Styles --- */
40
- #drop-zone { border: 2px dashed var(--border-color); border-radius: 1.5rem; padding: 1rem; text-align: center; cursor: pointer; transition: all 0.3s var(--ease-out-expo); position: relative; overflow: hidden; background-color: var(--drop-zone-bg); display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 280px; }
41
- #drop-zone::before { content: ''; position: absolute; inset: -2px; border-radius: 1.5rem; background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end)) border-box; -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); -webkit-mask-composite: destination-out; mask-composite: subtract; opacity: 0; transition: opacity 0.3s ease; }
42
- #drop-zone.dragover, #drop-zone:not(.has-image):hover { border-color: transparent; transform: translateY(-5px); box-shadow: 0 10px 20px -5px var(--shadow-color-light); }
43
- #drop-zone.dragover::before, #drop-zone:not(.has-image):hover::before { opacity: 1; }
44
- #drop-zone-content { transition: opacity 0.3s ease, transform 0.3s ease; }
45
- #drop-zone:not(.has-image):hover #drop-zone-content { transform: translateY(-5px); }
46
- #drop-zone-icon { width: 50px; height: 50px; margin-bottom: 1rem; color: #94a3b8; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  #drop-zone-text { color: #475569; font-weight: 500; }
48
- #image-preview { position: absolute; inset: 0; width: 100%; height: 100%; padding: 1rem; box-sizing: border-box; border-radius: 1.5rem; object-fit: contain; opacity: 0; transform: scale(0.95); transition: opacity 0.4s ease, transform 0.4s ease; pointer-events: none; }
49
- #image-preview.visible { opacity: 1; transform: scale(1); }
50
- #change-image-overlay { position: absolute; inset: 0; background-color: rgba(30, 41, 59, 0.7); display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; opacity: 0; transition: opacity 0.3s ease; pointer-events: none; }
51
- #drop-zone.has-image:hover #change-image-overlay { opacity: 1; pointer-events: auto; }
52
- #change-image-overlay svg { width: 40px; height: 40px; margin-bottom: 0.75rem; }
53
 
54
- /* --- Form & Result Styles --- */
55
- textarea { width: 100%; padding: 1rem; border: 2px solid var(--border-color); border-radius: 1rem; background-color: var(--drop-zone-bg); color: var(--text-color); font-family: 'Vazirmatn', sans-serif; font-size: 1rem; resize: vertical; min-height: 80px; box-sizing: border-box; transition: all 0.3s ease; }
56
- textarea:focus { outline: none; border-color: var(--primary-color-start); box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary-color-start) 15%, transparent); }
57
- button#submit-btn { position: relative; overflow: hidden; width: 100%; padding: 1.1rem; border: none; border-radius: 1rem; background-image: linear-gradient(45deg, var(--primary-color-start) 0%, var(--primary-color-end) 100%); background-size: 200% auto; color: white; font-size: 1.1rem; font-weight: 700; cursor: pointer; margin-top: 1rem; transition: all 0.4s var(--ease-out-expo); box-shadow: 0 4px 15px var(--shadow-color-light); }
58
- button#submit-btn:hover:not(:disabled) { transform: translateY(-4px); box-shadow: 0 10px 25px -5px var(--shadow-color-light); background-position: right center; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  button#submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
60
- #result-container { border: 2px solid var(--border-color); border-radius: 1.5rem; min-height: 300px; position: relative; background-color: var(--drop-zone-bg); overflow: hidden; padding: 0.5rem; transition: border-color 0.3s ease; }
61
- #result-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem; width: 100%; height: 100%; }
62
- @keyframes popIn-spring { 0% { opacity: 0; transform: scale(0.8) translateY(20px); } 80% { opacity: 1; transform: scale(1.05) translateY(-5px); } 100% { opacity: 1; transform: scale(1) translateY(0); } }
63
- #result-grid img { width: 100%; height: 100%; object-fit: cover; border-radius: 1rem; cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease; opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards; }
64
- #result-grid img:hover { transform: scale(1.05) rotate(1deg); box-shadow: 0 10px 30px -5px var(--shadow-color-dark); }
65
- #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- /* --- Loading Styles --- */
68
- #loading-placeholder { display: none; flex-direction: column; align-items: center; justify-content: center; gap: 1.5rem; opacity: 0; animation: fadeIn 0.5s ease forwards; position: absolute; inset: 0; }
 
 
 
69
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
70
  #result-container.loading #loading-placeholder { display: flex; }
 
71
  .creative-loader { width: 100px; height: 100px; position: relative; }
72
  .creative-loader svg { width: 100%; height: 100%; transform-origin: center; }
73
  .creative-loader .arc { fill: none; stroke-width: 4; stroke-linecap: round; transform-origin: 50% 50%; animation: spin-loader 2s linear infinite; }
@@ -77,46 +218,114 @@
77
  .creative-loader .star { fill: var(--primary-color-end); transform-origin: center; animation: pulse-star 1.5s ease-in-out infinite; }
78
  @keyframes spin-loader { to { transform: rotate(360deg); } }
79
  @keyframes pulse-star { 0%, 100% { transform: scale(0.9); opacity: 0.8; } 50% { transform: scale(1.1); opacity: 1; } }
 
80
  .loading-text { font-weight: 500; color: #64748b; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
- /* --- IMPROVED: History Gallery Styles --- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
84
- #clear-history-btn { background: none; border: 1px solid var(--border-color); color: #64748b; padding: 0.5rem 1rem; border-radius: 0.75rem; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; font-family: 'Vazirmatn', sans-serif; font-weight: 500; transition: all 0.2s; }
 
 
 
 
 
85
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
86
- #history-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.5rem; margin-top: 1.5rem; }
87
- #history-grid:empty::before { content: 'هنوز تصویری خلق نکرده‌اید. اولین ویرایش شما اینجا ذخیره خواهد شد.'; color: #64748b; grid-column: 1 / -1; text-align: center; padding: 3rem 1rem; background-color: var(--drop-zone-bg); border-radius: 1rem; }
88
- .history-item { background-color: var(--drop-zone-bg); border-radius: 1.25rem; border: 1px solid var(--border-color); overflow: hidden; box-shadow: 0 2px 8px var(--shadow-color-dark); transition: transform 0.3s ease, box-shadow 0.3s ease; opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards; display: flex; flex-direction: column; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  .history-item:hover { transform: translateY(-8px); box-shadow: 0 10px 20px var(--shadow-color-dark); }
90
- .history-item-img-stack { position: relative; width: 100%; height: 180px; background-color: #e2e8f0; cursor: pointer; }
91
- .history-item-img-stack img { position: absolute; width: 100%; height: 100%; object-fit: cover; border-radius: 0.5rem; box-shadow: 0 4px 15px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
92
- .history-item-img-stack:hover img { transform: scale(1.05) !important; }
93
- .history-item-img-stack img:nth-child(1) { z-index: 4; transform: rotate(2deg) scale(0.95); }
94
- .history-item-img-stack img:nth-child(2) { z-index: 3; transform: rotate(-2deg) scale(0.9); }
95
- .history-item-img-stack img:nth-child(3) { z-index: 2; transform: rotate(4deg) scale(0.85); }
96
- .history-item-img-stack img:nth-child(4) { z-index: 1; transform: rotate(-4deg) scale(0.8); }
97
- .history-item-content { padding: 1rem; display: flex; flex-direction: column; flex-grow: 1; }
98
- .history-item-prompt { font-size: 0.9rem; font-weight: 500; margin: 0 0 1rem 0; min-height: 4.5em; line-height: 1.5em; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; flex-grow: 1; }
99
- .history-item-actions { display: flex; gap: 0.5rem; margin-top: auto; }
100
- .history-btn { flex-grow: 1; text-align: center; padding: 0.6rem; border-radius: 0.75rem; border: 1px solid var(--border-color); background-color: var(--card-bg); color: var(--text-color); font-weight: 600; display: flex; justify-content: center; align-items: center; gap: 0.5rem; cursor: pointer; transition: all 0.2s; text-decoration: none; }
101
- .history-btn:hover { background-color: var(--primary-color-start); color: white; border-color: var(--primary-color-start); }
102
-
103
- /* --- IMPROVED: Lightbox with Navigation --- */
104
- #lightbox { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(10, 20, 40, 0.9); z-index: 1000; display: none; justify-content: center; align-items: center; padding: 1rem; box-sizing: border-box; backdrop-filter: blur(8px); }
105
- #lightbox-img { max-width: 90vw; max-height: 80vh; object-fit: contain; border-radius: 1rem; box-shadow: 0 20px 50px rgba(0,0,0,0.5); }
106
- .lightbox-nav { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2); width: 44px; height: 44px; border-radius: 50%; cursor: pointer; display: flex; justify-content: center; align-items: center; transition: all 0.2s; font-size: 2rem; user-select: none; }
107
- .lightbox-nav:hover { background-color: rgba(255, 255, 255, 0.2); transform: translateY(-50%) scale(1.1); }
108
- #lightbox-prev { right: 20px; }
109
- #lightbox-next { left: 20px; }
110
- #lightbox-controls { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 1rem; }
111
- #lightbox-counter { background-color: rgba(0,0,0,0.4); color: white; padding: 0.25rem 0.75rem; border-radius: 1rem; font-size: 0.9rem; font-weight: 500; }
112
- #lightbox-close { position: absolute; top: 20px; right: 20px; font-size: 2rem; }
113
- .lightbox-btn { background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2); width: 50px; height: 50px; border-radius: 50%; cursor: pointer; display: flex; justify-content: center; align-items: center; transition: all 0.2s; text-decoration: none; }
114
- .lightbox-btn:hover { background-color: rgba(255, 255, 255, 0.2); transform: scale(1.1); }
115
 
116
- @media (max-width: 600px) { body { padding: 1.5rem 1rem; } main, .gallery-section { padding: 1.5rem; } h1 { font-size: 2.5rem; } .lightbox-nav { display: none; } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  </style>
118
  </head>
119
  <body>
 
120
  <div class="container">
121
  <header>
122
  <h1>فتوشاپ هوش مصنوعی ✨</h1>
@@ -124,7 +333,7 @@
124
  </header>
125
 
126
  <main>
127
- <div class="panel">
128
  <h2>
129
  <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="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>
130
  ۱. تصویر خود را انتخاب کنید
@@ -132,91 +341,111 @@
132
  <div id="drop-zone">
133
  <div id="drop-zone-content">
134
  <svg id="drop-zone-icon" 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="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
135
- <span id="drop-zone-text">تصویر را اینجا بکشید یا برای انتخاب کلیک کنید</span>
 
136
  </div>
137
  <img id="image-preview" src="#" alt="پیش‌نمایش تصویر" />
138
- <!-- NEW: Change Image Overlay -->
139
- <div id="change-image-overlay">
140
- <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="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>
141
- <span>تغییر تصویر</span>
142
- </div>
143
  </div>
144
  <input type="file" id="file-input" accept="image/*" hidden>
145
  </div>
146
 
147
- <!-- The rest of the main content remains the same -->
148
- <div class="controls"> ... </div>
149
- <div class="panel"> ... </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  </main>
151
 
152
- <section class="gallery-section"> ... </section>
 
 
 
 
 
 
 
 
 
 
 
 
153
  </div>
154
 
155
- <!-- IMPROVED: Lightbox with Navigation Controls -->
156
  <div id="lightbox">
157
- <button id="lightbox-prev" class="lightbox-nav">&rsaquo;</button>
158
- <button id="lightbox-next" class="lightbox-nav">&lsaquo;</button>
159
  <img id="lightbox-img" src="">
160
  <div id="lightbox-controls">
161
- <span id="lightbox-counter"></span>
162
  <a id="lightbox-download" href="#" download="edited-image.png" title="دانلود تصویر" class="lightbox-btn">
163
- <svg fill="white" viewBox="0 0 24 24" width="24" height="24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
164
  </a>
165
  </div>
166
- <button id="lightbox-close" class="lightbox-btn" style="position: absolute; top: 20px; left: 20px;">&times;</button>
167
  </div>
168
 
169
  <script>
170
- // --- Element Selection ---
 
 
 
 
171
  const dropZone = document.getElementById('drop-zone');
 
172
  const fileInput = document.getElementById('file-input');
173
  const imagePreview = document.getElementById('image-preview');
174
  const promptInput = document.getElementById('prompt-input');
175
  const submitBtn = document.getElementById('submit-btn');
 
 
176
  const resultGrid = document.getElementById('result-grid');
177
- const historyGrid = document.getElementById('history-grid');
178
- const clearHistoryBtn = document.getElementById('clear-history-btn');
179
  const lightbox = document.getElementById('lightbox');
180
  const lightboxImg = document.getElementById('lightbox-img');
181
  const lightboxClose = document.getElementById('lightbox-close');
182
  const lightboxDownload = document.getElementById('lightbox-download');
183
- const lightboxPrev = document.getElementById('lightbox-prev');
184
- const lightboxNext = document.getElementById('lightbox-next');
185
- const lightboxCounter = document.getElementById('lightbox-counter');
186
- // ... (other element selections remain the same)
187
- const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
188
- const dropZoneContent = document.getElementById('drop-zone-content');
189
- const btnText = document.getElementById('btn-text');
190
- const resultContainer = document.getElementById('result-container');
191
- const errorMessage = document.getElementById('error-message');
192
-
193
  let uploadedFile = null;
194
- let lightboxImageUrls = [];
195
- let currentLightboxIndex = 0;
196
 
197
- // --- Event Listeners ---
198
  dropZone.addEventListener('click', () => fileInput.click());
 
 
 
 
199
  fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
200
  ['dragenter', 'dragover'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.add('dragover'); }));
201
  ['dragleave', 'drop'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); }));
202
  dropZone.addEventListener('drop', (e) => handleFile(e.dataTransfer.files[0]));
203
  promptInput.addEventListener('input', checkFormState);
204
  submitBtn.addEventListener('click', handleSubmit);
205
- clearHistoryBtn.addEventListener('click', handleClearHistory);
206
- document.addEventListener('DOMContentLoaded', renderHistory);
207
-
208
- // Lightbox Listeners
209
  lightboxClose.addEventListener('click', closeLightbox);
210
  lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); });
211
- lightboxNext.addEventListener('click', showNextImage);
212
- lightboxPrev.addEventListener('click', showPrevImage);
213
- document.addEventListener('keydown', (e) => {
214
- if (lightbox.style.display === 'flex') {
215
- if (e.key === 'ArrowRight') showPrevImage(); // RTL, so Right arrow is previous
216
- if (e.key === 'ArrowLeft') showNextImage(); // Left arrow is next
217
- if (e.key === 'Escape') closeLightbox();
218
- }
219
- });
220
 
221
  // --- Core Functions ---
222
  function handleFile(file) {
@@ -226,7 +455,6 @@
226
  reader.onload = (e) => {
227
  imagePreview.src = e.target.result;
228
  imagePreview.classList.add('visible');
229
- dropZone.classList.add('has-image'); // NEW: Add class to control overlay visibility
230
  dropZoneContent.style.opacity = '0';
231
  checkFormState();
232
  clearResult();
@@ -234,70 +462,124 @@
234
  reader.readAsDataURL(file);
235
  }
236
 
237
- // ... (handleSubmit, setLoading, checkFormState, displayError, clearResult remain mostly the same)
 
 
 
238
  async function handleSubmit() {
239
  if (submitBtn.disabled) return;
240
- // ... (rest of the submit logic is the same)
241
- // ... on success:
242
- displayResult(result.image_urls, true); // Pass true to show in result grid
243
- addToHistory({ prompt: promptInput.value.trim(), urls: result.image_urls });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  }
245
 
246
- function displayResult(imageUrls, isLiveResult = false) {
247
- if (!isLiveResult) return; // Only populate the main result grid for live results
248
  resultGrid.innerHTML = '';
249
  imageUrls.forEach((url, index) => {
250
  const img = document.createElement('img');
251
  img.src = url;
252
  img.alt = 'تصویر ویرایش شده';
253
  img.style.animationDelay = `${index * 120}ms`;
254
- img.addEventListener('click', () => openLightbox(imageUrls, index));
255
  resultGrid.appendChild(img);
256
  });
257
  }
258
 
259
- // --- IMPROVED Lightbox Functions with Navigation ---
260
- function openLightbox(urls, index = 0) {
261
- lightboxImageUrls = urls;
262
- currentLightboxIndex = index;
263
- lightbox.style.display = 'flex';
264
- updateLightboxImage();
265
  }
266
 
267
- function closeLightbox() {
268
- lightbox.style.display = 'none';
 
269
  }
270
-
271
- function updateLightboxImage() {
272
- if (lightboxImageUrls.length === 0) return;
273
- const currentUrl = lightboxImageUrls[currentLightboxIndex];
274
- lightboxImg.src = currentUrl;
275
- lightboxDownload.href = currentUrl;
276
- lightboxCounter.textContent = `${currentLightboxIndex + 1} / ${lightboxImageUrls.length}`;
277
- // Preload next and previous images for smoother navigation
278
- if(currentLightboxIndex < lightboxImageUrls.length - 1) new Image().src = lightboxImageUrls[currentLightboxIndex + 1];
279
- if(currentLightboxIndex > 0) new Image().src = lightboxImageUrls[currentLightboxIndex - 1];
 
 
 
 
 
 
 
280
  }
281
 
282
- function showNextImage() {
283
- currentLightboxIndex = (currentLightboxIndex + 1) % lightboxImageUrls.length;
284
- updateLightboxImage();
285
  }
286
 
287
- function showPrevImage() {
288
- currentLightboxIndex = (currentLightboxIndex - 1 + lightboxImageUrls.length) % lightboxImageUrls.length;
289
- updateLightboxImage();
 
 
290
  }
291
 
292
- // --- IMPROVED History Functions ---
293
- function getHistory() { /* ... (same) ... */ }
294
- function saveHistory(history) { /* ... (same) ... */ }
295
- function addToHistory(item) { /* ... (same) ... */ }
296
- function handleClearHistory() { /* ... (same) ... */ }
 
 
 
 
 
 
 
297
 
298
  function renderHistory() {
299
  const history = getHistory();
300
- historyGrid.innerHTML = '';
301
 
302
  clearHistoryBtn.style.display = history.length > 0 ? 'flex' : 'none';
303
 
@@ -306,39 +588,45 @@
306
  card.className = 'history-item';
307
  card.style.animationDelay = `${index * 80}ms`;
308
 
309
- const imageStackHTML = item.urls.slice(0, 4).map(url =>
310
- `<img src="${url}" alt="پیش‌نمایش تاریخچه">`
311
- ).join('');
 
 
 
 
 
 
312
 
313
- card.innerHTML = `
314
- <div class="history-item-img-stack">
315
- ${imageStackHTML}
316
- </div>
317
  <div class="history-item-content">
318
  <p class="history-item-prompt">${item.prompt}</p>
319
- <div class="history-item-actions">
320
- <a href="${item.urls[0]}" download="ai-edit-${Date.now()}.png" class="history-btn">
321
- <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="M12 17v-10"/><path d="m15 14-3 3-3-3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
322
- دانلود
323
- </a>
324
  </div>
325
  </div>
326
  `;
 
 
327
 
328
- // Event listener for the entire image stack
329
- card.querySelector('.history-item-img-stack').addEventListener('click', () => openLightbox(item.urls, 0));
330
-
 
 
 
 
 
 
331
  historyGrid.appendChild(card);
332
  });
333
  }
334
-
335
- // (Helper functions like checkFormState, setLoading etc. are assumed to be here and unchanged)
336
- // Make sure to copy them from the previous version if they are missing.
337
- function checkFormState() { submitBtn.disabled = !uploadedFile || promptInput.value.trim() === ''; }
338
- function setLoading(isLoading) { if (isLoading) { resultContainer.classList.add('loading'); btnText.classList.add('hidden'); const loader = document.createElement('div'); loader.className = 'loader-dots'; loader.innerHTML = '<div class="dot1"></div><div class="dot2"></div><div class="dot3"></div>'; submitBtn.appendChild(loader); submitBtn.disabled = true; resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; } else { resultContainer.classList.remove('loading'); btnText.classList.remove('hidden'); const loader = submitBtn.querySelector('.loader-dots'); if (loader) submitBtn.removeChild(loader); checkFormState(); } }
339
- function clearResult() { resultGrid.innerHTML = ''; errorMessage.style.display = 'none'; }
340
- function displayError(message) { errorMessage.textContent = message; errorMessage.style.display = 'block'; }
341
-
342
  checkFormState();
343
  </script>
344
  </body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>فتوشاپ هوش مصنوعی (نسخه گالری حرفه‌ای)</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;600;700;800&display=swap" rel="stylesheet">
8
  <style>
9
  :root {
 
21
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
22
  }
23
 
24
+ .dark {
25
+ --bg-start: #0f172a;
26
+ --bg-end: #0b1120;
27
+ --text-color: #e2e8f0;
28
+ --card-bg: #1e293b;
29
+ --border-color: #334155;
30
+ --shadow-color-light: rgba(0, 0, 0, 0.2);
31
+ --shadow-color-dark: rgba(0, 0, 0, 0.25);
32
+ --drop-zone-bg: #111827;
33
+ }
34
+
35
+ @keyframes fadeInUp {
36
+ from { opacity: 0; transform: translateY(30px); }
37
+ to { opacity: 1; transform: translateY(0); }
38
+ }
39
+
40
+ body {
41
+ font-family: 'Vazirmatn', sans-serif;
42
+ background-color: var(--bg-start);
43
+ background-image: radial-gradient(circle at top left, var(--bg-start), var(--bg-end));
44
+ color: var(--text-color);
45
+ margin: 0;
46
+ padding: 3rem 1rem;
47
+ display: flex;
48
+ justify-content: center;
49
+ align-items: flex-start;
50
+ min-height: 100vh;
51
+ transition: background-color 0.4s, color 0.4s;
52
+ }
53
+
54
+ .container {
55
+ max-width: 750px;
56
+ width: 100%;
57
+ }
58
+
59
+ header {
60
+ text-align: center;
61
+ margin-bottom: 3rem;
62
+ animation: fadeInUp 0.8s var(--ease-out-expo) forwards;
63
+ }
64
+
65
+ h1 {
66
+ font-size: 3rem;
67
+ font-weight: 800;
68
+ letter-spacing: -1px;
69
+ margin: 0;
70
+ background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end));
71
+ -webkit-background-clip: text;
72
+ -webkit-text-fill-color: transparent;
73
+ }
74
+
75
+ p.subtitle {
76
+ font-size: 1.2rem;
77
+ color: #64748b;
78
+ margin-top: 0.75rem;
79
+ }
80
+ .dark p.subtitle { color: #94a3b8; }
81
+
82
+ main, .gallery-section {
83
+ background-color: var(--card-bg);
84
+ border-radius: 2rem;
85
+ border: 1px solid var(--border-color);
86
+ box-shadow: 0 4px 10px var(--shadow-color-dark), 0 20px 40px var(--shadow-color-light);
87
+ padding: 3rem;
88
+ transition: all 0.4s;
89
+ animation: fadeInUp 0.8s var(--ease-out-expo) forwards;
90
+ opacity: 0;
91
+ }
92
  main { animation-delay: 0.2s; }
93
  .gallery-section { margin-top: 3rem; animation-delay: 0.4s; }
94
+
95
+ .panel, .controls { margin-bottom: 3rem; }
96
  .panel:last-child { margin-bottom: 0; }
97
+ .controls { margin-bottom: 2.5rem; }
98
+
99
+ h2 {
100
+ font-size: 1.4rem;
101
+ font-weight: 700;
102
+ margin: 0 0 1.5rem 0;
103
+ padding-bottom: 1rem;
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 0.75rem;
107
+ border-bottom: 1px solid var(--border-color);
108
+ }
109
  h2 svg { color: var(--primary-color-start); }
110
 
111
+ #drop-zone {
112
+ border: 2px dashed var(--border-color); border-radius: 1.5rem;
113
+ padding: 2rem; text-align: center; cursor: pointer;
114
+ transition: all 0.3s var(--ease-out-expo);
115
+ position: relative; overflow: hidden; background-color: var(--drop-zone-bg);
116
+ display: flex; flex-direction: column; justify-content: center;
117
+ align-items: center; min-height: 250px;
118
+ }
119
+ #drop-zone::before {
120
+ content: ''; position: absolute; inset: -2px; border-radius: 1.5rem;
121
+ background: linear-gradient(45deg, var(--primary-color-start), var(--primary-color-end)) border-box;
122
+ -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
123
+ mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
124
+ -webkit-mask-composite: destination-out; mask-composite: subtract;
125
+ opacity: 0; transition: opacity 0.3s ease;
126
+ }
127
+ #drop-zone.dragover, #drop-zone:hover {
128
+ border-color: transparent; transform: translateY(-5px);
129
+ box-shadow: 0 10px 20px -5px var(--shadow-color-light);
130
+ }
131
+ #drop-zone.dragover::before, #drop-zone:hover::before { opacity: 1; }
132
+ #drop-zone-content { transition: opacity 0.3s ease, transform 0.3s ease; display: flex; flex-direction: column; align-items: center; gap: 1rem; }
133
+ #drop-zone:hover #drop-zone-content { transform: translateY(-5px); }
134
+ #drop-zone-icon { width: 40px; height: 40px; color: #94a3b8; }
135
  #drop-zone-text { color: #475569; font-weight: 500; }
136
+ .dark #drop-zone-text { color: #94a3b8; }
 
 
 
 
137
 
138
+ .upload-btn-styled {
139
+ background-image: linear-gradient(45deg, var(--primary-color-start) 0%, var(--primary-color-end) 100%);
140
+ color: white; border: none; padding: 0.75rem 1.5rem;
141
+ border-radius: 0.75rem; font-family: 'Vazirmatn', sans-serif;
142
+ font-weight: 600; font-size: 1rem; cursor: pointer;
143
+ margin-top: 1rem; transition: all 0.3s ease;
144
+ }
145
+ .upload-btn-styled:hover { transform: translateY(-3px) scale(1.05); box-shadow: 0 8px 15px var(--shadow-color-light); }
146
+
147
+ #image-preview {
148
+ position: absolute; inset: 0; width: 100%; height: 100%;
149
+ padding: 1rem; box-sizing: border-box; border-radius: 1.5rem;
150
+ object-fit: contain; opacity: 0; transform: scale(0.95);
151
+ transition: opacity 0.4s ease, transform 0.4s ease;
152
+ pointer-events: none; backdrop-filter: blur(4px);
153
+ }
154
+ #image-preview.visible { opacity: 1; transform: scale(1); pointer-events: auto; }
155
+
156
+ textarea {
157
+ width: 100%; padding: 1rem; border: 2px solid var(--border-color);
158
+ border-radius: 1rem; background-color: var(--drop-zone-bg);
159
+ color: var(--text-color); font-family: 'Vazirmatn', sans-serif;
160
+ font-size: 1rem; resize: vertical; min-height: 80px;
161
+ box-sizing: border-box; transition: all 0.3s ease;
162
+ }
163
+ textarea:focus {
164
+ outline: none; border-color: var(--primary-color-start);
165
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary-color-start) 15%, transparent);
166
+ }
167
+
168
+ button#submit-btn {
169
+ position: relative; overflow: hidden;
170
+ width: 100%; padding: 1.1rem; border: none; border-radius: 1rem;
171
+ background-image: linear-gradient(45deg, var(--primary-color-start) 0%, var(--primary-color-end) 100%);
172
+ background-size: 200% auto; color: white;
173
+ font-size: 1.1rem; font-weight: 700; cursor: pointer;
174
+ margin-top: 1rem; transition: all 0.4s var(--ease-out-expo);
175
+ box-shadow: 0 4px 15px var(--shadow-color-light);
176
+ }
177
+ button#submit-btn:hover:not(:disabled) {
178
+ transform: translateY(-4px);
179
+ box-shadow: 0 10px 25px -5px var(--shadow-color-light);
180
+ background-position: right center;
181
+ }
182
  button#submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
183
+ .loader-dots {
184
+ display: flex; justify-content: center; align-items: center;
185
+ position: absolute; inset: 0;
186
+ }
187
+ .loader-dots div {
188
+ width: 8px; height: 8px; margin: 0 4px; background-color: white; border-radius: 50%;
189
+ animation: bounce 1.4s infinite ease-in-out both;
190
+ }
191
+ .loader-dots .dot1 { animation-delay: -0.32s; }
192
+ .loader-dots .dot2 { animation-delay: -0.16s; }
193
+ @keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1.0); } }
194
+ #btn-text.hidden { visibility: hidden; }
195
+
196
+ #result-container {
197
+ border: 2px solid var(--border-color); border-radius: 1.5rem;
198
+ min-height: 250px;
199
+ position: relative;
200
+ background-color: var(--drop-zone-bg); overflow: hidden;
201
+ padding: 0.5rem; transition: border-color 0.3s ease;
202
+ }
203
 
204
+ #loading-placeholder {
205
+ display: none; flex-direction: column; align-items: center; justify-content: center;
206
+ gap: 1.5rem; opacity: 0; animation: fadeIn 0.5s ease forwards;
207
+ position: absolute; inset: 0;
208
+ }
209
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
210
  #result-container.loading #loading-placeholder { display: flex; }
211
+
212
  .creative-loader { width: 100px; height: 100px; position: relative; }
213
  .creative-loader svg { width: 100%; height: 100%; transform-origin: center; }
214
  .creative-loader .arc { fill: none; stroke-width: 4; stroke-linecap: round; transform-origin: 50% 50%; animation: spin-loader 2s linear infinite; }
 
218
  .creative-loader .star { fill: var(--primary-color-end); transform-origin: center; animation: pulse-star 1.5s ease-in-out infinite; }
219
  @keyframes spin-loader { to { transform: rotate(360deg); } }
220
  @keyframes pulse-star { 0%, 100% { transform: scale(0.9); opacity: 0.8; } 50% { transform: scale(1.1); opacity: 1; } }
221
+
222
  .loading-text { font-weight: 500; color: #64748b; }
223
+ .dark .loading-text { color: #94a3b8; }
224
+
225
+ #result-grid {
226
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
227
+ gap: 0.5rem; width: 100%; height: 100%;
228
+ }
229
+ @keyframes popIn-spring { 0% { opacity: 0; transform: scale(0.8) translateY(20px); } 80% { opacity: 1; transform: scale(1.05) translateY(-5px); } 100% { opacity: 1; transform: scale(1) translateY(0); } }
230
+ #result-grid img {
231
+ width: 100%; height: 100%; max-height: 250px; object-fit: cover; border-radius: 1rem;
232
+ cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease;
233
+ opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards;
234
+ }
235
+ #result-grid img:hover { transform: scale(1.05) rotate(1deg); box-shadow: 0 10px 30px -5px var(--shadow-color-dark); }
236
+
237
+ #error-message { color: var(--danger-color); text-align: center; margin-top: 1rem; display: none; font-weight: 500; }
238
 
239
+ #lightbox {
240
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
241
+ background-color: rgba(10, 20, 40, 0.85); z-index: 1000; display: none;
242
+ justify-content: center; align-items: center; padding: 2rem;
243
+ box-sizing: border-box; backdrop-filter: blur(8px);
244
+ }
245
+ #lightbox-img { max-width: 90vw; max-height: 85vh; object-fit: contain; border-radius: 1rem; box-shadow: 0 20px 50px rgba(0,0,0,0.5); }
246
+ #lightbox-controls { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; gap: 1rem; }
247
+ .lightbox-btn {
248
+ background-color: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255,255,255,0.2);
249
+ width: 50px; height: 50px; border-radius: 50%; cursor: pointer; display: flex;
250
+ justify-content: center; align-items: center; transition: all 0.2s; text-decoration: none;
251
+ }
252
+ .lightbox-btn svg { width: 24px; height: 24px; }
253
+ .lightbox-btn:hover { background-color: rgba(255, 255, 255, 0.2); transform: scale(1.1); }
254
+ #lightbox-close { position: absolute; top: 20px; right: 20px; font-size: 2rem; }
255
+
256
+ /* --- History Gallery Styles --- */
257
  .gallery-header { display: flex; justify-content: space-between; align-items: center; }
258
+ #clear-history-btn {
259
+ background: none; border: 1px solid var(--border-color); color: #64748b;
260
+ padding: 0.5rem 1rem; border-radius: 0.75rem; cursor: pointer;
261
+ display: flex; align-items: center; gap: 0.5rem;
262
+ font-family: 'Vazirmatn', sans-serif; font-weight: 500; transition: all 0.2s;
263
+ }
264
  #clear-history-btn:hover { border-color: var(--danger-color); color: var(--danger-color); }
265
+ #clear-history-btn svg { width: 18px; height: 18px; }
266
+
267
+ #history-grid {
268
+ display: grid;
269
+ grid-template-columns: 1fr;
270
+ gap: 1.5rem;
271
+ margin-top: 1.5rem;
272
+ }
273
+ #history-grid:empty::before {
274
+ content: 'هنوز تصویری خلق نکرده‌اید. اولین ویرایش شما اینجا ذخیره خواهد شد.';
275
+ color: #64748b;
276
+ .dark & { color: #94a3b8; }
277
+ grid-column: 1 / -1;
278
+ text-align: center;
279
+ padding: 3rem 1rem;
280
+ background-color: var(--drop-zone-bg);
281
+ border-radius: 1rem;
282
+ }
283
+ .history-item {
284
+ background-color: var(--drop-zone-bg);
285
+ border-radius: 1.25rem;
286
+ border: 1px solid var(--border-color);
287
+ overflow: hidden;
288
+ box-shadow: 0 2px 8px var(--shadow-color-dark);
289
+ transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.5s ease;
290
+ opacity: 0; animation: popIn-spring 0.6s var(--ease-out-expo) forwards;
291
+ display: flex; flex-direction: column;
292
+ }
293
+ .history-item.removing { transform: scale(0.9); opacity: 0; }
294
  .history-item:hover { transform: translateY(-8px); box-shadow: 0 10px 20px var(--shadow-color-dark); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
 
296
+ .history-item-thumbnails {
297
+ display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; padding: 0.5rem;
298
+ }
299
+ .history-item-thumbnails img {
300
+ width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 0.75rem;
301
+ cursor: pointer; transition: transform 0.2s ease;
302
+ }
303
+ .history-item-thumbnails img:hover { transform: scale(1.08); }
304
+
305
+ .history-item-content { padding: 0 1.25rem 1.25rem 1.25rem; border-top: 1px solid var(--border-color); }
306
+ .history-item-prompt {
307
+ font-size: 0.95rem; font-weight: 500;
308
+ margin: 1rem 0 1rem 0;
309
+ line-height: 1.6em;
310
+ }
311
+ .history-item-footer { display: flex; justify-content: space-between; align-items: center; }
312
+ .history-btn.delete-btn {
313
+ background-color: transparent; color: var(--danger-color); border: 1px solid var(--danger-color);
314
+ padding: 0.5rem 1rem; border-radius: 0.75rem; cursor: pointer; display: flex; align-items: center; gap: 0.5rem;
315
+ font-weight: 600; transition: all 0.2s;
316
+ }
317
+ .history-btn.delete-btn:hover { background-color: var(--danger-color); color: white; }
318
+
319
+ @media (max-width: 600px) {
320
+ body { padding: 1.5rem 1rem; }
321
+ main, .gallery-section { padding: 1.5rem; }
322
+ h1 { font-size: 2.5rem; }
323
+ .history-item-thumbnails { grid-template-columns: repeat(2, 1fr); }
324
+ }
325
  </style>
326
  </head>
327
  <body>
328
+
329
  <div class="container">
330
  <header>
331
  <h1>فتوشاپ هوش مصنوعی ✨</h1>
 
333
  </header>
334
 
335
  <main>
336
+ <div class="panel">
337
  <h2>
338
  <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="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>
339
  ۱. تصویر خود را انتخاب کنید
 
341
  <div id="drop-zone">
342
  <div id="drop-zone-content">
343
  <svg id="drop-zone-icon" 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="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
344
+ <span id="drop-zone-text">تصویر را اینجا بکشید یا از دکمه زیر استفاده کنید</span>
345
+ <button class="upload-btn-styled">انتخاب فایل</button>
346
  </div>
347
  <img id="image-preview" src="#" alt="پیش‌نمایش تصویر" />
 
 
 
 
 
348
  </div>
349
  <input type="file" id="file-input" accept="image/*" hidden>
350
  </div>
351
 
352
+ <!-- Rest of the main content remains the same -->
353
+ <div class="controls">
354
+ <h2>
355
+ <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="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
356
+ ۲. دستور ویرایش را بنویسید
357
+ </h2>
358
+ <textarea id="prompt-input" rows="3" placeholder="مثال: پس‌زمینه را حذف کن و یک ساحل آفتابی قرار بده"></textarea>
359
+ <button id="submit-btn" disabled>
360
+ <span id="btn-text">خلق کن</span>
361
+ </button>
362
+ <p id="error-message"></p>
363
+ </div>
364
+
365
+ <div class="panel">
366
+ <h2>
367
+ <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="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>
368
+ ۳. نتیجه را ببینید
369
+ </h2>
370
+ <div id="result-container">
371
+ <div id="loading-placeholder">
372
+ <div class="creative-loader">
373
+ <svg viewBox="0 0 50 50"><g><circle class="arc arc1" cx="25" cy="25" r="22" stroke-dasharray="60 150"></circle><circle class="arc arc2" cx="25" cy="25" r="18" stroke-dasharray="60 150"></circle><circle class="arc arc3" cx="25" cy="25" r="14" stroke-dasharray="60 150"></circle><path class="star" d="M25,21 L26.9,24.1 L30,25 L26.9,25.9 L25,29 L23.1,25.9 L20,25 L23.1,24.1 Z"></path></g></svg>
374
+ </div>
375
+ <p class="loading-text">هوش مصنوعی در حال خلق اثر شماست...</p>
376
+ </div>
377
+ <div id="result-grid"></div>
378
+ </div>
379
+ </div>
380
  </main>
381
 
382
+ <section class="gallery-section">
383
+ <div class="gallery-header">
384
+ <h2>
385
+ <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"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></svg>
386
+ گالری و تاریخچه شما
387
+ </h2>
388
+ <button id="clear-history-btn" style="display: none;">
389
+ <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>
390
+ <span>پاک کردن همه</span>
391
+ </button>
392
+ </div>
393
+ <div id="history-grid"></div>
394
+ </section>
395
  </div>
396
 
 
397
  <div id="lightbox">
 
 
398
  <img id="lightbox-img" src="">
399
  <div id="lightbox-controls">
 
400
  <a id="lightbox-download" href="#" download="edited-image.png" title="دانلود تصویر" class="lightbox-btn">
401
+ <svg fill="white" viewBox="0 0 24 24"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
402
  </a>
403
  </div>
404
+ <button id="lightbox-close" class="lightbox-btn">&times;</button>
405
  </div>
406
 
407
  <script>
408
+ // Constants
409
+ const API_URL = 'https://alfa-editor-worker.onrender.com/api/edit';
410
+ const HISTORY_KEY = 'aiPhotoshopHistory';
411
+
412
+ // Element Selectors
413
  const dropZone = document.getElementById('drop-zone');
414
+ const dropZoneContent = document.getElementById('drop-zone-content');
415
  const fileInput = document.getElementById('file-input');
416
  const imagePreview = document.getElementById('image-preview');
417
  const promptInput = document.getElementById('prompt-input');
418
  const submitBtn = document.getElementById('submit-btn');
419
+ const btnText = document.getElementById('btn-text');
420
+ const resultContainer = document.getElementById('result-container');
421
  const resultGrid = document.getElementById('result-grid');
422
+ const errorMessage = document.getElementById('error-message');
 
423
  const lightbox = document.getElementById('lightbox');
424
  const lightboxImg = document.getElementById('lightbox-img');
425
  const lightboxClose = document.getElementById('lightbox-close');
426
  const lightboxDownload = document.getElementById('lightbox-download');
427
+ const historyGrid = document.getElementById('history-grid');
428
+ const clearHistoryBtn = document.getElementById('clear-history-btn');
429
+ const uploadBtnStyled = document.querySelector('.upload-btn-styled'); // New upload button
430
+
 
 
 
 
 
 
431
  let uploadedFile = null;
 
 
432
 
433
+ // --- Event Listeners Setup ---
434
  dropZone.addEventListener('click', () => fileInput.click());
435
+ uploadBtnStyled.addEventListener('click', (e) => {
436
+ e.stopPropagation(); // Prevent dropzone click from firing twice
437
+ fileInput.click();
438
+ });
439
  fileInput.addEventListener('change', (e) => handleFile(e.target.files[0]));
440
  ['dragenter', 'dragover'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.add('dragover'); }));
441
  ['dragleave', 'drop'].forEach(eventName => dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); }));
442
  dropZone.addEventListener('drop', (e) => handleFile(e.dataTransfer.files[0]));
443
  promptInput.addEventListener('input', checkFormState);
444
  submitBtn.addEventListener('click', handleSubmit);
 
 
 
 
445
  lightboxClose.addEventListener('click', closeLightbox);
446
  lightbox.addEventListener('click', (e) => { if (e.target === lightbox) closeLightbox(); });
447
+ clearHistoryBtn.addEventListener('click', handleClearHistory);
448
+ document.addEventListener('DOMContentLoaded', renderHistory);
 
 
 
 
 
 
 
449
 
450
  // --- Core Functions ---
451
  function handleFile(file) {
 
455
  reader.onload = (e) => {
456
  imagePreview.src = e.target.result;
457
  imagePreview.classList.add('visible');
 
458
  dropZoneContent.style.opacity = '0';
459
  checkFormState();
460
  clearResult();
 
462
  reader.readAsDataURL(file);
463
  }
464
 
465
+ function checkFormState() {
466
+ submitBtn.disabled = !uploadedFile || promptInput.value.trim() === '';
467
+ }
468
+
469
  async function handleSubmit() {
470
  if (submitBtn.disabled) return;
471
+ setLoading(true);
472
+ const formData = new FormData();
473
+ formData.append('image', uploadedFile);
474
+ formData.append('prompt', promptInput.value.trim());
475
+ try {
476
+ const response = await fetch(API_URL, { method: 'POST', body: formData });
477
+ if (!response.ok) {
478
+ const errorData = await response.json().catch(() => ({ error: `خطای سرور: ${response.statusText}` }));
479
+ throw new Error(errorData.error || `یک خطای ناشناخته رخ داد.`);
480
+ }
481
+ const result = await response.json();
482
+ if (result.image_urls && Array.isArray(result.image_urls) && result.image_urls.length > 0) {
483
+ displayResult(result.image_urls);
484
+ addToHistory({ prompt: promptInput.value.trim(), urls: result.image_urls, id: Date.now() });
485
+ } else {
486
+ throw new Error('پاسخ معتبری از سرور دریافت نشد.');
487
+ }
488
+ } catch (error) {
489
+ displayError(error.message);
490
+ } finally {
491
+ setLoading(false);
492
+ }
493
+ }
494
+
495
+ function setLoading(isLoading) {
496
+ if (isLoading) {
497
+ resultContainer.classList.add('loading');
498
+ btnText.classList.add('hidden');
499
+ const loader = document.createElement('div');
500
+ loader.className = 'loader-dots';
501
+ loader.innerHTML = '<div class="dot1"></div><div class="dot2"></div><div class="dot3"></div>';
502
+ submitBtn.appendChild(loader);
503
+ submitBtn.disabled = true;
504
+ resultGrid.innerHTML = '';
505
+ errorMessage.style.display = 'none';
506
+ } else {
507
+ resultContainer.classList.remove('loading');
508
+ btnText.classList.remove('hidden');
509
+ const loader = submitBtn.querySelector('.loader-dots');
510
+ if (loader) submitBtn.removeChild(loader);
511
+ checkFormState();
512
+ }
513
  }
514
 
515
+ function displayResult(imageUrls) {
 
516
  resultGrid.innerHTML = '';
517
  imageUrls.forEach((url, index) => {
518
  const img = document.createElement('img');
519
  img.src = url;
520
  img.alt = 'تصویر ویرایش شده';
521
  img.style.animationDelay = `${index * 120}ms`;
522
+ img.addEventListener('click', () => openLightbox(url));
523
  resultGrid.appendChild(img);
524
  });
525
  }
526
 
527
+ function clearResult() {
528
+ resultGrid.innerHTML = '';
529
+ errorMessage.style.display = 'none';
 
 
 
530
  }
531
 
532
+ function displayError(message) {
533
+ errorMessage.textContent = message;
534
+ errorMessage.style.display = 'block';
535
  }
536
+
537
+ function openLightbox(url) {
538
+ lightboxImg.src = url;
539
+ lightboxDownload.href = url;
540
+ lightbox.style.display = 'flex';
541
+ }
542
+ function closeLightbox() { lightbox.style.display = 'none'; }
543
+
544
+ // --- History Management Functions ---
545
+ function getHistory() {
546
+ try {
547
+ const history = localStorage.getItem(HISTORY_KEY);
548
+ return history ? JSON.parse(history) : [];
549
+ } catch (e) {
550
+ console.error("Failed to parse history from localStorage", e);
551
+ return [];
552
+ }
553
  }
554
 
555
+ function saveHistory(history) {
556
+ localStorage.setItem(HISTORY_KEY, JSON.stringify(history));
557
+ renderHistory();
558
  }
559
 
560
+ function addToHistory(item) {
561
+ let history = getHistory();
562
+ history.unshift(item);
563
+ if (history.length > 20) history.pop();
564
+ saveHistory(history);
565
  }
566
 
567
+ function deleteHistoryItem(id) {
568
+ let history = getHistory();
569
+ const updatedHistory = history.filter(item => item.id !== id);
570
+ saveHistory(updatedHistory);
571
+ }
572
+
573
+ function handleClearHistory() {
574
+ if (confirm('آیا از پاک کردن تمام تاریخچه ویرایش‌ها مطمئن هستید؟')) {
575
+ localStorage.removeItem(HISTORY_KEY);
576
+ renderHistory();
577
+ }
578
+ }
579
 
580
  function renderHistory() {
581
  const history = getHistory();
582
+ historyGrid.innerHTML = '';
583
 
584
  clearHistoryBtn.style.display = history.length > 0 ? 'flex' : 'none';
585
 
 
588
  card.className = 'history-item';
589
  card.style.animationDelay = `${index * 80}ms`;
590
 
591
+ const thumbnailsContainer = document.createElement('div');
592
+ thumbnailsContainer.className = 'history-item-thumbnails';
593
+ item.urls.forEach(url => {
594
+ const img = document.createElement('img');
595
+ img.src = url;
596
+ img.alt = 'تصویر کوچک تاریخچه';
597
+ img.addEventListener('click', () => openLightbox(url));
598
+ thumbnailsContainer.appendChild(img);
599
+ });
600
 
601
+ card.appendChild(thumbnailsContainer);
602
+
603
+ card.innerHTML += `
 
604
  <div class="history-item-content">
605
  <p class="history-item-prompt">${item.prompt}</p>
606
+ <div class="history-item-footer">
607
+ <button class="history-btn delete-btn" data-id="${item.id}">
608
+ <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>
609
+ حذف
610
+ </button>
611
  </div>
612
  </div>
613
  `;
614
+ // Re-append the thumbnails container because innerHTML overwrites it
615
+ card.insertBefore(thumbnailsContainer, card.firstChild);
616
 
617
+ card.querySelector('.delete-btn').addEventListener('click', (e) => {
618
+ const idToDelete = Number(e.currentTarget.getAttribute('data-id'));
619
+ const cardElement = e.currentTarget.closest('.history-item');
620
+ cardElement.classList.add('removing');
621
+ setTimeout(() => {
622
+ deleteHistoryItem(idToDelete);
623
+ }, 500); // Wait for animation
624
+ });
625
+
626
  historyGrid.appendChild(card);
627
  });
628
  }
629
+
 
 
 
 
 
 
 
630
  checkFormState();
631
  </script>
632
  </body>