| <!DOCTYPE html> |
| <html lang="hi"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Vedika AI - Premium Workspace</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css"> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script> |
|
|
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap'); |
| body { font-family: 'Inter', sans-serif; background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%); color: #f8fafc; height: 100vh; margin: 0; overflow: hidden; } |
| .glass-panel { background: rgba(255, 255, 255, 0.03); backdrop-filter: blur(16px); border: 1px solid rgba(255, 255, 255, 0.05); box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); } |
| .chat-container { height: calc(100vh - 180px); overflow-y: auto; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.2) transparent; } |
| .chat-container::-webkit-scrollbar { width: 6px; } |
| .chat-container::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 10px; } |
| .user-message { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border-radius: 20px 20px 0 20px; } |
| .ai-message { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 20px 20px 20px 0; } |
| .typing-indicator span { display: inline-block; width: 8px; height: 8px; background-color: #60a5fa; border-radius: 50%; margin-right: 4px; animation: typing 1.4s infinite both; } |
| .typing-indicator span:nth-child(1) { animation-delay: -0.32s; } .typing-indicator span:nth-child(2) { animation-delay: -0.16s; } |
| @keyframes typing { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } } |
| .markdown-body pre { background: #1e1e1e; padding: 15px; border-radius: 10px; overflow-x: auto; margin-top: 10px; } |
| .markdown-body code { font-family: 'Courier New', monospace; color: #a78bfa; } |
| </style> |
| </head> |
| <body class="flex flex-col h-screen"> |
|
|
| <header class="glass-panel py-4 px-8 flex justify-between items-center z-10"> |
| <div class="flex items-center gap-3"> |
| <div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-xl font-bold shadow-lg shadow-blue-500/50">V</div> |
| <div><h1 class="text-xl font-bold tracking-wide">Vedika AI</h1><p class="text-xs text-blue-300">Enterprise Edition - Designed by Divy Patel</p></div> |
| </div> |
| |
| <div class="flex items-center gap-4"> |
| <select id="modelSelector" class="glass-panel bg-transparent text-sm text-white px-4 py-2 rounded-lg outline-none cursor-pointer"> |
| <option value="FLASH" class="text-black">⚡ Vedika Flash</option> |
| <option value="PRO" class="text-black">🧠 Vedika Pro</option> |
| <option value="DEEP_THINKER" class="text-black">🤔 Deep Thinker</option> |
| <option value="IMAGE" class="text-black">🎨 Chitrakaar</option> |
| <option value="CODE" class="text-black">💻 Code Genesis</option> |
| <option value="COMPOUND" class="text-black">🌌 Next Gen Compound</option> |
| </select> |
| |
| <div id="tokenUI" class="flex items-center gap-2"> |
| <button onclick="generateAndSaveToken()" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg text-sm font-semibold transition-all shadow-lg flex items-center gap-2"> |
| <i class="fa-solid fa-key"></i> Generate Security Token |
| </button> |
| </div> |
| <div id="tokenActiveUI" class="hidden flex items-center gap-2 text-green-400 text-sm font-semibold"> |
| <i class="fa-solid fa-shield-check"></i> <span id="tokenDisplay">Secured</span> |
| <button onclick="clearToken()" class="text-red-400 hover:text-red-300 ml-2" title="Revoke Token"><i class="fa-solid fa-power-off"></i></button> |
| </div> |
| </div> |
| </header> |
|
|
| <main class="flex-1 overflow-hidden relative w-full max-w-6xl mx-auto p-4"> |
| <div id="chatBox" class="chat-container w-full h-full pr-4 pb-20 flex flex-col gap-6"> |
| <div class="flex w-full mt-4"> |
| <div class="ai-message glass-panel p-5 max-w-3xl text-sm md:text-base markdown-body shadow-lg"> |
| <p>नमस्कार आदरणीय दिव्य जी! 🙏</p> |
| <p>मैं <b>Vedika AI</b> हूँ। आपका एंटरप्राइज-ग्रेड सर्वर और सिक्योरिटी सिस्टम इसी स्पेस में सफलतापूर्वक लाइव है।</p> |
| </div> |
| </div> |
| </div> |
| </main> |
|
|
| <footer class="fixed bottom-0 w-full p-4 z-20"> |
| <div class="max-w-4xl mx-auto glass-panel rounded-2xl p-2 flex items-end gap-2 shadow-2xl relative"> |
| <input type="file" id="fileInput" class="hidden" accept="image/*, audio/*, video/*" onchange="handleFileSelect(event)"> |
| <button onclick="document.getElementById('fileInput').click()" class="p-4 text-gray-400 hover:text-blue-400 transition-colors rounded-xl hover:bg-white/5"><i class="fa-solid fa-paperclip text-xl"></i></button> |
| |
| <div id="filePreview" class="hidden absolute -top-12 left-4 glass-panel px-4 py-2 rounded-lg text-xs flex items-center gap-2 text-blue-300"> |
| <i class="fa-solid fa-file"></i> <span id="fileName">file.jpg</span> |
| <button onclick="removeFile()" class="text-red-400 hover:text-red-500 ml-2"><i class="fa-solid fa-times"></i></button> |
| </div> |
|
|
| <textarea id="userInput" rows="1" placeholder="Type your command here..." class="flex-1 bg-transparent text-white outline-none resize-none max-h-32 p-3 text-sm" oninput="this.style.height = ''; this.style.height = this.scrollHeight + 'px'"></textarea> |
|
|
| <button id="sendBtn" onclick="sendMessage()" class="bg-blue-600 hover:bg-blue-500 text-white p-4 rounded-xl transition-all shadow-lg flex items-center justify-center min-w-[60px]"><i class="fa-solid fa-paper-plane text-xl"></i></button> |
| </div> |
| </footer> |
|
|
| <script> |
| const GAS_URL = "https://script.google.com/macros/s/AKfycbx9QkGDBk7dvkbcDAy2Kl1SD8ypYF5JV4GF7V-2IyFEFg1fSR0FDonIhdKA1MMsga2H/exec"; |
| const BACKEND_CHAT_URL = "/chat"; |
| |
| let chatHistory = []; |
| let currentFileBase64 = null; |
| let currentFileType = null; |
| |
| marked.setOptions({ highlight: (code, lang) => hljs.highlight(code, { language: hljs.getLanguage(lang) ? lang : 'plaintext' }).value }); |
| |
| window.onload = () => { |
| const token = localStorage.getItem("vedika_token"); |
| if (token) activateTokenUI(token); |
| }; |
| |
| function activateTokenUI(token) { |
| document.getElementById("tokenUI").classList.add("hidden"); |
| document.getElementById("tokenActiveUI").classList.remove("hidden"); |
| document.getElementById("tokenDisplay").innerText = "Token: " + token.substring(0,6) + "..."; |
| } |
| |
| async function generateAndSaveToken() { |
| const btn = document.querySelector("#tokenUI button"); |
| btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Generating...'; |
| try { |
| const newToken = crypto.randomUUID(); |
| await fetch(GAS_URL, { method: 'POST', headers: { 'Content-Type': 'text/plain' }, body: JSON.stringify({ action: "generate", token: newToken }) }); |
| localStorage.setItem("vedika_token", newToken); |
| activateTokenUI(newToken); |
| addMessageToUI("System", "नया सुरक्षा टोकन सफलतापूर्वक जनरेट हो गया है।", "ai-message text-green-400"); |
| } catch (error) { |
| alert("टोकन जनरेट करने में समस्या आई।"); |
| btn.innerHTML = '<i class="fa-solid fa-key"></i> Generate Security Token'; |
| } |
| } |
| |
| function clearToken() { |
| localStorage.removeItem("vedika_token"); |
| document.getElementById("tokenUI").classList.remove("hidden"); |
| document.getElementById("tokenActiveUI").classList.add("hidden"); |
| document.querySelector("#tokenUI button").innerHTML = '<i class="fa-solid fa-key"></i> Generate Security Token'; |
| } |
| |
| function handleFileSelect(event) { |
| const file = event.target.files[0]; |
| if (!file) return; |
| const reader = new FileReader(); |
| reader.onload = e => { |
| currentFileBase64 = e.target.result; |
| currentFileType = file.type.split('/')[0]; |
| document.getElementById("fileName").innerText = file.name; |
| document.getElementById("filePreview").classList.remove("hidden"); |
| }; |
| reader.readAsDataURL(file); |
| } |
| |
| function removeFile() { |
| currentFileBase64 = null; currentFileType = null; |
| document.getElementById("fileInput").value = ""; |
| document.getElementById("filePreview").classList.add("hidden"); |
| } |
| |
| async function sendMessage() { |
| const token = localStorage.getItem("vedika_token"); |
| if (!token) return alert("आदरणीय दिव्य जी, कृपया पहले सुरक्षा टोकन जनरेट करें!"); |
| |
| const inputField = document.getElementById("userInput"); |
| let text = inputField.value.trim(); |
| if (!text && !currentFileBase64) return; |
| |
| const sendBtn = document.getElementById("sendBtn"); |
| let userContent = text; |
| if (currentFileBase64) userContent += `\n<${currentFileType} src="${currentFileBase64}" />`; |
| |
| addMessageToUI("You", text + (currentFileBase64 ? " [File Attached]" : ""), "user-message"); |
| chatHistory.push({ role: "user", content: userContent }); |
| |
| inputField.value = ""; inputField.style.height = ''; removeFile(); |
| sendBtn.disabled = true; sendBtn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i>'; |
| |
| const typingId = "typing-" + Date.now(); |
| addTypingIndicator(typingId); |
| |
| try { |
| const response = await fetch(BACKEND_CHAT_URL, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, |
| body: JSON.stringify({ mode: document.getElementById("modelSelector").value, messages: chatHistory }) |
| }); |
| |
| document.getElementById(typingId)?.remove(); |
| |
| if (!response.ok) throw new Error((await response.json()).error || "HTTP error"); |
| |
| const reader = response.body.getReader(); |
| const decoder = new TextDecoder("utf-8"); |
| let aiResponseText = ""; |
| |
| const msgId = "ai-msg-" + Date.now(); |
| createEmptyBubble(msgId, "ai-message"); |
| const msgContainer = document.getElementById(msgId); |
| |
| while (true) { |
| const { done, value } = await reader.read(); |
| if (done) break; |
| const lines = decoder.decode(value, { stream: true }).split("\n"); |
| for (const line of lines) { |
| if (line.startsWith("data: ") && line !== "data: [DONE]") { |
| try { |
| const json = JSON.parse(line.substring(6)); |
| if (json.choices?.[0]?.delta?.content) { |
| aiResponseText += json.choices[0].delta.content; |
| msgContainer.innerHTML = marked.parse(aiResponseText); |
| document.getElementById("chatBox").scrollTop = document.getElementById("chatBox").scrollHeight; |
| } |
| } catch(e) {} |
| } |
| } |
| } |
| chatHistory.push({ role: "assistant", content: aiResponseText }); |
| |
| } catch (error) { |
| document.getElementById(typingId)?.remove(); |
| addMessageToUI("System", "⚠️ त्रुटि: " + error.message, "ai-message text-red-400 border-red-500/50"); |
| if(error.message.includes("Token")) clearToken(); |
| } finally { |
| sendBtn.disabled = false; sendBtn.innerHTML = '<i class="fa-solid fa-paper-plane text-xl"></i>'; |
| } |
| } |
| |
| function addMessageToUI(sender, text, className) { |
| const box = document.getElementById("chatBox"); |
| box.insertAdjacentHTML('beforeend', `<div class="flex w-full mt-4 ${sender === 'You' ? 'justify-end' : ''}"><div class="${className} p-4 max-w-3xl text-sm md:text-base markdown-body shadow-lg">${sender === 'You' ? text.replace(/[&<>'"]/g, tag => ({'&': '&', '<': '<', '>': '>', "'": ''', '"': '"'}[tag])) : marked.parse(text)}</div></div>`); |
| box.scrollTop = box.scrollHeight; |
| } |
| |
| function createEmptyBubble(id, className) { |
| document.getElementById("chatBox").insertAdjacentHTML('beforeend', `<div class="flex w-full mt-4"><div id="${id}" class="${className} p-4 max-w-3xl text-sm md:text-base markdown-body shadow-lg"></div></div>`); |
| } |
| |
| function addTypingIndicator(id) { |
| document.getElementById("chatBox").insertAdjacentHTML('beforeend', `<div id="${id}" class="flex w-full mt-4"><div class="ai-message p-5 max-w-3xl flex items-center shadow-lg"><div class="typing-indicator"><span></span><span></span><span></span></div></div></div>`); |
| document.getElementById("chatBox").scrollTop = document.getElementById("chatBox").scrollHeight; |
| } |
| |
| document.getElementById('userInput').addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); |
| </script> |
| </body> |
| </html> |
|
|