Spaces:
Running
Running
| <html lang="en" class="dark"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Law bot</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --primary-purple: #9b87f5; | |
| --dark-purple: #1A1F2C; | |
| --light-purple: #D6BCFA; | |
| --neutral-gray: #8E9196; | |
| --light-gray: #C8C8C9; | |
| --off-white: #eee; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| body { | |
| transition: background-color 0.3s, color 0.3s; | |
| } | |
| body.dark { | |
| background-color: var(--dark-purple); | |
| color: var(--off-white); | |
| } | |
| body.light { | |
| background-color: #ffffff; | |
| color: var(--dark-purple); | |
| } | |
| @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap'); | |
| /* Base styles */ | |
| body { | |
| font-family: 'Roboto', sans-serif; | |
| margin: 0; | |
| padding: 0; | |
| display: flex; | |
| background: #f5f5f5; | |
| } | |
| /* Sidebar styles */ | |
| .sidebar { | |
| height: 100vh; | |
| width: 250px; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| background-color: #2c3e50; | |
| padding-top: 20px; | |
| color: #ecf0f1; | |
| } | |
| .sidebar h2 { | |
| text-align: center; | |
| margin-bottom: 20px; | |
| } | |
| .sidebar ul { | |
| list-style-type: none; | |
| padding: 0; | |
| } | |
| .sidebar ul li { | |
| padding: 10px 20px; | |
| } | |
| .sidebar ul li a { | |
| color: #ecf0f1; | |
| text-decoration: none; | |
| display: block; | |
| transition: background 0.3s; | |
| } | |
| .sidebar ul li a:hover { | |
| background: #34495e; | |
| } | |
| /* Content styles */ | |
| .content { | |
| margin-left: 250px; | |
| padding: 20px; | |
| flex: 1; | |
| display: none; | |
| /* Initially hide content */ | |
| } | |
| .content-section { | |
| background: #fff; | |
| padding: 15px; | |
| margin-bottom: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); | |
| display: none; | |
| /* Hide all sections initially */ | |
| } | |
| .content-section.active { | |
| display: block; | |
| /* Show active section */ | |
| } | |
| .content h3 { | |
| margin-top: 0; | |
| color: #333; | |
| } | |
| .content p { | |
| color: #555; | |
| } | |
| .container { | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 2rem; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 2rem; | |
| } | |
| .app-title { | |
| font-size: 2rem; | |
| font-weight: 600; | |
| background: linear-gradient(135deg, var(--primary-purple), var(--light-purple)); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| } | |
| .theme-toggle { | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| padding: 0.5rem; | |
| color: var(--primary-purple); | |
| transition: opacity 0.3s; | |
| } | |
| .theme-toggle:hover { | |
| opacity: 0.8; | |
| } | |
| .theme-toggle svg { | |
| width: 24px; | |
| height: 24px; | |
| } | |
| .chat-container { | |
| flex-grow: 1; | |
| overflow-y: auto; | |
| margin-bottom: 2rem; | |
| padding: 1rem; | |
| } | |
| .message { | |
| margin-bottom: 1.5rem; | |
| padding: 1rem; | |
| border-radius: 8px; | |
| animation: fadeIn 0.3s ease-in; | |
| } | |
| .user-message { | |
| background-color: rgba(155, 135, 245, 0.1); | |
| } | |
| .ai-message { | |
| background-color: rgba(255, 255, 255, 0.05); | |
| border: 1px solid rgba(200, 200, 201, 0.2); | |
| } | |
| body.light .user-message { | |
| background-color: var(--off-white); | |
| } | |
| body.light .ai-message { | |
| background-color: #ffffff; | |
| border: 1px solid var(--light-gray); | |
| } | |
| .message-header { | |
| display: flex; | |
| align-items: center; | |
| margin-bottom: 0.5rem; | |
| font-weight: 500; | |
| } | |
| .user-icon, | |
| .ai-icon { | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 50%; | |
| margin-right: 0.5rem; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 12px; | |
| } | |
| .user-icon { | |
| background-color: var(--primary-purple); | |
| color: white; | |
| } | |
| .ai-icon { | |
| background-color: var(--light-purple); | |
| color: var(--dark-purple); | |
| } | |
| .input-container { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| padding: 1rem; | |
| background-color: var(--dark-purple); | |
| border-top: 1px solid rgba(200, 200, 201, 0.2); | |
| } | |
| body.light .input-container { | |
| background-color: #ffffff; | |
| border-top: 1px solid var(--light-gray); | |
| } | |
| .input-wrapper { | |
| max-width: 800px; | |
| margin: 0 auto; | |
| position: relative; | |
| } | |
| #messageInput { | |
| width: 100%; | |
| padding: 1rem; | |
| padding-right: 3rem; | |
| border: 1px solid rgba(200, 200, 201, 0.3); | |
| border-radius: 8px; | |
| font-size: 1rem; | |
| outline: none; | |
| transition: border-color 0.3s; | |
| background-color: transparent; | |
| color: inherit; | |
| } | |
| body.light #messageInput { | |
| border: 1px solid var(--light-gray); | |
| } | |
| #messageInput:focus { | |
| border-color: var(--primary-purple); | |
| } | |
| #sendButton { | |
| position: absolute; | |
| right: 0.5rem; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| padding: 0.5rem; | |
| color: var(--primary-purple); | |
| opacity: 0.8; | |
| transition: opacity 0.3s; | |
| } | |
| #sendButton:hover { | |
| opacity: 1; | |
| } | |
| .typing-indicator { | |
| padding: 1rem; | |
| color: var(--neutral-gray); | |
| font-style: italic; | |
| display: none; | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .connection-status { | |
| position: fixed; | |
| top: 1rem; | |
| right: 1rem; | |
| padding: 0.5rem 1rem; | |
| border-radius: 4px; | |
| font-size: 0.875rem; | |
| display: none; | |
| } | |
| .connection-status.connected { | |
| background-color: #4ade80; | |
| color: white; | |
| } | |
| .connection-status.disconnected { | |
| background-color: #ef4444; | |
| color: white; | |
| } | |
| /* Styling for formatted text */ | |
| .message-content { | |
| line-height: 1.5; | |
| } | |
| .message-content h1, | |
| .message-content h2, | |
| .message-content h3 { | |
| margin: 1rem 0 0.5rem; | |
| font-weight: 600; | |
| } | |
| .message-content h1 { | |
| font-size: 1.5rem; | |
| } | |
| .message-content h2 { | |
| font-size: 1.25rem; | |
| } | |
| .message-content h3 { | |
| font-size: 1.1rem; | |
| } | |
| .message-content ul, | |
| .message-content ol { | |
| margin-left: 1.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| .message-content a { | |
| color: var(--primary-purple); | |
| text-decoration: none; | |
| } | |
| .message-content a:hover { | |
| text-decoration: underline; | |
| } | |
| </style> | |
| </head> | |
| <body class="dark"> | |
| <div class="sidebar"> | |
| <h2>Legislations</h2> | |
| <ul> | |
| <li><a href="#" onclick="showContent('dowry')">The Dowry Prohibition Act, 1961</a></li> | |
| <li><a href="#" onclick="showContent('equal-remuneration')">The Equal Remuneration Act, 1976</a></li> | |
| <li><a href="#" onclick="showContent('domestic-violence')">The Protection of Women from Domestic Violence | |
| Act, 2005</a></li> | |
| <li><a href="#" onclick="showContent('sexual-harassment')">The Sexual Harassment of Women at Workplace Act, | |
| 2013</a></li> | |
| <li><a href="#" onclick="showContent('maternity-benefit')">The Maternity Benefit Act, 1961</a></li> | |
| <li><a href="#" onclick="showContent('hindu-succession')">The Hindu Succession (Amendment) Act, 2005</a> | |
| </li> | |
| </ul> | |
| </div> | |
| <div class="container"> | |
| <header class="header"> | |
| <h1 class="app-title">Law Bot</h1> | |
| <button class="theme-toggle" id="themeToggle" aria-label="Toggle theme"> | |
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" /> | |
| </svg> | |
| </button> | |
| </header> | |
| <div class="chat-container" id="chatContainer"> | |
| <!-- Messages will be added here dynamically --> | |
| </div> | |
| <div class="typing-indicator" id="typingIndicator"> | |
| Law Bot is thinking... | |
| </div> | |
| </div> | |
| <div class="input-container"> | |
| <div class="input-wrapper"> | |
| <input type="text" id="messageInput" placeholder="Ask anything..." autocomplete="off"> | |
| <button id="sendButton"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" | |
| stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="22" y1="2" x2="11" y2="13"></line> | |
| <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="connection-status" id="connectionStatus"></div> | |
| <script> | |
| function showContent(sectionId) { | |
| // Display the content container | |
| document.querySelector('.content').style.display = 'block'; | |
| // Hide all content sections | |
| var sections = document.querySelectorAll('.content-section'); | |
| sections.forEach(function (section) { | |
| section.classList.remove('active'); | |
| }); | |
| // Show the selected section | |
| var activeSection = document.getElementById(sectionId); | |
| activeSection.classList.add('active'); | |
| } | |
| let ws; | |
| const chatContainer = document.getElementById('chatContainer'); | |
| const messageInput = document.getElementById('messageInput'); | |
| const sendButton = document.getElementById('sendButton'); | |
| const typingIndicator = document.getElementById('typingIndicator'); | |
| const connectionStatus = document.getElementById('connectionStatus'); | |
| // Keep track of current conversation | |
| let currentUserMessage = ''; | |
| let currentAiMessage = ''; | |
| let currentAiMessageElement = null; | |
| function formatText(text) { | |
| return text | |
| .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>") // Bold for **text** | |
| .replace(/\*(.*?)\*/g, "<em>$1</em>") // Italic for *text* | |
| .replace(/### (.*?)\n/g, "<h3>$1</h3>") // Heading 3 for ### text | |
| .replace(/## (.*?)\n/g, "<h2>$1</h2>") // Heading 2 for ## text | |
| .replace(/# (.*?)\n/g, "<h1>$1</h1>") // Heading 1 for # text | |
| .replace(/\n/g, "<br>") // Newline for line breaks | |
| .replace(/^\d+\.\s(.*?)$/gm, "<li>$1</li>") // Numbered list | |
| .replace(/<li>(.*?)<\/li>(?!<li>)/g, "<ul><li>$1</li></ul>") // Wrap in <ul> if not already | |
| .replace(/\[([^\]]+)\]\((https?:\/\/[^\)]+)\)/g, `<a href="$2" target="_blank">$1</a>`); // Markdown-style links | |
| } | |
| function connectWebSocket() { | |
| const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; | |
| ws = new WebSocket(`${protocol}//${window.location.host}/conversational_chat`); | |
| ws.onopen = () => { | |
| console.log('Connected to WebSocket'); | |
| connectionStatus.textContent = 'Connected'; | |
| connectionStatus.className = 'connection-status connected'; | |
| connectionStatus.style.display = 'block'; | |
| setTimeout(() => { | |
| connectionStatus.style.display = 'none'; | |
| }, 3000); | |
| }; | |
| ws.onclose = () => { | |
| console.log('Disconnected from WebSocket'); | |
| connectionStatus.textContent = 'Disconnected'; | |
| connectionStatus.className = 'connection-status disconnected'; | |
| connectionStatus.style.display = 'block'; | |
| setTimeout(() => { | |
| reconnectWebSocket(); | |
| }, 5000); | |
| }; | |
| ws.onerror = (error) => { | |
| console.error('WebSocket Error:', error); | |
| }; | |
| ws.onmessage = (event) => { | |
| console.log('Received message:', event.data); | |
| typingIndicator.style.display = 'none'; | |
| // Update or create AI message | |
| if (!currentAiMessageElement) { | |
| currentAiMessage = event.data; | |
| addMessage(currentAiMessage, 'ai'); | |
| } else { | |
| currentAiMessage += event.data; | |
| // Apply formatting to the full message | |
| const formattedMessage = formatText(currentAiMessage); | |
| currentAiMessageElement.querySelector('.message-content').innerHTML = formattedMessage; | |
| } | |
| }; | |
| } | |
| function reconnectWebSocket() { | |
| console.log('Attempting to reconnect...'); | |
| connectWebSocket(); | |
| } | |
| function addMessage(content, type) { | |
| // Reset current message tracking when adding a new user message | |
| if (type === 'user') { | |
| currentUserMessage = content; | |
| currentAiMessage = ''; | |
| currentAiMessageElement = null; | |
| } | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${type}-message`; | |
| const header = document.createElement('div'); | |
| header.className = 'message-header'; | |
| const icon = document.createElement('div'); | |
| icon.className = `${type}-icon`; | |
| icon.textContent = type === 'user' ? 'U' : 'L'; | |
| const name = document.createElement('span'); | |
| name.textContent = type === 'user' ? 'You' : 'Law Bot'; | |
| header.appendChild(icon); | |
| header.appendChild(name); | |
| const text = document.createElement('div'); | |
| text.className = 'message-content'; | |
| // Apply formatting for AI messages | |
| if (type === 'ai') { | |
| text.innerHTML = formatText(content); | |
| } else { | |
| text.textContent = content; | |
| } | |
| messageDiv.appendChild(header); | |
| messageDiv.appendChild(text); | |
| chatContainer.appendChild(messageDiv); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| // Store reference to AI message element | |
| if (type === 'ai') { | |
| currentAiMessageElement = messageDiv; | |
| } | |
| } | |
| function sendMessage() { | |
| const message = messageInput.value.trim(); | |
| if (message && ws.readyState === WebSocket.OPEN) { | |
| addMessage(message, 'user'); | |
| ws.send(message); | |
| messageInput.value = ''; | |
| typingIndicator.style.display = 'block'; | |
| } | |
| } | |
| sendButton.addEventListener('click', sendMessage); | |
| messageInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| sendMessage(); | |
| } | |
| }); | |
| // Theme toggle functionality | |
| const themeToggle = document.getElementById('themeToggle'); | |
| const body = document.body; | |
| const moonIcon = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" /> | |
| </svg>`; | |
| const sunIcon = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" /> | |
| </svg>`; | |
| themeToggle.addEventListener('click', () => { | |
| body.classList.toggle('dark'); | |
| body.classList.toggle('light'); | |
| themeToggle.innerHTML = body.classList.contains('dark') ? moonIcon : sunIcon; | |
| }); | |
| // Initial connection | |
| connectWebSocket(); | |
| </script> | |
| </body> | |
| </html> |