AnesKAM commited on
Commit
ba016d3
ยท
verified ยท
1 Parent(s): af5d2e2

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +88 -34
index.html CHANGED
@@ -2,16 +2,17 @@
2
  <html lang="en" dir="ltr">
3
  <head>
4
  <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Genisi AI</title>
 
7
  <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"/>
8
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
9
- <!-- ุฅุถุงูุฉ DOMPurify ู„ู„ุญู…ุงูŠุฉ ู…ู† XSS -->
10
  <script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
11
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css"/>
12
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js"></script>
13
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js"></script>
14
  <style>
 
15
  :root {
16
  --bg: #0d0f14; --surface: #161920; --surface2: #1e2230; --surface3: #252a3a;
17
  --border: #2a2f42; --border2: #333a52;
@@ -106,7 +107,6 @@
106
  .msg-row.user .bubble{background:var(--user-bubble);border:none; border-top-right-radius:6px}
107
  [dir="rtl"] .msg-row.user .bubble { border-top-right-radius:var(--radius); border-top-left-radius:6px; }
108
 
109
- /* ุชุญุณูŠู† ุนุฑุถ ุงู„ุตูˆุฑ ุฏุงุฎู„ ุงู„ูู‚ุงุนุฉ */
110
  .bubble img {
111
  max-width: 100%;
112
  height: auto;
@@ -117,15 +117,11 @@
117
  transition: transform 0.2s;
118
  border: 1px solid var(--border);
119
  }
120
- .bubble img:active {
121
- transform: scale(1.02);
122
- }
123
 
124
- /* GEMINI STREAMING EFFECT */
125
  .gemini-cursor { display: inline-block; width: 14px; height: 14px; margin-inline-start: 6px; background: linear-gradient(135deg, var(--accent), var(--accent2)); mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 0l2.5 9.5L24 12l-9.5 2.5L12 24l-2.5-9.5L0 12l9.5-2.5z"/></svg>'); -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 0l2.5 9.5L24 12l-9.5 2.5L12 24l-2.5-9.5L0 12l9.5-2.5z"/></svg>'); mask-size: cover; -webkit-mask-size: cover; animation: spinPulse 1s infinite linear; transform-origin: center; vertical-align: middle; }
126
  @keyframes spinPulse { 0% { transform: scale(0.8) rotate(0deg); opacity: 0.5; } 50% { transform: scale(1.2) rotate(90deg); opacity: 1; filter: drop-shadow(0 0 5px var(--accent)); } 100% { transform: scale(0.8) rotate(180deg); opacity: 0.5; } }
127
 
128
- /* IMAGE SKELETON LOADING (ุชุฃุซูŠุฑ ุงู„ุชุฏุฑุฌ ู„ุชูˆู„ูŠุฏ ุงู„ุตูˆุฑ) */
129
  .skeleton-img {
130
  width: 100%; max-width: 400px; height: 350px;
131
  margin: 15px auto; border-radius: 18px;
@@ -159,7 +155,7 @@
159
  .code-container pre code{white-space:pre;display:block;}
160
  .bubble code:not(pre code){font-family:'JetBrains Mono',monospace;font-size:.85rem;background:var(--surface3);padding:3px 6px;border-radius:6px;color:var(--accent);}
161
 
162
- /* KaTeX Math Styles */
163
  .katex-display{overflow-x:auto;overflow-y:hidden;padding:8px 0;-webkit-overflow-scrolling:touch;}
164
  .katex{font-size:1.05em;}
165
 
@@ -192,7 +188,6 @@
192
  .btn-send:hover{opacity:.9;transform:scale(1.08) rotate(-10deg);}[dir="rtl"] .btn-send:hover{transform:scale(1.08) rotate(10deg);}
193
  .btn-send:disabled{opacity:.4;cursor:not-allowed;transform:none;}
194
 
195
- /* Stop Button Styles */
196
  .btn-stop {background:var(--surface3); color:var(--text); border:1px solid var(--border);}
197
  .btn-stop:hover {background:var(--danger); color:#fff; transform:scale(1.05);}
198
 
@@ -228,7 +223,7 @@
228
  color:var(--danger);border-radius:var(--radius-pill);cursor:pointer;font-size:.9rem;font-weight:600;font-family:'Cairo',sans-serif; transition:all var(--tr);}
229
  .btn-danger:hover{background:var(--danger); color:#fff;}
230
 
231
- /* โ•โ• RESPONSIVE MOBILE โ•โ• */
232
  @media (max-width: 640px) {
233
  .msg-row { padding: 6px 12px; gap: 10px; }
234
  .bubble { max-width: 92vw; padding: 12px 14px; font-size: .93rem; }
@@ -240,13 +235,41 @@
240
  .w-title { font-size: 1.8rem; }
241
  .chips { gap: 8px; }
242
  .chip { font-size: .82rem; padding: 8px 14px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  }
244
  </style>
245
  </head>
246
  <body>
247
  <div class="app">
248
 
249
- <aside class="sidebar" id="sidebar">
 
250
  <div class="sidebar-header">
251
  <div class="s-logo">
252
  <img src="https://copilot.microsoft.com/th/id/BCO.f29916dd-b0c1-4089-87cd-43d099a7d1a6.png" alt="Genisi"/>
@@ -264,7 +287,8 @@
264
 
265
  <main class="main">
266
  <div class="topbar">
267
- <button class="btn-icon" onclick="toggleSidebar()">โ˜ฐ</button>
 
268
  <div class="topbar-title" id="topbar-title">Genisi AI</div>
269
  </div>
270
 
@@ -285,7 +309,7 @@
285
  </main>
286
  </div>
287
 
288
- <!-- SETTINGS MODAL -->
289
  <div class="overlay" id="settings-modal">
290
  <div class="modal">
291
  <div class="m-header">
@@ -320,7 +344,7 @@
320
  </div>
321
 
322
  <script>
323
- // โ•โ•โ•โ•โ•โ•โ•โ• i18n (DICTIONARY) โ•โ•โ•โ•โ•โ•โ•โ•
324
  const i18n = {
325
  en: { newChat: "โœฆ New Chat", chatsLabel: "CHATS", settings: "Settings", lang: "Language", dark: "Dark Mode", del: "Delete All Chats", delDesc: "This action cannot be undone", delBtn: "๐Ÿ—‘ Delete", saveBtn: "๐Ÿ’พ Close", placeholder: "Type your message... (Enter to send)", welcomeTitle: "Welcome to Genisi", welcomeSub: "Your smart assistant by AnesNT โ€” Batna ๐Ÿ‡ฉ๐Ÿ‡ฟ", c1: "What is AI?", c2: "Write Python code", c3: "Summarize a topic", c4: "Design an image in space ๐Ÿš€", errConnect: "Connection Error", memBadge: "Memory: {c}/{t} messages", stopMsg: "[Generation stopped by user]" },
326
  ar: { newChat: "โœฆ ู…ุญุงุฏุซุฉ ุฌุฏูŠุฏุฉ", chatsLabel: "ุงู„ู…ุญุงุฏุซุงุช", settings: "ุงู„ุฅุนุฏุงุฏุงุช", lang: "ุงู„ู„ุบุฉ", dark: "ุงู„ูˆุถุน ุงู„ู„ูŠู„ูŠ", del: "ุญุฐู ุฌู…ูŠุน ุงู„ู…ุญุงุฏุซุงุช", delDesc: "ู„ุง ูŠู…ูƒู† ุงู„ุชุฑุงุฌุน ุนู† ู‡ุฐุง ุงู„ุฅุฌุฑุงุก", delBtn: "๐Ÿ—‘ ุญุฐู", saveBtn: "๐Ÿ’พ ุฅุบู„ุงู‚", placeholder: "ุงูƒุชุจ ุฑุณุงู„ุชูƒ... (Enter ู„ู„ุฅุฑุณุงู„)", welcomeTitle: "ู…ุฑุญุจู‹ุง ููŠ Genisi", welcomeSub: "ู…ุณุงุนุฏูƒ ุงู„ุฐูƒูŠ ู…ู† AnesNT โ€” ูˆู„ุงูŠุฉ ุจุงุชู†ุฉ ๐Ÿ‡ฉ๐Ÿ‡ฟ", c1: "ู…ุง ู‡ูˆ ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠุŸ", c2: "ุงูƒุชุจ ูƒูˆุฏ ุจุงูŠุซูˆู†", c3: "ู„ุฎุต ู„ูŠ ู…ูˆุถูˆุนุงู‹", c4: "ุตู…ู… ุตูˆุฑุฉ ุจุงู„ูุถุงุก ๐Ÿš€", errConnect: "ุฎุทุฃ ููŠ ุงู„ุงุชุตุงู„", memBadge: "ุงู„ุฐุงูƒุฑุฉ: {c}/{t} ุฑุณุงุฆู„", stopMsg: "[ุชู… ุฅูŠู‚ุงู ุงู„ุชูˆู„ูŠุฏ]" },
@@ -351,7 +375,7 @@ function applyI18n() {
351
  }
352
  function changeLanguage(val) { currentLang = val; localStorage.setItem('genisi_lang', val); applyI18n(); }
353
 
354
- // โ•โ•โ•โ•โ•โ•โ•โ• STATE & CONFIG โ•โ•โ•โ•โ•โ•โ•โ•
355
  const genId = () => Date.now().toString(36) + Math.random().toString(36).substring(2, 6);
356
 
357
  let userId = localStorage.getItem('genisi_user_id');
@@ -366,7 +390,7 @@ let isGenerating = false;
366
  let pendingFiles =[];
367
  let currentAbortController = null;
368
 
369
- // โ•โ•โ•โ•โ•โ•โ•โ• MARKDOWN PARSER + DOMPurify (ุขู…ู†) โ•โ•โ•โ•โ•โ•โ•โ•
370
  const renderer = new marked.Renderer();
371
  renderer.code = function(token) {
372
  const codeText = typeof token === 'string' ? token : token.text || '';
@@ -393,7 +417,6 @@ renderer.table = function(header, body) {
393
  renderer.html = function(html) { return html; };
394
  marked.setOptions({ renderer: renderer, breaks: true, gfm: true });
395
 
396
- // ุฏุงู„ุฉ ู…ุณุงุนุฏุฉ: ุชุญูˆูŠู„ Markdown ุฅู„ู‰ HTML ุขู…ู† ุจุงุณุชุฎุฏุงู… DOMPurify
397
  function safeMarked(text) {
398
  if (!text) return '';
399
  const rawHtml = marked.parse(text);
@@ -422,12 +445,11 @@ function copyCode(btn, encodedCode) {
422
  });
423
  }
424
 
425
- // ุฅุฒุงู„ุฉ ู…ุคุดุฑ Gemini ูŠุฏูˆูŠู‹ุง (ูŠูุณุชุฏุนู‰ ุนู†ุฏ ุงู„ุฅูŠู‚ุงู ุฃูˆ ุงู„ุฎุทุฃ)
426
  function removeGeminiCursor() {
427
  document.querySelectorAll('.gemini-cursor').forEach(el => el.remove());
428
  }
429
 
430
- // โ•โ•โ•โ•โ•โ•โ•โ• UTILS & FILES โ•โ•โ•โ•โ•โ•โ•โ•
431
  const saveChats = () => localStorage.setItem('genisi_chats', JSON.stringify(chats));
432
  const getChat = (id) => chats.find(c => c.id === (id ?? activeChatId));
433
  function esc(t){ return String(t).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
@@ -463,7 +485,7 @@ function renderFileChips() {
463
  }
464
  function removeFile(idx) { pendingFiles.splice(idx, 1); renderFileChips(); }
465
 
466
- // โ•โ•โ•โ•โ•โ•โ•โ• THEME & UI โ•โ•โ•โ•โ•โ•โ•โ•
467
  function applyTheme(t){
468
  document.documentElement.setAttribute('data-theme',t);
469
  localStorage.setItem('genisi_theme',t);
@@ -473,7 +495,47 @@ function applyTheme(t){
473
  function toggleTheme(){ applyTheme(document.documentElement.getAttribute('data-theme')==='dark'?'light':'dark'); }
474
  function applyThemeToggle(){ applyTheme(document.getElementById('dark-toggle').checked?'dark':'light'); }
475
 
476
- function toggleSidebar(){ document.getElementById('sidebar').classList.toggle('collapsed'); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  function openSettings(){ document.getElementById('settings-modal').classList.add('open'); document.getElementById('lang-select').value=currentLang;}
478
  function closeSettings(){ document.getElementById('settings-modal').classList.remove('open'); }
479
 
@@ -518,7 +580,6 @@ function loadChat(id){
518
  chat.history.forEach((entry, idx)=>{
519
  appendBubble('user', entry.user, false, entry.uiFiles);
520
  appendBubble('bot', entry.bot, true);
521
- // ู„ุง ู†ุถูŠู mem-badge ู„ูƒู„ ุฑุณุงู„ุฉ ุจู„ ู†ุถูŠู ูˆุงุญุฏุฉ ููŠ ุงู„ู†ู‡ุงูŠุฉ ูู‚ุท
522
  });
523
  if (chat.history.length) {
524
  appendMemBadge(chat.history.length, chat.history.length);
@@ -533,18 +594,17 @@ function deleteChat(id,e){
533
  }
534
  function clearAllChats(){ chats=[]; saveChats(); newChat(); closeSettings(); }
535
 
536
- // โ•โ•โ•โ•โ•โ•โ•โ• STOP GENERATION โ•โ•โ•โ•โ•โ•โ•โ•
537
  function stopGeneration() {
538
  if (currentAbortController) {
539
  currentAbortController.abort();
540
  }
541
  }
542
 
543
- // โ•โ•โ•โ•โ•โ•โ•โ• MESSAGING & STREAMING โ•โ•โ•โ•โ•โ•โ•โ•
544
  function quickSend(text){ document.getElementById('msg-input').value=text; sendMessage(); }
545
  function handleKey(e){ if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendMessage();} }
546
  function autoResize(el){ el.style.height='auto'; el.style.height=Math.min(el.scrollHeight,160)+'px'; }
547
 
 
548
  async function sendMessage(){
549
  if(isGenerating) return;
550
  const input = document.getElementById('msg-input');
@@ -608,15 +668,12 @@ async function sendMessage(){
608
  const chunk = decoder.decode(value, {stream: true});
609
  fullBotResponse += chunk;
610
 
611
- // ุชุญูˆูŠู„ ุงู„ุฑุฏ ุฅู„ู‰ HTML ุขู…ู†
612
  let safeHtml = safeMarked(fullBotResponse);
613
  botContentDiv.innerHTML = safeHtml;
614
 
615
- // ุฅุฐุง ูƒุงู† ุงู„ุทู„ุจ ู„ุตูˆุฑุฉ ูˆู„ู… ุชุธู‡ุฑ ุจุนุฏุŒ ุฃุถู skeleton
616
  if (isImageRequest && !botContentDiv.querySelector('img')) {
617
  botContentDiv.innerHTML += '<div class="skeleton-img">๐ŸŽจ ุฌุงุฑูŠ ุงู„ุชุฎูŠู„ ูˆุงู„ุชุตู…ูŠู…...</div>';
618
  } else {
619
- // ุฅุฐุง ู„ู… ุชูƒู† ุตูˆุฑุฉุŒ ู†ุถูŠู ู…ุคุดุฑ Gemini ูู‚ุท
620
  botContentDiv.innerHTML += '<span class="gemini-cursor"></span>';
621
  }
622
 
@@ -624,7 +681,6 @@ async function sendMessage(){
624
  scrollToBottom();
625
  }
626
 
627
- // ุนู†ุฏ ุงู„ุงู†ุชู‡ุงุก: ุนุฑุถ ุงู„ู…ุญุชูˆู‰ ุงู„ู†ู‡ุงุฆูŠ ุจุฏูˆู† ู…ุคุดุฑ
628
  let finalHtml = safeMarked(fullBotResponse);
629
  botContentDiv.innerHTML = finalHtml;
630
  renderKaTeX(botContentDiv);
@@ -670,7 +726,6 @@ function appendBubble(role, text, isMarkdown, filesList =[]){
670
 
671
  const contentDiv = document.createElement('div');
672
  if(text) {
673
- // ุงุณุชุฎุฏุงู… safeMarked ู„ู„ู…ุญุชูˆู‰ Markdown
674
  contentDiv.innerHTML = isMarkdown ? safeMarked(text) : esc(text).replace(/\n/g, '<br/>');
675
  if (isMarkdown) renderKaTeX(contentDiv);
676
  }
@@ -679,7 +734,6 @@ function appendBubble(role, text, isMarkdown, filesList =[]){
679
  row.appendChild(av); row.appendChild(bub);
680
  area.appendChild(row);
681
 
682
- // ุฅุฐุง ูƒุงู†ุช ๏ฟฝ๏ฟฝู„ุฑุณุงู„ุฉ ู…ู† ุงู„ุจูˆุช ูˆุชุญุชูˆูŠ ุนู„ู‰ ุตูˆุฑุŒ ู†ู†ุชุธุฑ ุชุญู…ูŠู„ู‡ุง ุซู… ู†ู…ุฑุฑ
683
  if (role === 'bot') {
684
  const images = bub.querySelectorAll('img');
685
  if (images.length) {
@@ -705,12 +759,12 @@ function appendMemBadge(count, total){
705
  scrollToBottom();
706
  }
707
 
708
- // โ•โ•โ•โ•โ•โ•โ•โ• INIT โ•โ•โ•โ•โ•โ•โ•โ•
709
  applyTheme(localStorage.getItem('genisi_theme')||'dark');
710
  document.getElementById('lang-select').value = currentLang;
711
  applyI18n();
 
712
 
713
- // ุงู„ุชุฃูƒุฏ ู…ู† ุฃู† DOMPurify ู…ุชุงุญ
714
  if (typeof DOMPurify === 'undefined') {
715
  console.warn("DOMPurify not loaded - XSS protection disabled");
716
  }
 
2
  <html lang="en" dir="ltr">
3
  <head>
4
  <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
6
+ <title>Genisi AI ยท Hidden Sidebar</title>
7
+ <!-- ุงู„ุฎุทูˆุท ูˆุงู„ู…ูƒุชุจุงุช (ุจุฏูˆู† ุชุบูŠูŠุฑ) -->
8
  <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"/>
9
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
 
10
  <script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
11
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css"/>
12
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js"></script>
13
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js"></script>
14
  <style>
15
+ /* ========== ุงู„ู…ุชุบูŠุฑุงุช ูˆุงู„ุชู†ุณูŠู‚ุงุช ุงู„ุฃุณุงุณูŠุฉ (ุจุฏูˆู† ุชุบูŠูŠุฑ) ========== */
16
  :root {
17
  --bg: #0d0f14; --surface: #161920; --surface2: #1e2230; --surface3: #252a3a;
18
  --border: #2a2f42; --border2: #333a52;
 
107
  .msg-row.user .bubble{background:var(--user-bubble);border:none; border-top-right-radius:6px}
108
  [dir="rtl"] .msg-row.user .bubble { border-top-right-radius:var(--radius); border-top-left-radius:6px; }
109
 
 
110
  .bubble img {
111
  max-width: 100%;
112
  height: auto;
 
117
  transition: transform 0.2s;
118
  border: 1px solid var(--border);
119
  }
120
+ .bubble img:active { transform: scale(1.02); }
 
 
121
 
 
122
  .gemini-cursor { display: inline-block; width: 14px; height: 14px; margin-inline-start: 6px; background: linear-gradient(135deg, var(--accent), var(--accent2)); mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 0l2.5 9.5L24 12l-9.5 2.5L12 24l-2.5-9.5L0 12l9.5-2.5z"/></svg>'); -webkit-mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 0l2.5 9.5L24 12l-9.5 2.5L12 24l-2.5-9.5L0 12l9.5-2.5z"/></svg>'); mask-size: cover; -webkit-mask-size: cover; animation: spinPulse 1s infinite linear; transform-origin: center; vertical-align: middle; }
123
  @keyframes spinPulse { 0% { transform: scale(0.8) rotate(0deg); opacity: 0.5; } 50% { transform: scale(1.2) rotate(90deg); opacity: 1; filter: drop-shadow(0 0 5px var(--accent)); } 100% { transform: scale(0.8) rotate(180deg); opacity: 0.5; } }
124
 
 
125
  .skeleton-img {
126
  width: 100%; max-width: 400px; height: 350px;
127
  margin: 15px auto; border-radius: 18px;
 
155
  .code-container pre code{white-space:pre;display:block;}
156
  .bubble code:not(pre code){font-family:'JetBrains Mono',monospace;font-size:.85rem;background:var(--surface3);padding:3px 6px;border-radius:6px;color:var(--accent);}
157
 
158
+ /* KaTeX */
159
  .katex-display{overflow-x:auto;overflow-y:hidden;padding:8px 0;-webkit-overflow-scrolling:touch;}
160
  .katex{font-size:1.05em;}
161
 
 
188
  .btn-send:hover{opacity:.9;transform:scale(1.08) rotate(-10deg);}[dir="rtl"] .btn-send:hover{transform:scale(1.08) rotate(10deg);}
189
  .btn-send:disabled{opacity:.4;cursor:not-allowed;transform:none;}
190
 
 
191
  .btn-stop {background:var(--surface3); color:var(--text); border:1px solid var(--border);}
192
  .btn-stop:hover {background:var(--danger); color:#fff; transform:scale(1.05);}
193
 
 
223
  color:var(--danger);border-radius:var(--radius-pill);cursor:pointer;font-size:.9rem;font-weight:600;font-family:'Cairo',sans-serif; transition:all var(--tr);}
224
  .btn-danger:hover{background:var(--danger); color:#fff;}
225
 
226
+ /* โ•โ• RESPONSIVE MOBILE (ุชุญุณูŠู†ุงุช ุงู„ุดุฑูŠุท ุงู„ุฌุงู†ุจูŠ) โ•โ• */
227
  @media (max-width: 640px) {
228
  .msg-row { padding: 6px 12px; gap: 10px; }
229
  .bubble { max-width: 92vw; padding: 12px 14px; font-size: .93rem; }
 
235
  .w-title { font-size: 1.8rem; }
236
  .chips { gap: 8px; }
237
  .chip { font-size: .82rem; padding: 8px 14px; }
238
+
239
+ /* ๐Ÿ†• ุฌุนู„ ุงู„ุณุงูŠุฏุจุงุฑ ุทุจู‚ุฉ ู…ู†ุฒู„ู‚ุฉ ุนู„ู‰ ุงู„ู‡ูˆุงุชู */
240
+ .sidebar {
241
+ position: fixed;
242
+ top: 0;
243
+ bottom: 0;
244
+ left: 0;
245
+ z-index: 100;
246
+ height: 100vh;
247
+ box-shadow: 4px 0 20px rgba(0,0,0,0.2);
248
+ transition: transform 0.3s ease, width 0.3s ease;
249
+ }
250
+ [dir="rtl"] .sidebar {
251
+ left: auto;
252
+ right: 0;
253
+ }
254
+ .sidebar.collapsed {
255
+ transform: translateX(-100%);
256
+ width: var(--sidebar-w) !important;
257
+ min-width: var(--sidebar-w) !important;
258
+ }
259
+ [dir="rtl"] .sidebar.collapsed {
260
+ transform: translateX(100%);
261
+ }
262
+ .sidebar:not(.collapsed) {
263
+ transform: translateX(0);
264
+ }
265
  }
266
  </style>
267
  </head>
268
  <body>
269
  <div class="app">
270
 
271
+ <!-- ๐Ÿ†• ุงู„ุดุฑูŠุท ุงู„ุฌุงู†ุจูŠ: ู†ุถูŠู ูƒู„ุงุณ collapsed ุงูุชุฑุงุถูŠู‹ุง -->
272
+ <aside class="sidebar collapsed" id="sidebar">
273
  <div class="sidebar-header">
274
  <div class="s-logo">
275
  <img src="https://copilot.microsoft.com/th/id/BCO.f29916dd-b0c1-4089-87cd-43d099a7d1a6.png" alt="Genisi"/>
 
287
 
288
  <main class="main">
289
  <div class="topbar">
290
+ <!-- ุฒุฑ ุงู„ู‚ุงุฆู…ุฉ (ุณูŠุชู… ุชุญุฏูŠุซ ุฃูŠู‚ูˆู†ุชู‡ ุฏูŠู†ุงู…ูŠูƒูŠู‹ุง) -->
291
+ <button class="btn-icon" id="menu-toggle-btn" onclick="toggleSidebar()">โ˜ฐ</button>
292
  <div class="topbar-title" id="topbar-title">Genisi AI</div>
293
  </div>
294
 
 
309
  </main>
310
  </div>
311
 
312
+ <!-- SETTINGS MODAL (ุจุฏูˆู† ุชุบูŠูŠุฑ) -->
313
  <div class="overlay" id="settings-modal">
314
  <div class="modal">
315
  <div class="m-header">
 
344
  </div>
345
 
346
  <script>
347
+ // โ•โ•โ•โ•โ•โ•โ•โ• i18n (ูƒู…ุง ู‡ูŠ) โ•โ•โ•โ•โ•โ•โ•โ•
348
  const i18n = {
349
  en: { newChat: "โœฆ New Chat", chatsLabel: "CHATS", settings: "Settings", lang: "Language", dark: "Dark Mode", del: "Delete All Chats", delDesc: "This action cannot be undone", delBtn: "๐Ÿ—‘ Delete", saveBtn: "๐Ÿ’พ Close", placeholder: "Type your message... (Enter to send)", welcomeTitle: "Welcome to Genisi", welcomeSub: "Your smart assistant by AnesNT โ€” Batna ๐Ÿ‡ฉ๐Ÿ‡ฟ", c1: "What is AI?", c2: "Write Python code", c3: "Summarize a topic", c4: "Design an image in space ๐Ÿš€", errConnect: "Connection Error", memBadge: "Memory: {c}/{t} messages", stopMsg: "[Generation stopped by user]" },
350
  ar: { newChat: "โœฆ ู…ุญุงุฏุซุฉ ุฌุฏูŠุฏุฉ", chatsLabel: "ุงู„ู…ุญุงุฏุซุงุช", settings: "ุงู„ุฅุนุฏุงุฏุงุช", lang: "ุงู„ู„ุบุฉ", dark: "ุงู„ูˆุถุน ุงู„ู„ูŠู„ูŠ", del: "ุญุฐู ุฌู…ูŠุน ุงู„ู…ุญุงุฏุซุงุช", delDesc: "ู„ุง ูŠู…ูƒู† ุงู„ุชุฑุงุฌุน ุนู† ู‡ุฐุง ุงู„ุฅุฌุฑุงุก", delBtn: "๐Ÿ—‘ ุญุฐู", saveBtn: "๐Ÿ’พ ุฅุบู„ุงู‚", placeholder: "ุงูƒุชุจ ุฑุณุงู„ุชูƒ... (Enter ู„ู„ุฅุฑุณุงู„)", welcomeTitle: "ู…ุฑุญุจู‹ุง ููŠ Genisi", welcomeSub: "ู…ุณุงุนุฏูƒ ุงู„ุฐูƒูŠ ู…ู† AnesNT โ€” ูˆู„ุงูŠุฉ ุจุงุชู†ุฉ ๐Ÿ‡ฉ๐Ÿ‡ฟ", c1: "ู…ุง ู‡ูˆ ุงู„ุฐูƒุงุก ุงู„ุงุตุทู†ุงุนูŠุŸ", c2: "ุงูƒุชุจ ูƒูˆุฏ ุจุงูŠุซูˆู†", c3: "ู„ุฎุต ู„ูŠ ู…ูˆุถูˆุนุงู‹", c4: "ุตู…ู… ุตูˆุฑุฉ ุจุงู„ูุถุงุก ๐Ÿš€", errConnect: "ุฎุทุฃ ููŠ ุงู„ุงุชุตุงู„", memBadge: "ุงู„ุฐุงูƒุฑุฉ: {c}/{t} ุฑุณุงุฆู„", stopMsg: "[ุชู… ุฅูŠู‚ุงู ุงู„ุชูˆู„ูŠุฏ]" },
 
375
  }
376
  function changeLanguage(val) { currentLang = val; localStorage.setItem('genisi_lang', val); applyI18n(); }
377
 
378
+ // โ•โ•โ•โ•โ•โ•โ•โ• STATE & CONFIG (ุจุฏูˆู† ุชุบูŠูŠุฑ) โ•โ•โ•โ•โ•โ•โ•โ•
379
  const genId = () => Date.now().toString(36) + Math.random().toString(36).substring(2, 6);
380
 
381
  let userId = localStorage.getItem('genisi_user_id');
 
390
  let pendingFiles =[];
391
  let currentAbortController = null;
392
 
393
+ // โ•โ•โ•โ•โ•โ•โ•โ• MARKDOWN & PARSER (ุจุฏูˆู† ุชุบูŠูŠุฑ) โ•โ•โ•โ•โ•โ•โ•โ•
394
  const renderer = new marked.Renderer();
395
  renderer.code = function(token) {
396
  const codeText = typeof token === 'string' ? token : token.text || '';
 
417
  renderer.html = function(html) { return html; };
418
  marked.setOptions({ renderer: renderer, breaks: true, gfm: true });
419
 
 
420
  function safeMarked(text) {
421
  if (!text) return '';
422
  const rawHtml = marked.parse(text);
 
445
  });
446
  }
447
 
 
448
  function removeGeminiCursor() {
449
  document.querySelectorAll('.gemini-cursor').forEach(el => el.remove());
450
  }
451
 
452
+ // โ•โ•โ•โ•โ•โ•โ•โ• UTILS & FILES (ุจุฏูˆู† ุชุบูŠูŠุฑ) โ•โ•โ•โ•โ•โ•โ•โ•
453
  const saveChats = () => localStorage.setItem('genisi_chats', JSON.stringify(chats));
454
  const getChat = (id) => chats.find(c => c.id === (id ?? activeChatId));
455
  function esc(t){ return String(t).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
 
485
  }
486
  function removeFile(idx) { pendingFiles.splice(idx, 1); renderFileChips(); }
487
 
488
+ // โ•โ•โ•โ•โ•โ•โ•โ• THEME & UI (ุชุนุฏูŠู„ุงุช ุงู„ุดุฑูŠุท ุงู„ุฌุงู†ุจูŠ) โ•โ•โ•โ•โ•โ•โ•โ•
489
  function applyTheme(t){
490
  document.documentElement.setAttribute('data-theme',t);
491
  localStorage.setItem('genisi_theme',t);
 
495
  function toggleTheme(){ applyTheme(document.documentElement.getAttribute('data-theme')==='dark'?'light':'dark'); }
496
  function applyThemeToggle(){ applyTheme(document.getElementById('dark-toggle').checked?'dark':'light'); }
497
 
498
+ // ๐Ÿ†• ุชุญุฏูŠุซ ุฃูŠู‚ูˆู†ุฉ ุฒุฑ ุงู„ู‚ุงุฆู…ุฉ
499
+ function updateMenuIcon() {
500
+ const btn = document.getElementById('menu-toggle-btn');
501
+ const sidebar = document.getElementById('sidebar');
502
+ if (sidebar.classList.contains('collapsed')) {
503
+ btn.textContent = 'โ˜ฐ';
504
+ } else {
505
+ btn.textContent = 'โœ•';
506
+ }
507
+ }
508
+
509
+ // ๐Ÿ†• ุฏุงู„ุฉ ุงู„ุชุจุฏูŠู„ ุงู„ู…ุนุฏู„ุฉ
510
+ function toggleSidebar() {
511
+ const sidebar = document.getElementById('sidebar');
512
+ sidebar.classList.toggle('collapsed');
513
+ updateMenuIcon();
514
+ }
515
+
516
+ // ๐Ÿ†• ุถุจุท ุงู„ุญุงู„ุฉ ุงู„ุงูุชุฑุงุถูŠุฉ ุจู†ุงุกู‹ ุนู„ู‰ ุญุฌู… ุงู„ุดุงุดุฉ
517
+ function initSidebarState() {
518
+ const sidebar = document.getElementById('sidebar');
519
+ // ุนู„ู‰ ุงู„ู‡ูˆุงุชู ู†ุถู…ู† ุฃู†ู‡ ู…ุฎููŠ (collapsed)ุŒ ุนู„ู‰ ุงู„ูƒุจูŠุฑุฉ ู†ุฌุนู„ู‡ ุธุงู‡ุฑู‹ุง
520
+ if (window.innerWidth <= 640) {
521
+ sidebar.classList.add('collapsed');
522
+ } else {
523
+ sidebar.classList.remove('collapsed');
524
+ }
525
+ updateMenuIcon();
526
+ }
527
+
528
+ // ุงุณุชุฏุนุงุก ุนู†ุฏ ุชุบูŠูŠุฑ ุญุฌู… ุงู„ู†ุงูุฐุฉ (ู„ุถุจุท ุงู„ุดุงุดุงุช ุงู„ูƒุจูŠุฑุฉ ุชู„ู‚ุงุฆูŠู‹ุง)
529
+ window.addEventListener('resize', () => {
530
+ const sidebar = document.getElementById('sidebar');
531
+ if (window.innerWidth > 640) {
532
+ sidebar.classList.remove('collapsed');
533
+ } else {
534
+ sidebar.classList.add('collapsed');
535
+ }
536
+ updateMenuIcon();
537
+ });
538
+
539
  function openSettings(){ document.getElementById('settings-modal').classList.add('open'); document.getElementById('lang-select').value=currentLang;}
540
  function closeSettings(){ document.getElementById('settings-modal').classList.remove('open'); }
541
 
 
580
  chat.history.forEach((entry, idx)=>{
581
  appendBubble('user', entry.user, false, entry.uiFiles);
582
  appendBubble('bot', entry.bot, true);
 
583
  });
584
  if (chat.history.length) {
585
  appendMemBadge(chat.history.length, chat.history.length);
 
594
  }
595
  function clearAllChats(){ chats=[]; saveChats(); newChat(); closeSettings(); }
596
 
 
597
  function stopGeneration() {
598
  if (currentAbortController) {
599
  currentAbortController.abort();
600
  }
601
  }
602
 
 
603
  function quickSend(text){ document.getElementById('msg-input').value=text; sendMessage(); }
604
  function handleKey(e){ if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendMessage();} }
605
  function autoResize(el){ el.style.height='auto'; el.style.height=Math.min(el.scrollHeight,160)+'px'; }
606
 
607
+ // โ•โ•โ•โ•โ•โ•โ•โ• MESSAGING & STREAMING (ุจุฏูˆู† ุชุบูŠูŠุฑ) โ•โ•โ•โ•โ•โ•โ•โ•
608
  async function sendMessage(){
609
  if(isGenerating) return;
610
  const input = document.getElementById('msg-input');
 
668
  const chunk = decoder.decode(value, {stream: true});
669
  fullBotResponse += chunk;
670
 
 
671
  let safeHtml = safeMarked(fullBotResponse);
672
  botContentDiv.innerHTML = safeHtml;
673
 
 
674
  if (isImageRequest && !botContentDiv.querySelector('img')) {
675
  botContentDiv.innerHTML += '<div class="skeleton-img">๐ŸŽจ ุฌุงุฑูŠ ุงู„ุชุฎูŠู„ ูˆุงู„ุชุตู…ูŠู…...</div>';
676
  } else {
 
677
  botContentDiv.innerHTML += '<span class="gemini-cursor"></span>';
678
  }
679
 
 
681
  scrollToBottom();
682
  }
683
 
 
684
  let finalHtml = safeMarked(fullBotResponse);
685
  botContentDiv.innerHTML = finalHtml;
686
  renderKaTeX(botContentDiv);
 
726
 
727
  const contentDiv = document.createElement('div');
728
  if(text) {
 
729
  contentDiv.innerHTML = isMarkdown ? safeMarked(text) : esc(text).replace(/\n/g, '<br/>');
730
  if (isMarkdown) renderKaTeX(contentDiv);
731
  }
 
734
  row.appendChild(av); row.appendChild(bub);
735
  area.appendChild(row);
736
 
 
737
  if (role === 'bot') {
738
  const images = bub.querySelectorAll('img');
739
  if (images.length) {
 
759
  scrollToBottom();
760
  }
761
 
762
+ // โ•โ•โ•โ•โ•โ•โ•โ• INIT (ู…ุน ุงุณุชุฏุนุงุก ุถุจุท ุงู„ุดุฑูŠุท) โ•โ•โ•โ•โ•โ•โ•โ•
763
  applyTheme(localStorage.getItem('genisi_theme')||'dark');
764
  document.getElementById('lang-select').value = currentLang;
765
  applyI18n();
766
+ initSidebarState(); // ๐Ÿ†• ุชู‡ูŠุฆุฉ ุญุงู„ุฉ ุงู„ุดุฑูŠุท ุงู„ุฌุงู†ุจูŠ
767
 
 
768
  if (typeof DOMPurify === 'undefined') {
769
  console.warn("DOMPurify not loaded - XSS protection disabled");
770
  }