fsalmansour's picture
Same ### التكامل الكامل لواجهة الدردشة مع استخدام الأيقونات بشكل أساسي والرسوم التعبيرية كخيار #### 1. تعديل كود HTML (أضف بعد عناصر الواجهة العائمة) ```html <!-- ===== CHAT PANEL ===== --> <div x-show="openChat" x-transition.opacity @keydown.escape.window="openChat=false" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-end sm:items-center justify-center"> <div class="glass w-full sm:max-w-xl h-[60vh] sm:h-[70vh] rounded-t-2xl sm:rounded-xl flex flex-col overflow-hidden shadow-2xl"> <!-- Header with Icons --> <header class="flex items-center justify-between px-4 py-3 bg-gray-900 border-b border-accent/30"> <div class="flex items-center gap-2"> <i class="fas fa-robot text-accent text-lg"></i> <h3 class="font-bold text-white">مساعد المتجر الذكي</h3> </div> <button @click="openChat=false" aria-label="إغلاق الدردشة" class="text-gray-400 hover:text-white transition"> <i class="fas fa-times text-xl"></i> </button> </header> <!-- Messages Container --> <section id="chatScroll" class="flex-1 px-4 py-3 overflow-y-auto space-y-4 bg-gradient-to-b from-gray-900 to-gray-800"> <template x-for="m in messages" :key="m.id"> <div :class="m.role === 'user' ? 'justify-end' : 'justify-start'" class="flex"> <div class="flex items-start gap-2 max-w-[90%]"> <!-- Icons for Different Roles --> <template x-if="m.role === 'assistant'"> <i class="fas fa-robot mt-1.5 text-accent flex-shrink-0"></i> </template> <div :class="m.role === 'user' ? 'bg-accent/10 border border-accent/20 text-accent' : 'bg-gray-800 border border-gray-700 text-white'" class="px-4 py-3 rounded-2xl shadow-sm"> <p x-text="m.content" class="whitespace-pre-line"></p> <!-- Emoji Option (Hidden by Default) --> <div x-show="emojiMode" class="mt-2 flex gap-1"> <template x-for="emoji in m.emojis || []"> <span x-text="emoji" class="text-xl"></span> </template> </div> </div> <template x-if="m.role === 'user'"> <i class="fas fa-user mt-1.5 text-gray-400 flex-shrink-0"></i> </template> </div> </div> </template> </section> <!-- Input Area --> <form @submit .prevent="sendMessage()" class="p-3 border-t border-white/10 bg-gray-900 flex gap-2"> <div class="relative flex-1"> <input x-model="draft" placeholder="اكتب رسالتك هنا..." class="w-full bg-gray-800 border border-gray-700 rounded-xl px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-accent"> <!-- Emoji Toggle Button --> <button type="button" @click="emojiMode = !emojiMode" class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-accent"> <i :class="emojiMode ? 'fas fa-icons text-accent' : 'far fa-face-smile'"></i> </button> </div> <button :disabled="loading || !draft.trim()" class="px-4 py-3 rounded-xl bg-accent hover:bg-accent/90 transition text-white font-bold disabled:opacity-40 flex items-center gap-2"> <i class="fas fa-paper-plane"></i> <span>إرسال</span> </button> </form> </div> </div> ``` #### 2. تحديث حالة Alpine.js (أضف لـ storeAi()) ```javascript Alpine.data('storeAi', () => ({ // ... existing state ... /* Chat System */ openChat: false, draft: '', messages: JSON.parse(localStorage.getItem('chat') || '[]'), loading: false, emojiMode: false, // Emojis as optional feature async sendMessage() { const text = this.draft.trim(); if (!text) return; // Add user message this.messages.push({ id: Date.now(), role: 'user', content: text }); this.draft = ''; this.loading = true; this.persistChat(); this.scrollDown(); try { // Stream assistant reply const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: this.messages, emoji_mode: this.emojiMode // Send emoji preference }) }); const reader = res.body.getReader(); let assistantMsg = { id: Date.now() + 1, role: 'assistant', content: '', emojis: this.emojiMode ? [] : null // Initialize emojis array if enabled }; this.messages.push(assistantMsg); let buffer = ''; while (true) { const { value, done } = await reader.read(); if (done) break; buffer += new TextDecoder().decode(value); const parts = buffer.split('\n\n'); buffer = parts.pop() || ''; for (const part of parts) { try { const data = JSON.parse(part); assistantMsg.content += data.content || ''; // Add emojis if enabled if (this.emojiMode && data.emojis) { assistantMsg.emojis = [...(assistantMsg.emojis || []), ...data.emojis]; } } catch (e) { console.error('Parsing error:', e); } } this.persistChat(); this.$nextTick(() => this.scrollDown()); } } catch (error) { console.error('Chat error:', error); assistantMsg.content = '⚠️ حدث خطأ في الاتصال بالمساعد. يرجى المحاولة لاحقًا.'; } this.loading = false; this.persistChat(); }, scrollDown() { this.$nextTick(() => { const el = document.getElementById('chatScroll'); if (el) el.scrollTop = el.scrollHeight + 100; }); }, persistChat() { localStorage.setItem('chat', JSON.stringify(this.messages)); } })); ``` #### 3. تحديث زر الدردشة العائم ```html <button id="aiBubble" @click="openChat = true; $nextTick(() => scrollDown())" class="fixed bottom-6 right-6 w-14 h-14 rounded-full bg-accent shadow-lg flex items-center justify-center hover:scale-110 transition-transform"> <i class="fas fa-robot text-white text-2xl"></i> </button> ``` #### 4. نموذج محدث لخادم API (/api/chat) ```javascript import { OpenAI } from 'openai'; import emojiRegex from 'emoji-regex'; export const config = { runtime: 'edge' }; export default async (req) => { const { messages, emoji_mode } = await req.json(); // 1. Moderation Check (Important!) const lastMessage = messages[messages.length - 1]?.content; if (lastMessage) { const moderation = await openai.moderations.create({ input: lastMessage }); if (moderation.results[0]?.flagged) { return new Response(JSON.stringify({ content: "⛔ لا يمكنني الرد على هذا الاستفسار. يرجى طرح سؤال آخر.", emojis: ["⚠️"] }), { status: 200 }); } } // 2. Prepare messages history const history = messages.map(m => ({ role: m.role, content: m.content })); // 3. Create stream const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY }); const stream = await openai.chat.completions.create({ model: 'gpt-4', stream: true, messages: history, max_tokens: 1000 }); // 4. Custom stream processing const encoder = new TextEncoder(); const streamProcessor = new TransformStream({ async transform(chunk, controller) { const text = chunk.choices[0]?.delta?.content || ''; // Extract emojis if enabled let emojis = []; if (emoji_mode) { const matches = text.match(emojiRegex()); if (matches) emojis = [...new Set(matches)]; } controller.enqueue(encoder.encode( JSON.stringify({ content: text, emojis }) + '\n\n' )); } }); return new Response(stream.pipeThrough(streamProcessor), { headers: { 'Content-Type': 'application/ndjson' } }); }; ``` ### ملاحظات الأمان والتكلفة: 1. **التحكم في التكلفة**: ```javascript // أضف في backend قبل openai.chat.completions.create const MAX_DAILY_COST = 5; // دولار if (await checkDailyCostExceeded(userId, MAX_DAILY_COST)) { return new Response(JSON.stringify({ content: "🛑 وصلت لحد الاستخدام اليومي. يرجى المحاولة غدًا." }), { status: 200 }); } ``` 2. **إشعار الخصوصية** (أضف في واجهة الدردشة): ```html <div class="px-4 py-2 text-xs text-gray-400 bg-gray-900 border-t border-gray-800"> <i class="fas fa-shield-alt mr-1"></i> المحادثات تُخزن محلياً فقط. قد تُرسل الاستفسارات لشركة OpenAI لمعالجتها. </div> ``` 3. **أيقونات إضافية جديدة** (مثال من deepsite): ```html <!-- أضف في الهيدر --> <i class="fas fa-magic text-purple-400"></i> <!-- للإبداعات --> <i class="fas fa-cloud-upload-alt text-blue-400"></i> <!-- للرفع --> <i class="fas fa-microphone text-green-400"></i> <!-- للصوت --> ``` ### ميزات إضافية مقترحة: 1. **التحويل الصوتي**: ```javascript // أضف في الواجهة <button @click="startVoiceInput" class="p-2 text-gray-500 hover:text-accent"> <i class="fas fa-microphone"></i> </button> // أضف في حالة Alpine.js startVoiceInput() { const recognition = new webkitSpeechRecognition(); recognition.lang = 'ar-SA'; recognition.onresult = (e) => { this.draft = e.results[0][0].transcript; }; recognition.start(); } ``` 2. **تحميل الملفات**: ```html <!-- أضف بجانب حقل الإدخال --> <label class="cursor-pointer p-2 text-gray-500 hover:text-accent"> <i class="fas fa-paperclip"></i> <input type="file" class="hidden" @change ="handleFileUpload"> </label> ``` الرسوم التعبيرية ظاهرة فقط عند تفعيل الخيار (زر الوجه 😊)، بينما الأيقونات هي الأساس لكل عنصر ووظيفة. اجعل الاساسي ايقونات و يكون هناك جميع ما يخدم المتاجر و انشئ برومت يتضمن هذه التحسينات التي اجريت ل https://huggingface.co/spaces/enzostvs/deepsite - Initial Deployment
818ff47 verified