Chat / index.html
1Egyb's picture
Upload 6 files
fe31703 verified
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>FECUOY AI | المحرك الاحترافي v2.2</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;500;600&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<style>
:root {
--bg-dark: #1a1b1e;
--bg-sidebar: #111214;
--primary: #10a37f;
--text-main: #e1e1e6;
--input-bg: #2a2b32;
--border-color: rgba(255,255,255,0.08);
--assistant-msg: rgba(255,255,255,0.04);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'IBM Plex Sans Arabic', sans-serif;
background-color: var(--bg-dark);
color: var(--text-main);
height: 100dvh;
overflow: hidden;
display: flex;
}
.app-layout { display: flex; width: 100%; height: 100%; position: relative; }
/*Sidebar*/
.sidebar {
width: 280px; background: var(--bg-sidebar);
border-left: 1px solid var(--border-color);
display: flex; flex-direction: column;
transition: all 0.3s ease;
}
@media (max-width: 768px) {
.sidebar { position: absolute; right: -280px; height: 100%; z-index: 1000; }
.sidebar.active { right: 0; }
}
.main-content { flex: 1; display: flex; flex-direction: column; height: 100%; min-width: 0; }
header {
height: 60px; padding: 0 20px;
display: flex; align-items: center; justify-content: space-between;
border-bottom: 1px solid var(--border-color);
}
#chatBox { flex: 1; overflow-y: auto; padding: 30px 15%; scroll-behavior: smooth; }
@media (max-width: 1024px) { #chatBox { padding: 20px 5%; } }
.msg-row { display: flex; margin-bottom: 25px; animation: fadeIn 0.3s ease; }
.msg { max-width: 85%; padding: 15px 20px; border-radius: 12px; line-height: 1.7; font-size: 15.5px; word-wrap: break-word; }
.user { background: var(--primary); color: white; margin-right: auto; }
.assistant { background: var(--assistant-msg); border: 1px solid var(--border-color); margin-left: auto; }
/* Controls Area */
.controls-bar {
padding: 10px 15%;
display: flex;
gap: 20px;
background: rgba(0,0,0,0.2);
border-top: 1px solid var(--border-color);
font-size: 13px;
align-items: center;
}
@media (max-width: 1024px) { .controls-bar { padding: 10px 5%; flex-wrap: wrap; } }
.control-group { display: flex; align-items: center; gap: 8px; color: #aaa; }
select { background: var(--input-bg); color: white; border: 1px solid var(--border-color); padding: 4px 8px; border-radius: 5px; outline: none; }
input[type="checkbox"] { accent-color: var(--primary); cursor: pointer; }
.input-area { padding: 10px 15% 30px; }
@media (max-width: 1024px) { .input-area { padding: 10px 5% 20px; } }
.input-wrapper {
background: var(--input-bg); border: 1px solid var(--border-color);
border-radius: 16px; padding: 10px 15px;
display: flex; align-items: flex-end; gap: 10px;
}
textarea { flex: 1; background: none; border: none; color: white; resize: none; font-size: 16px; outline: none; max-height: 200px; }
.action-btn { background: none; border: none; color: #8e8ea0; cursor: pointer; font-size: 20px; }
.send-btn { color: var(--primary); }
pre { background: #000 !important; padding: 15px; border-radius: 8px; margin: 10px 0; overflow-x: auto; direction: ltr; }
code { font-family: monospace; }
#fileInput { display: none; }
.upload-success { color: var(--primary) !important; }
</style>
</head>
<body>
<div class="app-layout">
<aside class="sidebar" id="sidebar">
<button style="margin:15px; padding:12px; background:var(--primary); border:none; color:white; border-radius:8px; cursor:pointer;" onclick="location.reload()">
<i class="fas fa-plus"></i> محادثة جديدة
</button>
<div style="flex:1"></div>
<div style="padding: 20px; border-top: 1px solid var(--border-color); font-size: 12px; color: #444;">
FECUOY AI v2.2 - Multi-Model Engine
</div>
</aside>
<main class="main-content">
<header>
<button class="action-btn" onclick="document.getElementById('sidebar').classList.toggle('active')"><i class="fas fa-bars"></i></button>
<div style="font-weight:600;">FECUOY <span style="color:var(--primary)">AI</span></div>
<div style="width:10px; height:10px; background:var(--primary); border-radius:50%;"></div>
</header>
<div id="chatBox">
<div class="msg-row">
<div class="msg assistant">مرحباً! يمكنك الآن التبديل بين النماذج وتفعيل أدوات البحث المباشر من الأسفل.</div>
</div>
</div>
<div class="controls-bar">
<div class="control-group">
<span>النموذج:</span>
<select id="modelSelect">
<option value="qwen_abliterated">Qwen 2.5 72B (محرر)</option>
<option value="kimi_k2">Kimi-K2 (رسمي)</option>
<option value="internvl_vision">InternVL2 (تحليل صور)</option>
</select>
</div>
<div class="control-group">
<input type="checkbox" id="toolSearch">
<label for="toolSearch">بحث ويب 🌐</label>
</div>
<div class="control-group">
<input type="checkbox" id="toolURL">
<label for="toolURL">تحليل روابط 🔗</label>
</div>
</div>
<div class="input-area">
<div class="input-wrapper">
<label for="fileInput" class="action-btn" id="attachLabel"><i class="fas fa-paperclip"></i></label>
<input type="file" id="fileInput" onchange="handleFileUpload(this)">
<textarea id="userInput" placeholder="اسأل أي شيء..." rows="1" oninput="this.style.height='auto';this.style.height=this.scrollHeight+'px'"></textarea>
<button class="action-btn send-btn" id="sendBtn" onclick="send()"><i class="fas fa-paper-plane"></i></button>
</div>
</div>
</main>
</div>
<script>
let currentFileId = null;
async function handleFileUpload(input) {
if (!input.files[0]) return;
const icon = document.querySelector('#attachLabel i');
icon.className = "fas fa-spinner fa-spin";
const formData = new FormData();
formData.append('file', input.files[0]);
try {
const res = await fetch('/api/upload', { method: 'POST', body: formData });
const data = await res.json();
if (data.file_id) {
currentFileId = data.file_id;
icon.className = "fas fa-check-circle upload-success";
}
} catch (e) { alert("خطأ في الرفع"); icon.className = "fas fa-paperclip"; }
}
async function send() {
const input = document.getElementById('userInput');
const text = input.value.trim();
if (!text && !currentFileId) return;
const selectedModel = document.getElementById('modelSelect').value;
const tools = [];
if(document.getElementById('toolSearch').checked) tools.push("web_search");
if(document.getElementById('toolURL').checked) tools.push("url_reader");
appendMsg('user', text);
input.value = '';
input.style.height = 'auto';
const assistantMsg = appendMsg('assistant', 'جاري المعالجة...');
try {
const response = await fetch('/api/chat/stream', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message: text,
model: selectedModel,
tools: tools,
file_id: currentFileId
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.substring(6));
if (data.token) {
fullText += data.token;
assistantMsg.innerHTML = marked.parse(fullText);
assistantMsg.querySelectorAll('pre code').forEach(el => hljs.highlightElement(el));
}
}
}
}
} catch (e) { assistantMsg.innerText = "⚠️ خطأ في الاتصال."; }
}
function appendMsg(role, content) {
const box = document.getElementById('chatBox');
const row = document.createElement('div');
row.className = 'msg-row';
const msg = document.createElement('div');
msg.className = `msg ${role}`;
msg.innerHTML = content;
row.appendChild(msg);
box.appendChild(row);
box.scrollTop = box.scrollHeight;
return msg;
}
document.getElementById('userInput').addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); }
});
</script>
</body>
</html>