Update index.html
Browse files- 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
|
| 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 |
-
<
|
|
|
|
| 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 |
-
<
|
|
|
|
| 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 (
|
| 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
|
| 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,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
|
@@ -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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
|
|
|
| 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 |
}
|