Sada8888 commited on
Commit
a437711
·
verified ·
1 Parent(s): 4778604

Update templates/chat-gemma-4-e4b-it.html

Browse files
Files changed (1) hide show
  1. templates/chat-gemma-4-e4b-it.html +44 -128
templates/chat-gemma-4-e4b-it.html CHANGED
@@ -6,72 +6,44 @@
6
  <title>دستیار هوشمند Gemma-4</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;700&display=swap" rel="stylesheet">
8
  <style>
9
- :root {
10
- --bg-color: #E5DDD5;
11
- --chat-bg: #E5DDD5;
12
- --header-bg: #075E54;
13
- --user-msg: #DCF8C6;
14
- --bot-msg: #FFFFFF;
15
- --text-main: #303030;
16
- --text-muted: #888888;
17
- }
18
-
19
  * { box-sizing: border-box; margin: 0; padding: 0; }
20
  body, html { font-family: 'Vazirmatn', sans-serif; height: 100%; background-color: var(--bg-color); overflow: hidden; }
21
-
22
  .chat-container { display: flex; flex-direction: column; height: 100vh; height: 100dvh; max-width: 800px; margin: 0 auto; background: var(--chat-bg); box-shadow: 0 0 20px rgba(0,0,0,0.1); }
23
-
24
- /* Header */
25
  .chat-header { background: var(--header-bg); color: white; padding: 15px 20px; display: flex; align-items: center; gap: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); z-index: 10; }
26
  .avatar { width: 45px; height: 45px; background: #fff; border-radius: 50%; display: flex; justify-content: center; align-items: center; font-size: 24px; }
27
  .header-info h2 { font-size: 1.1rem; font-weight: 700; margin-bottom: 2px; }
28
  .header-info p { font-size: 0.8rem; color: #d1d1d1; }
29
-
30
- /* Chat Area */
31
  .chat-box { flex-grow: 1; padding: 20px 15px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; background-image: url('https://www.transparenttextures.com/patterns/cubes.png'); }
32
-
33
  .message-row { display: flex; flex-direction: column; max-width: 85%; animation: fadeIn 0.3s ease; }
34
  .message-row.user { align-self: flex-start; }
35
  .message-row.bot { align-self: flex-end; }
36
-
37
  .message-bubble { padding: 12px 15px; border-radius: 18px; font-size: 0.95rem; line-height: 1.6; color: var(--text-main); position: relative; word-wrap: break-word; }
38
  .message-row.user .message-bubble { background: var(--user-msg); border-top-right-radius: 4px; box-shadow: 1px 1px 3px rgba(0,0,0,0.08); }
39
  .message-row.bot .message-bubble { background: var(--bot-msg); border-top-left-radius: 4px; box-shadow: -1px 1px 3px rgba(0,0,0,0.08); }
40
-
41
  .file-preview-in-chat { max-width: 100%; border-radius: 12px; margin-bottom: 8px; }
42
-
43
- /* Typing Indicator */
44
  .typing-indicator { display: none; padding: 12px 15px; background: var(--bot-msg); border-radius: 18px; border-top-left-radius: 4px; align-self: flex-end; box-shadow: -1px 1px 3px rgba(0,0,0,0.08); margin-top: 5px;}
45
  .typing-indicator span { display: inline-block; width: 6px; height: 6px; background-color: #90949C; border-radius: 50%; margin: 0 2px; animation: bounce 1.3s linear infinite; }
46
  .typing-indicator span:nth-child(2) { animation-delay: -1.1s; }
47
  .typing-indicator span:nth-child(3) { animation-delay: -0.9s; }
48
-
49
- /* Input Area */
50
  .input-area { background: #f0f0f0; padding: 10px 15px; display: flex; flex-direction: column; z-index: 10; }
51
-
52
  .attachment-preview { display: none; align-items: center; background: white; padding: 8px 12px; border-radius: 12px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); gap: 10px;}
53
  .attachment-preview img { width: 40px; height: 40px; border-radius: 6px; object-fit: cover; }
54
  .attachment-preview .file-info { flex-grow: 1; font-size: 0.85rem; color: var(--text-main); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
55
  .remove-file { background: #ff4d4d; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; cursor: pointer; display: flex; justify-content: center; align-items: center; font-size: 14px;}
56
-
57
  .input-wrapper { display: flex; align-items: flex-end; gap: 10px; }
58
  .attach-btn { background: transparent; border: none; color: #888; font-size: 24px; cursor: pointer; padding: 5px; transition: color 0.3s; display: flex; align-items: center;}
59
  .attach-btn:hover { color: var(--header-bg); }
60
  #file-input { display: none; }
61
-
62
  textarea { flex-grow: 1; border: none; border-radius: 20px; padding: 12px 15px; font-family: 'Vazirmatn', sans-serif; font-size: 0.95rem; resize: none; max-height: 120px; overflow-y: auto; outline: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
63
-
64
  .send-btn { background: var(--header-bg); color: white; border: none; border-radius: 50%; width: 45px; height: 45px; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: transform 0.2s, background 0.3s; box-shadow: 0 2px 5px rgba(0,0,0,0.2); flex-shrink: 0;}
65
  .send-btn:hover { background: #054c44; transform: scale(1.05); }
66
  .send-btn:disabled { background: #a5c0bd; cursor: not-allowed; transform: none; }
67
  .send-btn svg { width: 20px; height: 20px; transform: translateX(-2px); }
68
-
69
  @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
70
  @keyframes bounce { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-5px); } }
71
-
72
- /* مارک‌داون ساده برای پیام‌ها */
73
  .message-bubble b, .message-bubble strong { font-weight: 700; }
74
- .message-bubble code { background: #f4f4f4; padding: 2px 5px; border-radius: 4px; font-family: monospace; font-size: 0.9rem; }
75
  </style>
76
  </head>
77
  <body>
@@ -110,9 +82,7 @@
110
  <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.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>
111
  </button>
112
  <input type="file" id="file-input" accept="image/*,audio/*,video/*">
113
-
114
  <textarea id="text-input" rows="1" placeholder="پیام خود را بنویسید..."></textarea>
115
-
116
  <button class="send-btn" id="send-btn">
117
  <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"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
118
  </button>
@@ -129,7 +99,6 @@
129
  const typingIndicator = document.getElementById('typing-indicator');
130
  const statusText = document.getElementById('status-text');
131
 
132
- // Attachment UI
133
  const attachmentPreview = document.getElementById('attachment-preview');
134
  const previewImg = document.getElementById('preview-img');
135
  const fileIcon = document.getElementById('file-icon');
@@ -137,15 +106,12 @@
137
  const removeFileBtn = document.getElementById('remove-file-btn');
138
 
139
  let currentFile = null;
140
- let pollTimer = null;
141
 
142
- // تنظیم ارتفاع اتوماتیک Textarea
143
  textInput.addEventListener('input', function() {
144
  this.style.height = 'auto';
145
  this.style.height = (this.scrollHeight < 120 ? this.scrollHeight : 120) + 'px';
146
  });
147
 
148
- // مدیریت فایل
149
  attachBtn.addEventListener('click', () => fileInput.click());
150
 
151
  fileInput.addEventListener('change', function() {
@@ -174,7 +140,6 @@
174
  if (previewImg.src) URL.revokeObjectURL(previewImg.src);
175
  });
176
 
177
- // اضافه کردن پیام به صفحه
178
  function appendMessage(text, isUser, fileUrl = null, isImage = false) {
179
  const row = document.createElement('div');
180
  row.className = `message-row ${isUser ? 'user' : 'bot'}`;
@@ -185,61 +150,17 @@
185
  else mediaHtml = `<div style="background:rgba(0,0,0,0.1); padding:8px; border-radius:8px; margin-bottom:8px;">📎 فایل ضمیمه شد</div>`;
186
  }
187
 
188
- // تبدیل متن به فرمت خوانا (بولد کردن، خطوط جدید)
189
  const formattedText = text.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>').replace(/\n/g, '<br>');
190
-
191
  row.innerHTML = `<div class="message-bubble">${mediaHtml}${formattedText}</div>`;
192
  chatBox.appendChild(row);
193
  chatBox.scrollTop = chatBox.scrollHeight;
194
- return row.querySelector('.message-bubble'); // برای استفاده در استریم
195
  }
196
 
197
- // شبیه‌سازی افکت استریم (تایپ شدن)
198
- function streamTextEffect(bubbleElement, fullText) {
199
- bubbleElement.innerHTML = '';
200
- const formattedText = fullText.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>').replace(/\n/g, '<br>');
201
-
202
- // تقسیم متن بر اساس تگ‌های HTML تا تگ‌ها تایپ نشوند
203
- const parts = formattedText.split(/(<[^>]*>)/);
204
- let i = 0;
205
-
206
- function typePart() {
207
- if (i < parts.length) {
208
- const part = parts[i];
209
- if (part.startsWith('<')) {
210
- // اگر تگ HTML است، بلافاصله اضافه کن
211
- bubbleElement.innerHTML += part;
212
- i++;
213
- typePart();
214
- } else {
215
- // اگر متن است، کاراکتر به کاراکتر تایپ کن
216
- let charIndex = 0;
217
- function typeChar() {
218
- if (charIndex < part.length) {
219
- bubbleElement.innerHTML += part.charAt(charIndex);
220
- charIndex++;
221
- chatBox.scrollTop = chatBox.scrollHeight;
222
- setTimeout(typeChar, 15); // سرعت تایپ
223
- } else {
224
- i++;
225
- typePart();
226
- }
227
- }
228
- typeChar();
229
- }
230
- } else {
231
- statusText.textContent = "آماده پاسخگویی به شما";
232
- }
233
- }
234
- typePart();
235
- }
236
-
237
- // ارسال درخواست
238
  sendBtn.addEventListener('click', async () => {
239
  const text = textInput.value.trim();
240
  if (!text && !currentFile) return;
241
 
242
- // نمایش پیام کاربر
243
  let fileUrlForUI = null;
244
  let isImage = false;
245
  if (currentFile) {
@@ -248,7 +169,6 @@
248
  }
249
  appendMessage(text, true, fileUrlForUI, isImage);
250
 
251
- // ریست کردن ورودی‌ها
252
  const fileToSend = currentFile;
253
  const textToSend = text;
254
 
@@ -256,80 +176,76 @@
256
  textInput.style.height = 'auto';
257
  removeFileBtn.click();
258
 
259
- // قفل کردن فرم
260
  sendBtn.disabled = true;
261
  textInput.disabled = true;
262
  attachBtn.disabled = true;
263
  typingIndicator.style.display = 'block';
264
  chatBox.scrollTop = chatBox.scrollHeight;
265
- statusText.textContent = "در حال تفکر...";
266
 
267
  try {
268
  const formData = new FormData();
269
  formData.append('prompt', textToSend);
270
  if (fileToSend) formData.append('file', fileToSend);
271
 
272
- // ارسال به Flask App شما
273
- const response = await fetch('/api/chat', {
274
- method: 'POST',
275
- body: formData
276
- });
277
-
278
  const data = await response.json();
279
 
280
  if (data.status === 'success') {
281
- statusText.textContent = "سرور ابری در حال پردازش...";
 
 
 
282
 
283
- // پیگیری وضعیت از گیت هاب
284
- pollTimer = setInterval(async () => {
285
- try {
286
- const statusRes = await fetch(`/api/status/${data.run_id}`);
287
- const statusData = await statusRes.json();
288
-
289
- if (statusData.status === 'ready' && statusData.url.endsWith('.txt')) {
290
- clearInterval(pollTimer);
291
-
292
- // دریافت متن پاسخ
293
- const txtRes = await fetch(statusData.url);
294
- const botReplyText = await txtRes.text();
295
-
296
  typingIndicator.style.display = 'none';
297
  statusText.textContent = "در حال نوشتن...";
298
-
299
- // ساخت حباب پیام ربات خالی و استریم کردن متن داخل آن
300
- const botBubble = appendMessage('', false);
301
- streamTextEffect(botBubble, botReplyText);
302
-
303
- // باز کردن قفل فرم
304
- sendBtn.disabled = false;
305
- textInput.disabled = false;
306
- attachBtn.disabled = false;
307
- textInput.focus();
308
  }
309
- } catch (e) {
310
- console.error('Polling error', e);
 
 
 
 
311
  }
312
- }, 5000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  } else {
314
- throw new Error("خطا در ارسال به سرور");
315
  }
316
  } catch (error) {
317
  typingIndicator.style.display = 'none';
318
  statusText.textContent = "آماده پاسخگویی به شما";
319
- appendMessage(`خطا در ارتباط: ${error.message}`, false);
320
- sendBtn.disabled = false;
321
- textInput.disabled = false;
322
- attachBtn.disabled = false;
323
  }
324
  });
325
 
326
  textInput.addEventListener('keypress', function (e) {
327
- if (e.key === 'Enter' && !e.shiftKey) {
328
- e.preventDefault();
329
- sendBtn.click();
330
- }
331
  });
332
  </script>
333
-
334
  </body>
335
  </html>
 
6
  <title>دستیار هوشمند Gemma-4</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;700&display=swap" rel="stylesheet">
8
  <style>
9
+ :root { --bg-color: #E5DDD5; --chat-bg: #E5DDD5; --header-bg: #075E54; --user-msg: #DCF8C6; --bot-msg: #FFFFFF; --text-main: #303030; }
 
 
 
 
 
 
 
 
 
10
  * { box-sizing: border-box; margin: 0; padding: 0; }
11
  body, html { font-family: 'Vazirmatn', sans-serif; height: 100%; background-color: var(--bg-color); overflow: hidden; }
 
12
  .chat-container { display: flex; flex-direction: column; height: 100vh; height: 100dvh; max-width: 800px; margin: 0 auto; background: var(--chat-bg); box-shadow: 0 0 20px rgba(0,0,0,0.1); }
 
 
13
  .chat-header { background: var(--header-bg); color: white; padding: 15px 20px; display: flex; align-items: center; gap: 15px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); z-index: 10; }
14
  .avatar { width: 45px; height: 45px; background: #fff; border-radius: 50%; display: flex; justify-content: center; align-items: center; font-size: 24px; }
15
  .header-info h2 { font-size: 1.1rem; font-weight: 700; margin-bottom: 2px; }
16
  .header-info p { font-size: 0.8rem; color: #d1d1d1; }
 
 
17
  .chat-box { flex-grow: 1; padding: 20px 15px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; background-image: url('https://www.transparenttextures.com/patterns/cubes.png'); }
 
18
  .message-row { display: flex; flex-direction: column; max-width: 85%; animation: fadeIn 0.3s ease; }
19
  .message-row.user { align-self: flex-start; }
20
  .message-row.bot { align-self: flex-end; }
 
21
  .message-bubble { padding: 12px 15px; border-radius: 18px; font-size: 0.95rem; line-height: 1.6; color: var(--text-main); position: relative; word-wrap: break-word; }
22
  .message-row.user .message-bubble { background: var(--user-msg); border-top-right-radius: 4px; box-shadow: 1px 1px 3px rgba(0,0,0,0.08); }
23
  .message-row.bot .message-bubble { background: var(--bot-msg); border-top-left-radius: 4px; box-shadow: -1px 1px 3px rgba(0,0,0,0.08); }
 
24
  .file-preview-in-chat { max-width: 100%; border-radius: 12px; margin-bottom: 8px; }
 
 
25
  .typing-indicator { display: none; padding: 12px 15px; background: var(--bot-msg); border-radius: 18px; border-top-left-radius: 4px; align-self: flex-end; box-shadow: -1px 1px 3px rgba(0,0,0,0.08); margin-top: 5px;}
26
  .typing-indicator span { display: inline-block; width: 6px; height: 6px; background-color: #90949C; border-radius: 50%; margin: 0 2px; animation: bounce 1.3s linear infinite; }
27
  .typing-indicator span:nth-child(2) { animation-delay: -1.1s; }
28
  .typing-indicator span:nth-child(3) { animation-delay: -0.9s; }
 
 
29
  .input-area { background: #f0f0f0; padding: 10px 15px; display: flex; flex-direction: column; z-index: 10; }
 
30
  .attachment-preview { display: none; align-items: center; background: white; padding: 8px 12px; border-radius: 12px; margin-bottom: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); gap: 10px;}
31
  .attachment-preview img { width: 40px; height: 40px; border-radius: 6px; object-fit: cover; }
32
  .attachment-preview .file-info { flex-grow: 1; font-size: 0.85rem; color: var(--text-main); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
33
  .remove-file { background: #ff4d4d; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; cursor: pointer; display: flex; justify-content: center; align-items: center; font-size: 14px;}
 
34
  .input-wrapper { display: flex; align-items: flex-end; gap: 10px; }
35
  .attach-btn { background: transparent; border: none; color: #888; font-size: 24px; cursor: pointer; padding: 5px; transition: color 0.3s; display: flex; align-items: center;}
36
  .attach-btn:hover { color: var(--header-bg); }
37
  #file-input { display: none; }
 
38
  textarea { flex-grow: 1; border: none; border-radius: 20px; padding: 12px 15px; font-family: 'Vazirmatn', sans-serif; font-size: 0.95rem; resize: none; max-height: 120px; overflow-y: auto; outline: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
 
39
  .send-btn { background: var(--header-bg); color: white; border: none; border-radius: 50%; width: 45px; height: 45px; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: transform 0.2s, background 0.3s; box-shadow: 0 2px 5px rgba(0,0,0,0.2); flex-shrink: 0;}
40
  .send-btn:hover { background: #054c44; transform: scale(1.05); }
41
  .send-btn:disabled { background: #a5c0bd; cursor: not-allowed; transform: none; }
42
  .send-btn svg { width: 20px; height: 20px; transform: translateX(-2px); }
 
43
  @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
44
  @keyframes bounce { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-5px); } }
 
 
45
  .message-bubble b, .message-bubble strong { font-weight: 700; }
46
+ .message-bubble code { background: #f4f4f4; padding: 2px 5px; border-radius: 4px; font-family: monospace; font-size: 0.9rem; direction: ltr; display: inline-block; white-space: pre-wrap; word-wrap: break-word; max-width: 100%;}
47
  </style>
48
  </head>
49
  <body>
 
82
  <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.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>
83
  </button>
84
  <input type="file" id="file-input" accept="image/*,audio/*,video/*">
 
85
  <textarea id="text-input" rows="1" placeholder="پیام خود را بنویسید..."></textarea>
 
86
  <button class="send-btn" id="send-btn">
87
  <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"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
88
  </button>
 
99
  const typingIndicator = document.getElementById('typing-indicator');
100
  const statusText = document.getElementById('status-text');
101
 
 
102
  const attachmentPreview = document.getElementById('attachment-preview');
103
  const previewImg = document.getElementById('preview-img');
104
  const fileIcon = document.getElementById('file-icon');
 
106
  const removeFileBtn = document.getElementById('remove-file-btn');
107
 
108
  let currentFile = null;
 
109
 
 
110
  textInput.addEventListener('input', function() {
111
  this.style.height = 'auto';
112
  this.style.height = (this.scrollHeight < 120 ? this.scrollHeight : 120) + 'px';
113
  });
114
 
 
115
  attachBtn.addEventListener('click', () => fileInput.click());
116
 
117
  fileInput.addEventListener('change', function() {
 
140
  if (previewImg.src) URL.revokeObjectURL(previewImg.src);
141
  });
142
 
 
143
  function appendMessage(text, isUser, fileUrl = null, isImage = false) {
144
  const row = document.createElement('div');
145
  row.className = `message-row ${isUser ? 'user' : 'bot'}`;
 
150
  else mediaHtml = `<div style="background:rgba(0,0,0,0.1); padding:8px; border-radius:8px; margin-bottom:8px;">📎 فایل ضمیمه شد</div>`;
151
  }
152
 
 
153
  const formattedText = text.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>').replace(/\n/g, '<br>');
 
154
  row.innerHTML = `<div class="message-bubble">${mediaHtml}${formattedText}</div>`;
155
  chatBox.appendChild(row);
156
  chatBox.scrollTop = chatBox.scrollHeight;
157
+ return row.querySelector('.message-bubble');
158
  }
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  sendBtn.addEventListener('click', async () => {
161
  const text = textInput.value.trim();
162
  if (!text && !currentFile) return;
163
 
 
164
  let fileUrlForUI = null;
165
  let isImage = false;
166
  if (currentFile) {
 
169
  }
170
  appendMessage(text, true, fileUrlForUI, isImage);
171
 
 
172
  const fileToSend = currentFile;
173
  const textToSend = text;
174
 
 
176
  textInput.style.height = 'auto';
177
  removeFileBtn.click();
178
 
 
179
  sendBtn.disabled = true;
180
  textInput.disabled = true;
181
  attachBtn.disabled = true;
182
  typingIndicator.style.display = 'block';
183
  chatBox.scrollTop = chatBox.scrollHeight;
184
+ statusText.textContent = "در حال اتصال به هوش مصنوعی...";
185
 
186
  try {
187
  const formData = new FormData();
188
  formData.append('prompt', textToSend);
189
  if (fileToSend) formData.append('file', fileToSend);
190
 
191
+ const response = await fetch('/api/chat', { method: 'POST', body: formData });
 
 
 
 
 
192
  const data = await response.json();
193
 
194
  if (data.status === 'success') {
195
+ const botBubble = appendMessage('', false);
196
+ let isFirstChunk = true;
197
+
198
+ const eventSource = new EventSource(`/api/stream_chat/${data.run_id}`);
199
 
200
+ eventSource.onmessage = function(event) {
201
+ if (event.data === "DONE") {
202
+ eventSource.close();
203
+ statusText.textContent = "آماده پاسخگویی به شما";
204
+ sendBtn.disabled = false; textInput.disabled = false; attachBtn.disabled = false;
205
+ textInput.focus();
206
+ } else {
207
+ if (isFirstChunk) {
 
 
 
 
 
208
  typingIndicator.style.display = 'none';
209
  statusText.textContent = "در حال نوشتن...";
210
+ isFirstChunk = false;
 
 
 
 
 
 
 
 
 
211
  }
212
+ try {
213
+ const parsed = JSON.parse(event.data);
214
+ const formattedText = parsed.text.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>').replace(/\n/g, '<br>');
215
+ botBubble.innerHTML = formattedText;
216
+ chatBox.scrollTop = chatBox.scrollHeight;
217
+ } catch (e) {}
218
  }
219
+ };
220
+
221
+ eventSource.onerror = function() {
222
+ eventSource.close();
223
+ typingIndicator.style.display = 'none';
224
+ statusText.textContent = "آماده پاسخگویی به شما";
225
+
226
+ // بررسی اینکه اگر کادر خالی بود خطای قطع شدن بنویسد
227
+ if (botBubble.innerHTML === '' || botBubble.innerHTML === '<br>') {
228
+ botBubble.innerHTML = "⚠️ خطای شبکه: اتصال استریم با سرور قطع شد.";
229
+ } else {
230
+ botBubble.innerHTML += "<br><br>⚠️ <i>(ارتباط استریم با سرور قطع شد)</i>";
231
+ }
232
+
233
+ sendBtn.disabled = false; textInput.disabled = false; attachBtn.disabled = false;
234
+ };
235
  } else {
236
+ throw new Error(data.message || "خطای ناشناخته از سمت سرور");
237
  }
238
  } catch (error) {
239
  typingIndicator.style.display = 'none';
240
  statusText.textContent = "آماده پاسخگویی به شما";
241
+ appendMessage(`⚠️ خطا: ${error.message}`, false);
242
+ sendBtn.disabled = false; textInput.disabled = false; attachBtn.disabled = false;
 
 
243
  }
244
  });
245
 
246
  textInput.addEventListener('keypress', function (e) {
247
+ if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendBtn.click(); }
 
 
 
248
  });
249
  </script>
 
250
  </body>
251
  </html>