Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Gratitude Journal</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.1/font/bootstrap-icons.min.css"> | |
| <style> | |
| :root { | |
| --bg-primary: #0A0B1E; | |
| --bg-secondary: #12142B; | |
| --accent-primary: #7B68EE; | |
| --accent-secondary: #00D4FF; | |
| --accent-tertiary: #FF69B4; | |
| --text-primary: #E2E8F0; | |
| --text-secondary: #94A3B8; | |
| --border-light: rgba(255, 255, 255, 0.08); | |
| --glass: rgba(255, 255, 255, 0.03); | |
| --card-bg: rgba(18, 20, 43, 0.95); | |
| } | |
| @keyframes gradientFlow { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| @keyframes floatingGlow { | |
| 0% { box-shadow: 0 0 40px rgba(123, 104, 238, 0.2); } | |
| 50% { box-shadow: 0 0 60px rgba(0, 212, 255, 0.2); } | |
| 100% { box-shadow: 0 0 40px rgba(123, 104, 238, 0.2); } | |
| } | |
| body { | |
| font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif; | |
| margin: 0; | |
| min-height: 100vh; | |
| background: linear-gradient(135deg, var(--bg-primary), var(--bg-secondary)); | |
| color: var(--text-primary); | |
| padding: 2rem; | |
| line-height: 1.6; | |
| } | |
| .container { | |
| max-width: 1600px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 2.5rem; | |
| } | |
| .header { | |
| grid-column: 1 / -1; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 1.5rem 2.5rem; | |
| background: linear-gradient(180deg, | |
| rgba(123, 104, 238, 0.05) 0%, | |
| rgba(0, 212, 255, 0.05) 100%); | |
| border-radius: 2rem; | |
| border: 1px solid var(--border-light); | |
| backdrop-filter: blur(20px); | |
| animation: floatingGlow 6s infinite; | |
| } | |
| .title-section { | |
| flex: 1; | |
| } | |
| .header h1 { | |
| font-size: 2.5rem; | |
| margin: 0; | |
| background: linear-gradient(135deg, #FFFFFF 0%, #7B68EE 50%, #00D4FF 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-size: 200% 200%; | |
| animation: gradientFlow 6s ease infinite; | |
| letter-spacing: -1px; | |
| font-weight: 800; | |
| } | |
| .header p { | |
| color: var(--text-secondary); | |
| font-size: 1rem; | |
| margin: 0.5rem 0 0 0; | |
| font-weight: 300; | |
| letter-spacing: 0.5px; | |
| } | |
| .powered-by { | |
| display: flex; | |
| align-items: center; | |
| gap: 2rem; | |
| margin-left: 2rem; | |
| padding-left: 2rem; | |
| border-left: 1px solid var(--border-light); | |
| } | |
| .powered-by a { | |
| opacity: 0.7; | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); | |
| transform: translateY(0); | |
| } | |
| .powered-by a:hover { | |
| opacity: 1; | |
| transform: translateY(-3px); | |
| } | |
| .powered-by img { | |
| height: 24px; | |
| /*filter: brightness(0) invert(1);*/ | |
| } | |
| .input-card { | |
| background: var(--card-bg); | |
| border-radius: 2rem; | |
| padding: 2.5rem; | |
| border: 1px solid var(--border-light); | |
| backdrop-filter: blur(20px); | |
| height: calc(100vh - 200px); | |
| display: flex; | |
| flex-direction: column; | |
| transition: all 0.3s ease; | |
| } | |
| .input-card:hover { | |
| box-shadow: 0 25px 50px rgba(123, 104, 238, 0.1); | |
| } | |
| textarea { | |
| flex: 1; | |
| background: rgba(255, 255, 255, 0.02); | |
| border: 1px solid var(--border-light); | |
| border-radius: 1.5rem; | |
| padding: 1.5rem; | |
| color: var(--text-primary); | |
| font-size: 1.1rem; | |
| resize: none; | |
| transition: all 0.3s ease; | |
| margin-bottom: 2rem; | |
| } | |
| textarea:focus { | |
| outline: none; | |
| border-color: var(--accent-primary); | |
| box-shadow: 0 0 0 3px rgba(123, 104, 238, 0.1); | |
| } | |
| .media-section { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 1.5rem; | |
| margin-bottom: 2rem; | |
| } | |
| .media-upload { | |
| background: rgba(255, 255, 255, 0.02); | |
| border: 1px solid var(--border-light); | |
| border-radius: 1.5rem; | |
| padding: 2rem; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .media-upload:hover { | |
| border-color: var(--accent-primary); | |
| transform: translateY(-5px); | |
| box-shadow: 0 15px 30px rgba(123, 104, 238, 0.1); | |
| } | |
| .media-upload i { | |
| font-size: 2.5rem; | |
| background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| margin-bottom: 1rem; | |
| display: block; | |
| } | |
| .generate-button { | |
| background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); | |
| color: white; | |
| border: none; | |
| padding: 1.75rem; | |
| border-radius: 1.5rem; | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| letter-spacing: 1.5px; | |
| text-transform: uppercase; | |
| cursor: pointer; | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .generate-button::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient( | |
| 90deg, | |
| transparent, | |
| rgba(255, 255, 255, 0.2), | |
| transparent | |
| ); | |
| transition: transform 0.6s; | |
| } | |
| .generate-button:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 40px rgba(123, 104, 238, 0.3); | |
| } | |
| .generate-button:hover::before { | |
| transform: translateX(200%); | |
| } | |
| .messages { | |
| background: var(--card-bg); | |
| border-radius: 2rem; | |
| padding: 2.5rem; | |
| border: 1px solid var(--border-light); | |
| backdrop-filter: blur(20px); | |
| height: calc(100vh - 200px); | |
| overflow-y: auto; | |
| } | |
| .message { | |
| background: rgba(255, 255, 255, 0.02); | |
| border: 1px solid var(--border-light); | |
| border-radius: 1.5rem; | |
| padding: 2rem; | |
| margin-bottom: 1.5rem; | |
| transition: all 0.3s ease; | |
| } | |
| .message:hover { | |
| transform: translateX(5px); | |
| border-color: var(--accent-primary); | |
| } | |
| .message.user { | |
| border-left: 4px solid var(--accent-secondary); | |
| } | |
| .message.ai { | |
| border-left: 4px solid var(--accent-primary); | |
| } | |
| .message.img { | |
| border-left: 4px solid var(--accent-tertiary); | |
| } | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(255, 255, 255, 0.02); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: rgba(123, 104, 238, 0.3); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: rgba(123, 104, 238, 0.5); | |
| } | |
| @media (max-width: 1200px) { | |
| .container { | |
| grid-template-columns: 1fr; | |
| } | |
| .header { | |
| flex-direction: column; | |
| text-align: center; | |
| padding: 2rem; | |
| } | |
| .powered-by { | |
| margin: 1.5rem 0 0 0; | |
| padding: 1.5rem 0 0 0; | |
| border-left: none; | |
| border-top: 1px solid var(--border-light); | |
| } | |
| .input-card, .messages { | |
| height: 600px; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .header h1 { | |
| font-size: 2rem; | |
| } | |
| .powered-by { | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| gap: 1.5rem; | |
| } | |
| .media-section { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <div class="title-section"> | |
| <h1>AI Gratitude Journal</h1> | |
| <p>Transform your thoughts into meaningful reflections</p> | |
| </div> | |
| <div class="powered-by"> | |
| <a href="https://groq.com" target="_blank" rel="noopener noreferrer"> | |
| <img src="https://groq.com/wp-content/uploads/2024/03/PBG-mark1-color.svg" alt="Powered by Groq"> | |
| </a> | |
| <a href="https://huggingface.co" target="_blank" rel="noopener noreferrer" > | |
| <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face" > | |
| </a> | |
| <a href="https://falcons.ai" target="_blank" rel="noopener noreferrer"> | |
| <img src="https://falcons.ai/assets/images/icons-red/logo.png" alt="Falcons.ai"> | |
| </a> | |
| <a href="https://blackforestlabs.ai/" target="_blank" rel="noopener noreferrer"> | |
| <img src="https://blackforestlabs.ai/wp-content/uploads/2024/08/white_logo-1280x1213.png" alt="Black Forest Lab"> | |
| </a> | |
| </div> | |
| </div> | |
| <div class="input-card"> | |
| <form id="journalForm"> | |
| <textarea | |
| placeholder="Share your gratitude..." | |
| id="textInput" | |
| style="width: 90%;"></textarea> | |
| <div class="media-section" style="display: none;"> | |
| <div class="media-upload" id="imageUpload"> | |
| <i class="bi bi-image"></i> | |
| <span>Add Images</span> | |
| <input type="file" id="imageInput" multiple accept="image/*" style="display: none;"> | |
| </div> | |
| <div class="media-upload" id="audioUpload"> | |
| <i class="bi bi-mic"></i> | |
| <span>Add Voice Note</span> | |
| <input type="file" id="audioInput" accept="audio/*" style="display: none;"> | |
| </div> | |
| </div> | |
| <button type="submit" class="generate-button"> | |
| <i class="bi bi-stars"></i> | |
| Generate | |
| </button> | |
| </form> | |
| </div> | |
| <div class="messages" id="messages"></div> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const form = document.getElementById('journalForm'); | |
| const textInput = document.getElementById('textInput'); | |
| const imageInput = document.getElementById('imageInput'); | |
| const audioInput = document.getElementById('audioInput'); | |
| const imageUpload = document.getElementById('imageUpload'); | |
| const audioUpload = document.getElementById('audioUpload'); | |
| const messages = document.querySelector('.messages'); | |
| let imageFiles = []; | |
| let audioFile = null; | |
| let ws; | |
| // Function to connect/reconnect WebSocket | |
| function connectWebSocket() { | |
| const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; | |
| ws = new WebSocket(`${protocol}//${window.location.host}/ws`); | |
| ws.onopen = () => { | |
| addMessage('Connected to the server.', 'system'); | |
| }; | |
| ws.onmessage = (event) => { | |
| const message = JSON.parse(event.data); | |
| if (message.type === 'journal') { | |
| const htmlContent = marked.parse(message.content); // Convert markdown to HTML | |
| addMessage(htmlContent, 'ai', true); | |
| } else if (message.type === 'info') { | |
| const htmlContent = marked.parse(message.content); // Convert markdown to HTML | |
| addMessage(htmlContent, 'ai', true); | |
| } else if (message.type === 'image') { | |
| const img = document.createElement('img'); | |
| const imgDiv = document.createElement('div'); | |
| imgDiv.className = `message img`; | |
| if (img){ | |
| img.src = `data:image/png;base64,${message.image}`; // Set Base64 as the image source | |
| img.alt = 'Generated Image'; | |
| img.style.width = '100%'; | |
| imgDiv.appendChild(img); | |
| messages.appendChild(imgDiv); | |
| } else{ | |
| const htmlContent = "The generated image might be inappropraite"; // Convert markdown to HTML | |
| addMessage(htmlContent, 'ai', true); | |
| } | |
| } | |
| }; | |
| ws.onclose = () => { | |
| addMessage('Connection closed. Attempting to reconnect...', 'system'); | |
| setTimeout(connectWebSocket, 5000); // Attempt reconnection after 5 seconds | |
| }; | |
| ws.onerror = (error) => { | |
| console.error('WebSocket error:', error); | |
| }; | |
| } | |
| // Initialize WebSocket connection | |
| connectWebSocket(); | |
| // Handle image upload button | |
| imageUpload.addEventListener('click', () => { | |
| imageInput.click(); | |
| }); | |
| // Handle audio upload button | |
| audioUpload.addEventListener('click', () => { | |
| audioInput.click(); | |
| }); | |
| // Track selected image files | |
| imageInput.addEventListener('change', (e) => { | |
| imageFiles = Array.from(e.target.files); | |
| }); | |
| // Track selected audio file | |
| audioInput.addEventListener('change', (e) => { | |
| audioFile = e.target.files[0]; | |
| }); | |
| // Handle form submission | |
| form.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const data = { text: textInput.value, images: [], audio: null }; | |
| const tasks = []; | |
| // Process images | |
| if (imageFiles.length > 0) { | |
| imageFiles.forEach((file) => { | |
| const reader = new FileReader(); | |
| const promise = new Promise((resolve) => { | |
| reader.onload = () => { | |
| data.images.push(reader.result); | |
| resolve(); | |
| }; | |
| reader.readAsDataURL(file); | |
| }); | |
| tasks.push(promise); | |
| }); | |
| } | |
| // Process audio | |
| if (audioFile) { | |
| const reader = new FileReader(); | |
| const promise = new Promise((resolve) => { | |
| reader.onload = () => { | |
| data.audio = reader.result; | |
| resolve(); | |
| }; | |
| reader.readAsDataURL(audioFile); | |
| }); | |
| tasks.push(promise); | |
| } | |
| // Once all files are processed | |
| Promise.all(tasks).then(() => { | |
| ws.send(JSON.stringify(data)); | |
| addMessage('Your input is being processed...', 'user'); | |
| }); | |
| }); | |
| // Add messages to the UI | |
| function addMessage(content, type, isHtml = false) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${type}`; | |
| if (isHtml) { | |
| messageDiv.innerHTML = content; // Render HTML for markdown | |
| } else { | |
| messageDiv.textContent = content; | |
| } | |
| messages.appendChild(messageDiv); | |
| messages.scrollTop = messages.scrollHeight; | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |