al00sakkaf's picture
قم بتطوير التطبيق واضف التحسينات التالية : اضف صفحة لاختيار النموذج وادخال مفتاح api ، اضف صفحة لادارة المحادثات السابقة ، قم بتطوير ادارة الشخصيات الافتراضية مع امكانية اضافة شخصية او تعديل شخصية او حذف شخصية ، اجعل التطبيق يقوم بحفظ المحادثات تلقائيا في قاعدة بيانات محلية ، اضف دعم markdown لعرض المحادثات بتنسيق ، اجعل التطبيق قادر على حفظ السياق في المحادثة ويجب ان يتقمص النموذج الشخصية ، النماذج الذي يستخدمها التطبيق هي نماذج Gemini-2.0-flash و glm-4.5-flash ، قم باستخدام واجهة انيقة ومتناسبة مع شاشاة الهواتف المحمولة، اضف التحسينات لجعل التطبيق مثالي
b9d2156 verified
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>المحادثة - GeminiPersona</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700;800&display=swap');
* {
font-family: 'Tajawal', sans-serif;
}
.chat-container {
height: calc(100vh - 8rem);
}
.messages-container {
height: calc(100% - 80px);
}
.chat-bubble-user {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 20px 20px 0 20px;
}
.chat-bubble-ai {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 20px 20px 20px 0;
}
.typing-indicator {
display: inline-flex;
align-items: center;
}
.typing-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #9ca3af;
margin: 0 2px;
animation: typing 1.4s infinite ease-in-out;
}
.typing-dot:nth-child(1) { animation-delay: -0.32s; }
.typing-dot:nth-child(2) { animation-delay: -0.16s; }
@keyframes typing {
0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
.markdown-content h1, .markdown-content h2, .markdown-content h3 {
margin-top: 1rem;
margin-bottom: 0.5rem;
font-weight: bold;
}
.markdown-content h1 { font-size: 1.5rem; }
.markdown-content h2 { font-size: 1.25rem; }
.markdown-content h3 { font-size: 1.125rem; }
.markdown-content code {
background: #f1f5f9;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
font-family: 'Courier New', monospace;
}
.markdown-content pre {
background: #1e293b;
color: #e2e8f0;
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
margin: 1rem 0;
}
.markdown-content ul, .markdown-content ol {
margin: 1rem 0;
padding-right: 1.5rem;
}
.markdown-content li {
margin: 0.5rem 0;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- Navigation -->
<nav class="bg-white shadow-lg border-b">
<div class="max-w-7xl mx-auto px-4">
<div class="flex justify-between items-center h-16">
<div class="flex items-center space-x-4 space-x-reverse">
<a href="index.html" class="text-gray-600 hover:text-primary transition-colors">
<i data-feather="arrow-right"></i>
</a>
<i data-feather="message-circle" class="text-primary w-6 h-6"></i>
<h1 class="text-xl font-bold text-gray-800" id="chatTitle">المحادثة</h1>
</div>
<div class="flex items-center space-x-4 space-x-reverse">
<button id="saveChatBtn" class="text-gray-600 hover:text-primary transition-colors">
<i data-feather="save"></i>
</button>
<a href="conversations.html" class="text-gray-600 hover:text-primary transition-colors">
<i data-feather="archive"></i>
</a>
<a href="characters.html" class="text-gray-600 hover:text-primary transition-colors">
<i data-feather="users"></i>
</a>
</div>
</div>
</div>
</nav>
<!-- Character Info Bar -->
<div id="characterInfo" class="bg-gradient-to-r from-primary to-secondary text-white py-2 px-4 hidden">
<div class="max-w-4xl mx-auto flex items-center justify-between">
<div class="flex items-center space-x-3 space-x-reverse">
<i data-feather="user" class="w-4 h-4"></i>
<span id="currentCharacterName">الشخصية المحددة</span>
</div>
<button onclick="changeCharacter()" class="text-white/80 hover:text-white text-sm">
تغيير الشخصية
</button>
</div>
</div>
<!-- Main Chat Area -->
<div class="max-w-4xl mx-auto">
<div class="chat-container flex flex-col">
<!-- Messages Container -->
<div id="messagesContainer" class="messages-container overflow-y-auto p-4 space-y-6">
<!-- Welcome Message -->
<div class="text-center py-8">
<i data-feather="message-circle" class="w-16 h-16 text-gray-300 mx-auto mb-4"></i>
<h2 class="text-2xl font-bold text-gray-800 mb-2">مرحباً بك في GeminiPersona</h2>
<p class="text-gray-600">ابدأ محادثة مع الذكاء الاصطناعي. اختر شخصية من قسم الشخصيات أو ابدأ مباشرة!</p>
</div>
</div>
<!-- Input Area -->
<div class="border-t bg-white p-4">
<form id="messageForm" class="flex space-x-3 space-x-reverse">
<div class="flex-1">
<input type="text" id="messageInput"
placeholder="اكتب رسالتك هنا..."
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent"
autocomplete="off">
</div>
<button type="submit"
class="bg-primary text-white px-6 py-3 rounded-lg hover:bg-primary/90 transition-colors flex items-center space-x-2 space-x-reverse disabled:opacity-50"
id="sendButton">
<i data-feather="send" class="w-4 h-4"></i>
<span>إرسال</span>
</button>
</form>
</div>
</div>
</div>
<!-- Character Selection Modal -->
<div id="characterModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 hidden">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full max-h-[80vh] overflow-y-auto">
<div class="p-6">
<div class="flex justify-between items-center mb-6">
<h3 class="text-xl font-bold text-gray-800">اختر شخصية</h3>
<button onclick="closeCharacterModal()" class="text-gray-400 hover:text-gray-600">
<i data-feather="x" class="w-6 h-6"></i>
</button>
</div>
<div id="charactersList" class="space-y-3">
<!-- Characters will be loaded here -->
</div>
<div class="mt-6 pt-4 border-t">
<button onclick="useDefaultAI()" class="w-full bg-gray-100 text-gray-700 py-3 px-4 rounded-lg hover:bg-gray-200 transition-colors text-center">
استخدام الذكاء الاصطناعي الافتراضي
</button>
</div>
</div>
</div>
</div>
<script>
let messages = [];
let currentCharacter = null;
let isTyping = false;
let currentConversationId = null;
// Load characters from IndexedDB
function loadCharacters() {
const request = indexedDB.open("GeminiPersonaDB", 1);
request.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(['characters'], 'readonly');
const store = transaction.objectStore('characters');
const getAllRequest = store.getAll();
getAllRequest.onsuccess = function() {
const characters = getAllRequest.result;
renderCharacterSelection(characters);
};
};
}
// Render character selection
function renderCharacterSelection(characters) {
const list = document.getElementById('charactersList');
list.innerHTML = characters.map(character => `
<div class="border border-gray-200 rounded-lg p-4 cursor-pointer hover:border-primary transition-colors" onclick="selectCharacter(${character.id})">
<h4 class="font-semibold text-gray-800">${character.name}</h4>
${character.role ? `<p class="text-sm text-gray-600">${character.role}</p>` : ''}
${character.description ? `<p class="text-sm text-gray-500 mt-2">${character.description}</p>` : ''}
</div>
`).join('');
}
// Select character
function selectCharacter(characterId) {
const request = indexedDB.open("GeminiPersonaDB", 1);
request.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(['characters'], 'readonly');
const store = transaction.objectStore('characters');
const getRequest = store.get(characterId);
getRequest.onsuccess = function() {
currentCharacter = getRequest.result;
document.getElementById('characterInfo').classList.remove('hidden');
document.getElementById('currentCharacterName').textContent = currentCharacter.name;
closeCharacterModal();
// Add system message if this is the first message
if (messages.length === 0) {
addSystemMessage(`أنت الآن تتحدث مع ${currentCharacter.name}${currentCharacter.role ? ` - ${currentCharacter.role}` : ''}. ${currentCharacter.instructions || ''}`);
}
};
};
}
// Use default AI
function useDefaultAI() {
currentCharacter = null;
document.getElementById('characterInfo').classList.add('hidden');
closeCharacterModal();
if (messages.length === 0) {
addSystemMessage('أنت الآن تتحدث مع الذكاء الاصطناعي الافتراضي. كيف يمكنني مساعدتك؟');
}
}
// Change character
function changeCharacter() {
loadCharacters();
document.getElementById('characterModal').classList.remove('hidden');
}
// Close character modal
function closeCharacterModal() {
document.getElementById('characterModal').classList.add('hidden');
}
// Add system message
function addSystemMessage(content) {
const message = {
role: 'system',
content: content,
timestamp: new Date().toISOString()
};
messages.push(message);
renderMessages();
}
// Add message to chat
function addMessage(role, content) {
const message = {
role: role,
content: content,
timestamp: new Date().toISOString()
};
messages.push(message);
renderMessages();
// Auto-save if enabled
const settings = JSON.parse(localStorage.getItem('geminiPersonaSettings') || '{}');
if (settings.autoSave !== false) {
autoSaveConversation();
}
}
// Render messages
function renderMessages() {
const container = document.getElementById('messagesContainer');
const markdownEnabled = JSON.parse(localStorage.getItem('geminiPersonaSettings') || '{}').markdownEnabled !== false;
container.innerHTML = messages.map(message => {
if (message.role === 'system') {
return `
<div class="text-center">
<div class="inline-flex items-center space-x-2 space-x-reverse bg-gray-100 text-gray-600 px-4 py-2 rounded-full text-sm">
<i data-feather="info" class="w-3 h-3"></i>
<span>${message.content}</span>
</div>
</div>
`;
}
return `
<div class="flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}">
<div class="max-w-3/4 ${message.role === 'user' ? 'chat-bubble-user' : 'chat-bubble-ai'} p-4">
<div class="${markdownEnabled && message.role === 'assistant' ? 'markdown-content' : ''}">
${markdownEnabled && message.role === 'assistant' ? marked.parse(message.content) : message.content}
</div>
<div class="text-xs opacity-70 mt-2 ${message.role === 'user' ? 'text-blue-100' : 'text-gray-500'}">
${new Date(message.timestamp).toLocaleTimeString('ar-EG', { hour: '2-digit', minute: '2-digit' })}
</div>
</div>
</div>
`;
}).join('');
container.scrollTop = container.scrollHeight;
feather.replace();
}
// Show typing indicator
function showTypingIndicator() {
if (isTyping) return;
isTyping = true;
const container = document.getElementById('messagesContainer');
const indicator = document.createElement('div');
indicator.className = 'flex justify-start';
indicator.innerHTML = `
<div class="chat-bubble-ai p-4">
<div class="typing-indicator">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
</div>
</div>
`;
container.appendChild(indicator);
container.scrollTop = container.scrollHeight;
}
// Hide typing indicator
function hideTypingIndicator() {
isTyping = false;
const container = document.getElementById('messagesContainer');
const indicator = container.querySelector('.typing-indicator');
if (indicator) {
indicator.closest('.flex').remove();
}
}
// Send message to AI
async function sendMessageToAI(message) {
const settings = JSON.parse(localStorage.getItem('geminiPersonaSettings') || '{}');
if (!settings.apiKey) {
addMessage('assistant', '⚠️ يرجى إضافة مفتاح API من صفحة الإعدادات أولاً.');
return;
}
showTypingIndicator();
try {
// Prepare conversation context
const conversationContext = messages
.filter(msg => msg.role !== 'system')
.slice(-10) // Keep last 10 messages for context
.map(msg => ({
role: msg.role === 'user' ? 'user' : 'model',
parts: [{ text: msg.content }]
}));
// Add system prompt if character is selected
let systemInstruction = '';
if (currentCharacter) {
systemInstruction = `أنت ${currentCharacter.name}${currentCharacter.role ? ` - ${currentCharacter.role}` : ''}. ${currentCharacter.description || ''}. ${currentCharacter.instructions || ''}. يجب أن تتصرف وتتحدث كما تم وصف شخصيتك.`;
}
const requestBody = {
contents: conversationContext,
generationConfig: {
temperature: 0.7,
topK: 40,
topP: 0.95,
maxOutputTokens: 1024,
}
};
if (systemInstruction) {
requestBody.systemInstruction = {
parts: [{ text: systemInstruction }]
};
}
const model = settings.model === 'glm-4.5-flash' ? 'glm-4-5-flash' : 'gemini-2.0-flash-exp';
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${settings.apiKey}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.candidates && data.candidates[0] && data.candidates[0].content) {
const aiResponse = data.candidates[0].content.parts[0].text;
hideTypingIndicator();
addMessage('assistant', aiResponse);
} else {
throw new Error('No response from AI');
}
} catch (error) {
hideTypingIndicator();
console.error('Error:', error);
addMessage('assistant', '❌ حدث خطأ في الاتصال بالذكاء الاصطناعي. يرجى التحقق من مفتاح API وإعدادات الاتصال.');
}
}
// Auto-save conversation
function autoSaveConversation() {
if (messages.length < 2) return; // Don't save empty conversations
const conversation = {
title: messages.find(m => m.role === 'user')?.content.substring(0, 50) + '...' || 'محادثة جديدة',
messages: messages,
character: currentCharacter,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
const request = indexedDB.open("GeminiPersonaDB", 1);
request.onsuccess = function(event) {
const db = event.target.result;
const transaction = db.transaction(['conversations'], 'readwrite');
const store = transaction.objectStore('conversations');
if (currentConversationId) {
conversation.id = currentConversationId;
store.put(conversation);
} else {
const addRequest = store.add(conversation);
addRequest.onsuccess = function() {
currentConversationId = addRequest.result;
};
}
};
}
// Manual save
document.getElementById('saveChatBtn').addEventListener('click', function() {
autoSaveConversation();
// Show success message
const btn = this;
const originalHTML = btn.innerHTML;
btn.innerHTML = '<i data-feather="check" class="w-4 h-4 text-green-500"></i>';
feather.replace();
setTimeout(() => {
btn.innerHTML = originalHTML;
feather.replace();
}, 2000);
});
// Handle form submission
document.getElementById('messageForm').addEventListener('submit', async function(e) {
e.preventDefault();
const messageInput = document.getElementById('messageInput');
const message = messageInput.value.trim();
if (!message) return;
// Add user message
addMessage('user', message);
messageInput.value = '';
// Send to AI
await sendMessageToAI(message);
});
// Load restored conversation
function loadRestoredConversation() {
const restored = localStorage.getItem('restoredConversation');
if (restored) {
const conversation = JSON.parse(restored);
messages = conversation.messages;
currentCharacter = conversation.character;
currentConversationId = conversation.id;
if (currentCharacter) {
document.getElementById('characterInfo').classList.remove('hidden');
document.getElementById('currentCharacterName').textContent = currentCharacter.name;
}
renderMessages();
localStorage.removeItem('restoredConversation');
}
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
feather.replace();
loadCharacters();
loadRestoredConversation();
// Focus on input
document.getElementById('messageInput').focus();
});
</script>
</body>
</html>