AnesKAM commited on
Commit
94533db
ยท
verified ยท
1 Parent(s): 5b5f670

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +165 -119
index.html CHANGED
@@ -5,7 +5,6 @@
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
- <!-- ู…ูƒุชุจุฉ ุงู„ู…ุงุฑูƒุฏุงูˆู† ู„ุฏุนู… ุงู„ุฌุฏุงูˆู„ ูˆุงู„ุฃูƒูˆุงุฏ -->
9
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
10
  <style>
11
  :root {
@@ -14,8 +13,10 @@
14
  --accent: #4f8ef7; --accent2: #7c5cf7; --accent-soft: rgba(79,142,247,0.12);
15
  --text: #e8eaf2; --text2: #9aa3be; --text3: #5c6480;
16
  --user-bubble: #1a2340; --bot-bubble: #161920;
17
- --danger: #f75f5f; --radius: 18px; --radius-sm: 10px;
18
- --sidebar-w: 280px; --tr: 0.22s cubic-bezier(.4,0,.2,1);
 
 
19
  }
20
  [data-theme="light"] {
21
  --bg: #f0f2f8; --surface: #fff; --surface2: #f5f6fa; --surface3: #ebedf5;
@@ -24,143 +25,179 @@
24
  --user-bubble: #dde6ff; --bot-bubble: #fff; --accent-soft: rgba(79,142,247,0.1);
25
  }
26
  *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
27
- html,body{height:100%;font-family:'Cairo',sans-serif;background:var(--bg);color:var(--text)}
28
- ::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:transparent}
29
- ::-webkit-scrollbar-thumb{background:var(--border2);border-radius:99px}
30
 
31
  .app{display:flex;height:100vh;overflow:hidden}
32
 
33
  /* SIDEBAR */
34
  .sidebar{width:var(--sidebar-w);min-width:var(--sidebar-w);background:var(--surface);
35
  border-right:1px solid var(--border);display:flex;flex-direction:column;
36
- transition:width var(--tr),min-width var(--tr);overflow:hidden}
37
- [dir="rtl"] .sidebar { border-right:none; border-left:1px solid var(--border); }
 
38
  .sidebar.collapsed{width:0;min-width:0;border:none}
39
- .sidebar-header{padding:18px 16px 12px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--border)}
40
- .s-logo{display:flex;align-items:center;gap:10px;flex:1}
41
- .s-logo img{width:32px;height:32px;border-radius:8px;object-fit:cover}
42
- .s-logo-name{font-size:1.1rem;font-weight:700;background:linear-gradient(135deg,var(--accent),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
43
- .btn-new{margin:12px 12px 4px;padding:10px 14px;background:linear-gradient(135deg,var(--accent),var(--accent2));
44
- color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-family:'Cairo',sans-serif;
45
- font-size:.9rem;font-weight:600;display:flex;align-items:center;gap:8px;
46
- transition:opacity var(--tr),transform var(--tr)}
47
- .btn-new:hover{opacity:.88;transform:translateY(-1px)}
48
- .s-label{padding:10px 16px 4px;font-size:.72rem;font-weight:600;color:var(--text3);letter-spacing:.06em;text-transform:uppercase}
49
- .chats-list{flex:1;overflow-y:auto;padding:4px 8px;display:flex;flex-direction:column;gap:2px}
50
- .chat-item{padding:9px 12px;border-radius:var(--radius-sm);cursor:pointer;font-size:.87rem;color:var(--text2);
51
- display:flex;align-items:center;gap:8px;transition:background var(--tr),color var(--tr);
52
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
53
- .chat-item:hover{background:var(--surface2);color:var(--text)}
54
- .chat-item.active{background:var(--accent-soft);color:var(--accent);font-weight:600}
 
55
  .chat-item .ct{flex:1;overflow:hidden;text-overflow:ellipsis}
56
- .del-btn{opacity:0;background:none;border:none;color:var(--danger);cursor:pointer;font-size:.85rem;
57
- padding:2px 5px;border-radius:5px;transition:opacity var(--tr)}
58
  .chat-item:hover .del-btn{opacity:1}
59
- .s-footer{padding:12px;border-top:1px solid var(--border);display:flex;align-items:center;gap:10px}
60
- .btn-icon{width:36px;height:36px;background:var(--surface2);border:1px solid var(--border);
61
- border-radius:var(--radius-sm);color:var(--text2);cursor:pointer;display:flex;
62
- align-items:center;justify-content:center;font-size:1rem;transition:background var(--tr),color var(--tr)}
63
- .btn-icon:hover{background:var(--surface3);color:var(--text)}
 
64
 
65
  /* MAIN */
66
- .main{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}
67
- .topbar{height:58px;padding:0 18px;display:flex;align-items:center;gap:12px;
68
- border-bottom:1px solid var(--border);background:var(--surface);flex-shrink:0}
69
- .topbar-title{flex:1;font-size:.95rem;font-weight:600;color:var(--text2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
70
 
71
- /* CHAT */
72
- .chat-area{flex:1;overflow-y:auto;padding:28px 0;display:flex;flex-direction:column}
73
  .welcome{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
74
- gap:16px;padding:40px 24px;text-align:center}
75
- .w-logo{width:72px;height:72px;border-radius:18px;object-fit:cover;box-shadow:0 8px 32px rgba(79,142,247,.25)}
76
- .w-title{font-size:2rem;font-weight:700;background:linear-gradient(135deg,var(--accent),var(--accent2));
77
  -webkit-background-clip:text;-webkit-text-fill-color:transparent}
78
- .w-sub{color:var(--text3);font-size:.95rem}
79
- .chips{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:8px;max-width:520px}
80
- .chip{padding:8px 16px;background:var(--surface2);border:1px solid var(--border);border-radius:99px;
81
- font-size:.83rem;color:var(--text2);cursor:pointer;transition:background var(--tr),border-color var(--tr),color var(--tr)}
82
- .chip:hover{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}
83
-
84
- /* MESSAGES */
85
- .msg-row{display:flex;padding:5px 28px;gap:12px;animation:fadeUp .28s ease}
86
- @keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
 
 
 
 
 
 
 
87
  .msg-row.user{flex-direction:row-reverse}
88
- .msg-av{width:34px;height:34px;border-radius:10px;flex-shrink:0;display:flex;align-items:center;
89
- justify-content:center;font-size:1rem;font-weight:700;margin-top:2px;overflow:hidden}
 
90
  .msg-row.bot .msg-av{background:linear-gradient(135deg,var(--accent),var(--accent2))}
91
  .msg-row.bot .msg-av img{width:100%;height:100%;object-fit:cover}
92
- .msg-row.user .msg-av{background:var(--surface3);color:var(--text2)}
93
- .bubble{max-width:min(640px,78vw);padding:12px 16px;border-radius:var(--radius);
94
- font-size:.93rem;line-height:1.72;word-break:break-word}
95
- .msg-row.bot .bubble{background:var(--bot-bubble);border:1px solid var(--border);border-top-left-radius:4px}
96
- [dir="rtl"] .msg-row.bot .bubble { border-top-left-radius:var(--radius); border-top-right-radius:4px; }
97
- .msg-row.user .bubble{background:var(--user-bubble);border:1px solid var(--border2);border-top-right-radius:4px}
98
- [dir="rtl"] .msg-row.user .bubble { border-top-right-radius:var(--radius); border-top-left-radius:4px; }
 
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  /* MARKDOWN STYLES */
101
- .bubble p{margin-bottom:10px}.bubble p:last-child{margin-bottom:0}
102
- .bubble ul,.bubble ol{padding-inline-start:20px;margin:10px 0}
 
103
 
104
  /* Tables */
105
- .bubble table {width:100%;border-collapse:collapse;margin:12px 0;}
106
- .bubble th, .bubble td {border:1px solid var(--border);padding:8px;text-align:start;}
107
  .bubble th {background:var(--surface3);font-weight:600;}
108
 
109
  /* Code Blocks */
110
- .code-container {position:relative; margin:12px 0; background:var(--surface3); border-radius:8px; border:1px solid var(--border); overflow:hidden;}
111
- .code-header {display:flex; justify-content:space-between; align-items:center; background:#12141a; padding:6px 12px; font-size:0.75rem; color:var(--text2);}
112
- .copy-btn {background:none;border:none;color:var(--text2);cursor:pointer;display:flex;align-items:center;gap:4px;font-size:0.8rem;}
113
- .copy-btn:hover {color:#fff;}
114
- .code-container pre {margin:0; padding:12px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:0.85rem;}
115
- .bubble code:not(pre code) {font-family:'JetBrains Mono',monospace;font-size:.83rem;background:var(--surface3);padding:2px 5px;border-radius:4px; color:var(--accent);}
116
 
117
  /* memory badge */
118
- .mem-badge{font-size:.71rem;color:var(--text3);padding:0 28px 2px;display:flex;align-items:center;gap:4px}
119
-
120
- /* INPUT */
121
- .input-area{padding:14px 20px 18px;background:var(--surface);border-top:1px solid var(--border);flex-shrink:0}
122
- .input-box{display:flex;align-items:flex-end;gap:10px;background:var(--surface2);
123
- border:1.5px solid var(--border);border-radius:var(--radius);padding:10px 12px;
124
- transition:border-color var(--tr)}
125
- .input-box:focus-within{border-color:var(--accent)}
 
126
  .input-box textarea{flex:1;background:none;border:none;outline:none;color:var(--text);
127
- font-family:'Cairo',sans-serif;font-size:.93rem;resize:none;max-height:160px;line-height:1.6;}
128
  .input-box textarea::placeholder{color:var(--text3)}
129
- .in-actions{display:flex;align-items:center;gap:6px}
130
- .btn-send{width:36px;height:36px;background:linear-gradient(135deg,var(--accent),var(--accent2));
131
- border:none;border-radius:10px;cursor:pointer;color:#fff;display:flex;align-items:center;
132
- justify-content:center;font-size:1rem;transition:opacity var(--tr),transform var(--tr)}
133
- .btn-send:hover{opacity:.85;transform:scale(1.05)}
134
- .btn-send:disabled{opacity:.4;cursor:not-allowed;transform:none}
 
135
 
136
  /* MODAL */
137
- .overlay{position:fixed;inset:0;z-index:999;background:rgba(0,0,0,.55);display:flex;
138
- align-items:center;justify-content:center;backdrop-filter:blur(4px);
139
  opacity:0;pointer-events:none;transition:opacity var(--tr)}
140
  .overlay.open{opacity:1;pointer-events:all}
141
- .modal{background:var(--surface);border:1px solid var(--border);border-radius:20px;padding:28px;
142
- width:min(440px,92vw);box-shadow:0 24px 60px rgba(0,0,0,.4);
143
- transform:translateY(16px);transition:transform var(--tr)}
144
- .overlay.open .modal{transform:translateY(0)}
145
- .m-header{display:flex;align-items:center;gap:10px;margin-bottom:22px}
146
- .m-title{font-size:1.1rem;font-weight:700;flex:1}
147
- .m-close{background:none;border:none;color:var(--text3);cursor:pointer;font-size:1.2rem}
148
- .s-row{display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-bottom:1px solid var(--border)}
149
- .s-lbl{font-size:.9rem;color:var(--text)}
150
- .s-desc{font-size:.78rem;color:var(--text3);margin-top:2px}
151
- .toggle{position:relative;width:42px;height:24px}
 
152
  .toggle input{opacity:0;width:0;height:0}
153
- .tslider{position:absolute;inset:0;background:var(--surface3);border-radius:99px;cursor:pointer;transition:background var(--tr)}
154
- .tslider::before{content:'';position:absolute;width:18px;height:18px;border-radius:50%;
155
- background:#fff;top:3px;left:3px;transition:transform var(--tr)}
156
  .toggle input:checked+.tslider{background:var(--accent)}
157
- .toggle input:checked+.tslider::before{transform:translateX(18px)}
158
 
159
- select.s-input { width:auto; padding:5px 10px; background:var(--surface2); border:1px solid var(--border); border-radius:8px; color:var(--text); cursor:pointer;}
160
- .btn-save{width:100%;margin-top:16px;padding:11px;background:linear-gradient(135deg,var(--accent),var(--accent2));
161
- color:#fff;border:none;border-radius:10px;cursor:pointer;font-family:'Cairo',sans-serif;font-size:.95rem;font-weight:600;}
162
- .btn-danger{padding:6px 14px;background:rgba(247,95,95,.12);border:1px solid var(--danger);
163
- color:var(--danger);border-radius:8px;cursor:pointer;font-size:.83rem;font-family:'Cairo',sans-serif;}
 
 
164
  </style>
165
  </head>
166
  <body>
@@ -191,9 +228,11 @@
191
  <div class="chat-area" id="chat-area"></div>
192
 
193
  <div class="input-area">
194
- <div class="input-box">
195
- <textarea id="msg-input" rows="1" onkeydown="handleKey(event)" oninput="autoResize(this)" placeholder="Type a message..."></textarea>
196
- <button class="btn-send" id="send-btn" onclick="sendMessage()">โžค</button>
 
 
197
  </div>
198
  </div>
199
  </main>
@@ -273,7 +312,6 @@ let isGenerating = false;
273
 
274
  // โ•โ•โ•โ•โ•โ•โ•โ• MARKDOWN PARSER (marked.js) โ•โ•โ•โ•โ•โ•โ•โ•
275
  const renderer = new marked.Renderer();
276
- // Custom renderer for code blocks to add COPY button
277
  renderer.code = function(code, language) {
278
  const validLang = language || 'text';
279
  const escapedCode = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
@@ -301,6 +339,11 @@ const genId = () => Date.now().toString(36);
301
  const saveChats = () => localStorage.setItem('genisi_chats', JSON.stringify(chats));
302
  const getChat = (id) => chats.find(c => c.id === (id ?? activeChatId));
303
  function esc(t){ return String(t).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
 
 
 
 
 
304
 
305
  // โ•โ•โ•โ•โ•โ•โ•โ• THEME โ•โ•โ•โ•โ•โ•โ•โ•
306
  function applyTheme(t){
@@ -360,7 +403,7 @@ function loadChat(id){
360
  appendBubble('bot', entry.bot, true);
361
  appendMemBadge(idx+1, chat.history.length);
362
  });
363
- area.scrollTop=area.scrollHeight;
364
  renderChatList();
365
  }
366
 
@@ -398,7 +441,7 @@ async function sendMessage(){
398
  isGenerating=true;
399
  document.getElementById('send-btn').disabled=true;
400
 
401
- // Create an empty bot bubble for streaming
402
  const botContentDiv = appendBubble('bot', '', true);
403
 
404
  try {
@@ -410,7 +453,6 @@ async function sendMessage(){
410
 
411
  if(!response.ok) throw new Error("Server Error");
412
 
413
- // โ•โ•โ•โ•โ•โ•โ•โ• READING THE STREAM โ•โ•โ•โ•โ•โ•โ•โ•
414
  const reader = response.body.getReader();
415
  const decoder = new TextDecoder("utf-8");
416
  let fullBotResponse = "";
@@ -422,13 +464,16 @@ async function sendMessage(){
422
  const chunk = decoder.decode(value, {stream: true});
423
  fullBotResponse += chunk;
424
 
425
- // Parse markdown live and update the DOM
426
- botContentDiv.innerHTML = marked.parse(fullBotResponse);
427
- const area = document.getElementById('chat-area');
428
- area.scrollTop = area.scrollHeight;
429
  }
430
 
431
- // Save to history after stream is complete
 
 
 
432
  chat.history.push({ user: text, bot: fullBotResponse });
433
  saveChats();
434
  appendMemBadge(chat.history.length, chat.history.length);
@@ -458,16 +503,17 @@ function appendBubble(role, text, isMarkdown){
458
  bub.appendChild(contentDiv);
459
 
460
  row.appendChild(av); row.appendChild(bub);
461
- area.appendChild(row); area.scrollTop=area.scrollHeight;
 
462
 
463
- return contentDiv; // Return the inner div so we can inject stream into it
464
  }
465
 
466
  function appendMemBadge(count, total){
467
  const area=document.getElementById('chat-area');
468
  const b=document.createElement('div'); b.className='mem-badge';
469
  b.innerHTML=`๐Ÿง  ${i18n[currentLang].memBadge.replace('{c}', count).replace('{t}', total)}`;
470
- area.appendChild(b); area.scrollTop=area.scrollHeight;
471
  }
472
 
473
  // โ•โ•โ•โ•โ•โ•โ•โ• INIT โ•โ•โ•โ•โ•โ•โ•โ•
 
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
  <style>
10
  :root {
 
13
  --accent: #4f8ef7; --accent2: #7c5cf7; --accent-soft: rgba(79,142,247,0.12);
14
  --text: #e8eaf2; --text2: #9aa3be; --text3: #5c6480;
15
  --user-bubble: #1a2340; --bot-bubble: #161920;
16
+ --danger: #f75f5f;
17
+ /* ุงู„ุญูˆุงู ุงู„ุฏุงุฆุฑูŠุฉ ุงู„ู†ุงุนู…ุฉ ุฌุฏุงู‹ */
18
+ --radius: 28px; --radius-sm: 18px; --radius-pill: 50px;
19
+ --sidebar-w: 280px; --tr: 0.3s cubic-bezier(.4,0,.2,1);
20
  }
21
  [data-theme="light"] {
22
  --bg: #f0f2f8; --surface: #fff; --surface2: #f5f6fa; --surface3: #ebedf5;
 
25
  --user-bubble: #dde6ff; --bot-bubble: #fff; --accent-soft: rgba(79,142,247,0.1);
26
  }
27
  *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
28
+ html,body{height:100%;font-family:'Cairo',sans-serif;background:var(--bg);color:var(--text); scroll-behavior: smooth;}
29
+ ::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}
30
+ ::-webkit-scrollbar-thumb{background:var(--border2);border-radius:var(--radius-pill)}
31
 
32
  .app{display:flex;height:100vh;overflow:hidden}
33
 
34
  /* SIDEBAR */
35
  .sidebar{width:var(--sidebar-w);min-width:var(--sidebar-w);background:var(--surface);
36
  border-right:1px solid var(--border);display:flex;flex-direction:column;
37
+ transition:width var(--tr),min-width var(--tr);overflow:hidden;
38
+ border-top-right-radius: var(--radius); border-bottom-right-radius: var(--radius);}
39
+ [dir="rtl"] .sidebar { border-right:none; border-left:1px solid var(--border); border-radius: 0 var(--radius) var(--radius) 0; }
40
  .sidebar.collapsed{width:0;min-width:0;border:none}
41
+ .sidebar-header{padding:24px 20px 16px;display:flex;align-items:center;gap:12px;}
42
+ .s-logo{display:flex;align-items:center;gap:12px;flex:1}
43
+ .s-logo img{width:36px;height:36px;border-radius:12px;object-fit:cover; box-shadow: 0 4px 12px var(--accent-soft);}
44
+ .s-logo-name{font-size:1.2rem;font-weight:700;background:linear-gradient(135deg,var(--accent),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
45
+ .btn-new{margin:12px 16px;padding:12px 16px;background:linear-gradient(135deg,var(--accent),var(--accent2));
46
+ color:#fff;border:none;border-radius:var(--radius-pill);cursor:pointer;font-family:'Cairo',sans-serif;
47
+ font-size:.95rem;font-weight:600;display:flex;align-items:center;gap:8px; justify-content:center;
48
+ transition:opacity var(--tr),transform var(--tr); box-shadow: 0 4px 15px var(--accent-soft);}
49
+ .btn-new:hover{opacity:.9;transform:translateY(-2px)}
50
+ .s-label{padding:10px 20px;font-size:.75rem;font-weight:700;color:var(--text3);letter-spacing:.08em;text-transform:uppercase}
51
+ .chats-list{flex:1;overflow-y:auto;padding:4px 12px;display:flex;flex-direction:column;gap:6px}
52
+ .chat-item{padding:10px 16px;border-radius:var(--radius-sm);cursor:pointer;font-size:.9rem;color:var(--text2);
53
+ display:flex;align-items:center;gap:10px;transition:all var(--tr);
54
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
55
+ .chat-item:hover{background:var(--surface2);color:var(--text); transform: translateX(4px);}
56
+ [dir="rtl"] .chat-item:hover {transform: translateX(-4px);}
57
+ .chat-item.active{background:var(--accent-soft);color:var(--accent);font-weight:600;}
58
  .chat-item .ct{flex:1;overflow:hidden;text-overflow:ellipsis}
59
+ .del-btn{opacity:0;background:none;border:none;color:var(--danger);cursor:pointer;font-size:.9rem;
60
+ padding:4px;border-radius:50%;transition:all var(--tr); display:flex; align-items:center; justify-content:center;}
61
  .chat-item:hover .del-btn{opacity:1}
62
+ .del-btn:hover{background:rgba(247,95,95,.15); transform:scale(1.1);}
63
+ .s-footer{padding:16px;display:flex;align-items:center;gap:12px; justify-content: center;}
64
+ .btn-icon{width:42px;height:42px;background:var(--surface2);border:none;
65
+ border-radius:var(--radius-pill);color:var(--text2);cursor:pointer;display:flex;
66
+ align-items:center;justify-content:center;font-size:1.1rem;transition:all var(--tr)}
67
+ .btn-icon:hover{background:var(--surface3);color:var(--text); transform:rotate(10deg);}
68
 
69
  /* MAIN */
70
+ .main{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0; background: var(--bg);}
71
+ .topbar{height:70px;padding:0 24px;display:flex;align-items:center;gap:16px; flex-shrink:0}
72
+ .topbar-title{flex:1;font-size:1.05rem;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
 
73
 
74
+ /* CHAT AREA */
75
+ .chat-area{flex:1;overflow-y:auto;padding:20px 0 40px;display:flex;flex-direction:column; scroll-behavior: smooth;}
76
  .welcome{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
77
+ gap:20px;padding:40px 24px;text-align:center; animation: fadeSlideUp 0.6s ease;}
78
+ .w-logo{width:85px;height:85px;border-radius:24px;object-fit:cover;box-shadow:0 12px 40px var(--accent-soft)}
79
+ .w-title{font-size:2.4rem;font-weight:700;background:linear-gradient(135deg,var(--accent),var(--accent2));
80
  -webkit-background-clip:text;-webkit-text-fill-color:transparent}
81
+ .w-sub{color:var(--text2);font-size:1.05rem}
82
+ .chips{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin-top:12px;max-width:600px}
83
+ .chip{padding:10px 20px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-pill);
84
+ font-size:.9rem;color:var(--text);cursor:pointer;transition:all var(--tr); box-shadow: 0 4px 12px rgba(0,0,0,0.05);}
85
+ .chip:hover{background:var(--accent-soft);border-color:var(--accent);color:var(--accent); transform:translateY(-2px);}
86
+
87
+ /* MESSAGES & ANIMATIONS */
88
+ .msg-row{display:flex;padding:8px 30px;gap:16px; animation: popIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;}
89
+ @keyframes popIn {
90
+ from { opacity: 0; transform: translateY(15px) scale(0.98); filter: blur(4px); }
91
+ to { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
92
+ }
93
+ @keyframes fadeSlideUp {
94
+ from { opacity: 0; transform: translateY(20px); }
95
+ to { opacity: 1; transform: translateY(0); }
96
+ }
97
  .msg-row.user{flex-direction:row-reverse}
98
+ .msg-av{width:38px;height:38px;border-radius:14px;flex-shrink:0;display:flex;align-items:center;
99
+ justify-content:center;font-size:1.1rem;font-weight:700;margin-top:2px;overflow:hidden;
100
+ box-shadow: 0 4px 10px rgba(0,0,0,0.1);}
101
  .msg-row.bot .msg-av{background:linear-gradient(135deg,var(--accent),var(--accent2))}
102
  .msg-row.bot .msg-av img{width:100%;height:100%;object-fit:cover}
103
+ .msg-row.user .msg-av{background:var(--surface3);color:var(--text)}
104
+
105
+ .bubble{max-width:min(700px,80vw);padding:16px 22px;border-radius:var(--radius);
106
+ font-size:.98rem;line-height:1.75;word-break:break-word; box-shadow: 0 4px 15px rgba(0,0,0,0.03);}
107
+ .msg-row.bot .bubble{background:var(--bot-bubble);border:1px solid var(--border);border-top-left-radius:6px}
108
+ [dir="rtl"] .msg-row.bot .bubble { border-top-left-radius:var(--radius); border-top-right-radius:6px; }
109
+ .msg-row.user .bubble{background:var(--user-bubble);border:none; border-top-right-radius:6px}
110
+ [dir="rtl"] .msg-row.user .bubble { border-top-right-radius:var(--radius); border-top-left-radius:6px; }
111
 
112
+ /* GEMINI STREAMING EFFECT */
113
+ .gemini-cursor {
114
+ display: inline-block;
115
+ width: 14px; height: 14px;
116
+ margin-inline-start: 6px;
117
+ background: linear-gradient(135deg, var(--accent), var(--accent2));
118
+ 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>');
119
+ -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>');
120
+ mask-size: cover; -webkit-mask-size: cover;
121
+ animation: spinPulse 1s infinite linear;
122
+ transform-origin: center;
123
+ vertical-align: middle;
124
+ }
125
+ @keyframes spinPulse {
126
+ 0% { transform: scale(0.8) rotate(0deg); opacity: 0.5; }
127
+ 50% { transform: scale(1.2) rotate(90deg); opacity: 1; filter: drop-shadow(0 0 5px var(--accent)); }
128
+ 100% { transform: scale(0.8) rotate(180deg); opacity: 0.5; }
129
+ }
130
+
131
  /* MARKDOWN STYLES */
132
+ .bubble p{margin-bottom:12px}.bubble p:last-child{margin-bottom:0}
133
+ .bubble ul,.bubble ol{padding-inline-start:24px;margin:12px 0}
134
+ .bubble li {margin-bottom: 6px;}
135
 
136
  /* Tables */
137
+ .bubble table {width:100%;border-collapse:hidden;margin:16px 0; border-radius: var(--radius-sm); overflow:hidden; border: 1px solid var(--border);}
138
+ .bubble th, .bubble td {border:1px solid var(--border);padding:10px 14px;text-align:start;}
139
  .bubble th {background:var(--surface3);font-weight:600;}
140
 
141
  /* Code Blocks */
142
+ .code-container {position:relative; margin:16px 0; background:var(--surface3); border-radius:var(--radius-sm); border:1px solid var(--border); overflow:hidden;}
143
+ .code-header {display:flex; justify-content:space-between; align-items:center; background:#12141a; padding:8px 16px; font-size:0.8rem; color:var(--text2); font-family:'JetBrains Mono',monospace;}
144
+ .copy-btn {background:var(--surface2);border:none;color:var(--text);cursor:pointer;display:flex;align-items:center;gap:6px;font-size:0.8rem; padding: 4px 10px; border-radius: var(--radius-pill); transition: all 0.2s;}
145
+ .copy-btn:hover {background:var(--accent); color:#fff;}
146
+ .code-container pre {margin:0; padding:16px; overflow-x:auto; font-family:'JetBrains Mono',monospace; font-size:0.85rem;}
147
+ .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);}
148
 
149
  /* memory badge */
150
+ .mem-badge{font-size:.75rem;color:var(--text3);padding:4px 30px 10px;display:flex;align-items:center;gap:6px; font-weight: 600;}
151
+
152
+ /* INPUT - FLOATING PILL DESIGN */
153
+ .input-area{padding:0 24px 30px; display:flex; justify-content:center; flex-shrink:0;}
154
+ .input-wrapper{width:100%; max-width:850px; position:relative;}
155
+ .input-box{display:flex;align-items:flex-end;gap:12px;background:var(--surface);
156
+ border:1px solid var(--border);border-radius:32px;padding:12px 16px 12px 24px;
157
+ transition:all var(--tr); box-shadow: 0 8px 30px rgba(0,0,0,0.15);}[dir="rtl"] .input-box {padding:12px 24px 12px 16px;}
158
+ .input-box:focus-within{border-color:var(--accent); box-shadow: 0 8px 30px var(--accent-soft), 0 0 0 3px var(--accent-soft);}
159
  .input-box textarea{flex:1;background:none;border:none;outline:none;color:var(--text);
160
+ font-family:'Cairo',sans-serif;font-size:.98rem;resize:none;max-height:160px;line-height:1.6; padding: 6px 0;}
161
  .input-box textarea::placeholder{color:var(--text3)}
162
+
163
+ .btn-send{width:42px;height:42px;background:linear-gradient(135deg,var(--accent),var(--accent2));
164
+ border:none;border-radius:50%;cursor:pointer;color:#fff;display:flex;align-items:center;
165
+ justify-content:center;font-size:1.1rem;transition:all var(--tr); flex-shrink:0; margin-bottom: 2px;}
166
+ .btn-send:hover{opacity:.9;transform:scale(1.08) rotate(-10deg);}
167
+ [dir="rtl"] .btn-send:hover{transform:scale(1.08) rotate(10deg);}
168
+ .btn-send:disabled{opacity:.4;cursor:not-allowed;transform:none;}
169
 
170
  /* MODAL */
171
+ .overlay{position:fixed;inset:0;z-index:999;background:rgba(0,0,0,.6);display:flex;
172
+ align-items:center;justify-content:center;backdrop-filter:blur(8px);
173
  opacity:0;pointer-events:none;transition:opacity var(--tr)}
174
  .overlay.open{opacity:1;pointer-events:all}
175
+ .modal{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:32px;
176
+ width:min(440px,90vw);box-shadow:0 30px 80px rgba(0,0,0,.5);
177
+ transform:translateY(20px) scale(0.95);transition:all var(--tr)}
178
+ .overlay.open .modal{transform:translateY(0) scale(1)}
179
+ .m-header{display:flex;align-items:center;gap:12px;margin-bottom:24px}
180
+ .m-title{font-size:1.3rem;font-weight:700;flex:1}
181
+ .m-close{background:var(--surface2);border:none;color:var(--text);cursor:pointer;width:36px;height:36px; border-radius:50%; display:flex; align-items:center; justify-content:center; transition:background var(--tr);}
182
+ .m-close:hover{background:var(--danger); color:#fff;}
183
+ .s-row{display:flex;align-items:center;justify-content:space-between;padding:16px 0;border-bottom:1px solid var(--border)}
184
+ .s-lbl{font-size:.95rem;font-weight:600;color:var(--text)}
185
+ .s-desc{font-size:.8rem;color:var(--text3);margin-top:4px}
186
+ .toggle{position:relative;width:48px;height:26px}
187
  .toggle input{opacity:0;width:0;height:0}
188
+ .tslider{position:absolute;inset:0;background:var(--surface3);border-radius:var(--radius-pill);cursor:pointer;transition:all var(--tr)}
189
+ .tslider::before{content:'';position:absolute;width:20px;height:20px;border-radius:50%;
190
+ background:#fff;top:3px;left:4px;transition:all var(--tr); box-shadow: 0 2px 5px rgba(0,0,0,0.2);}
191
  .toggle input:checked+.tslider{background:var(--accent)}
192
+ .toggle input:checked+.tslider::before{transform:translateX(20px)}
193
 
194
+ select.s-input { width:auto; padding:8px 16px; background:var(--surface2); border:1px solid var(--border); border-radius:var(--radius-pill); color:var(--text); cursor:pointer; font-family:'Cairo',sans-serif; font-weight:600;}
195
+ .btn-save{width:100%;margin-top:24px;padding:14px;background:linear-gradient(135deg,var(--accent),var(--accent2));
196
+ color:#fff;border:none;border-radius:var(--radius-pill);cursor:pointer;font-family:'Cairo',sans-serif;font-size:1rem;font-weight:700; transition:opacity var(--tr);}
197
+ .btn-save:hover{opacity:.9}
198
+ .btn-danger{padding:8px 18px;background:rgba(247,95,95,.12);border:1px solid var(--danger);
199
+ 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);}
200
+ .btn-danger:hover{background:var(--danger); color:#fff;}
201
  </style>
202
  </head>
203
  <body>
 
228
  <div class="chat-area" id="chat-area"></div>
229
 
230
  <div class="input-area">
231
+ <div class="input-wrapper">
232
+ <div class="input-box">
233
+ <textarea id="msg-input" rows="1" onkeydown="handleKey(event)" oninput="autoResize(this)" placeholder="Type a message..."></textarea>
234
+ <button class="btn-send" id="send-btn" onclick="sendMessage()">โžค</button>
235
+ </div>
236
  </div>
237
  </div>
238
  </main>
 
312
 
313
  // โ•โ•โ•โ•โ•โ•โ•โ• MARKDOWN PARSER (marked.js) โ•โ•โ•โ•โ•โ•โ•โ•
314
  const renderer = new marked.Renderer();
 
315
  renderer.code = function(code, language) {
316
  const validLang = language || 'text';
317
  const escapedCode = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
 
339
  const saveChats = () => localStorage.setItem('genisi_chats', JSON.stringify(chats));
340
  const getChat = (id) => chats.find(c => c.id === (id ?? activeChatId));
341
  function esc(t){ return String(t).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
342
+ // Auto scroll smoothly to bottom
343
+ function scrollToBottom() {
344
+ const area = document.getElementById('chat-area');
345
+ area.scrollTop = area.scrollHeight;
346
+ }
347
 
348
  // โ•โ•โ•โ•โ•โ•โ•โ• THEME โ•โ•โ•โ•โ•โ•โ•โ•
349
  function applyTheme(t){
 
403
  appendBubble('bot', entry.bot, true);
404
  appendMemBadge(idx+1, chat.history.length);
405
  });
406
+ scrollToBottom();
407
  renderChatList();
408
  }
409
 
 
441
  isGenerating=true;
442
  document.getElementById('send-btn').disabled=true;
443
 
444
+ // ุฅู†ุดุงุก ุนู†ุตุฑ ุงู„ู…ุญุงุฏุซุฉ ู…ุน ุฅุธู‡ุงุฑู‡ ุจุฃู†ูŠู…ูŠุดู†
445
  const botContentDiv = appendBubble('bot', '', true);
446
 
447
  try {
 
453
 
454
  if(!response.ok) throw new Error("Server Error");
455
 
 
456
  const reader = response.body.getReader();
457
  const decoder = new TextDecoder("utf-8");
458
  let fullBotResponse = "";
 
464
  const chunk = decoder.decode(value, {stream: true});
465
  fullBotResponse += chunk;
466
 
467
+ // ุฏู…ุฌ ุงู„ู…ุงุฑูƒุฏุงูˆู† ู…ุน "ู…ุคุดุฑ ุฌูŠู…ูŠู†ูŠ ุงู„ู„ุงู…ุน"
468
+ botContentDiv.innerHTML = marked.parse(fullBotResponse) + '<span class="gemini-cursor"></span>';
469
+
470
+ scrollToBottom();
471
  }
472
 
473
+ // ุจุนุฏ ุงู„ุงู†ุชู‡ุงุกุŒ ู†ุญุฐู ุงู„ู…ุคุดุฑ ุงู„ู„ุงู…ุน ูˆู†ุถุน ุงู„ู†ุต ุงู„ู†ู‡ุงุฆูŠ
474
+ botContentDiv.innerHTML = marked.parse(fullBotResponse);
475
+
476
+ // Save to history
477
  chat.history.push({ user: text, bot: fullBotResponse });
478
  saveChats();
479
  appendMemBadge(chat.history.length, chat.history.length);
 
503
  bub.appendChild(contentDiv);
504
 
505
  row.appendChild(av); row.appendChild(bub);
506
+ area.appendChild(row);
507
+ scrollToBottom();
508
 
509
+ return contentDiv;
510
  }
511
 
512
  function appendMemBadge(count, total){
513
  const area=document.getElementById('chat-area');
514
  const b=document.createElement('div'); b.className='mem-badge';
515
  b.innerHTML=`๐Ÿง  ${i18n[currentLang].memBadge.replace('{c}', count).replace('{t}', total)}`;
516
+ area.appendChild(b); scrollToBottom();
517
  }
518
 
519
  // โ•โ•โ•โ•โ•โ•โ•โ• INIT โ•โ•โ•โ•โ•โ•โ•โ•