| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Vaporwave LLM Chat</title> |
| | <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | <script> |
| | tailwind.config = { |
| | theme: { |
| | extend: { |
| | colors: { |
| | vaporPink: '#ff6ad5', |
| | vaporTeal: '#00ffcc', |
| | vaporPurple: '#9d00ff', |
| | }, |
| | fontFamily: { |
| | sans: ['Courier New', 'monospace'], |
| | }, |
| | backgroundImage: { |
| | 'vapor-bg': "url('http://static.photos/abstract/1200x630/42')", |
| | } |
| | } |
| | } |
| | } |
| | </script> |
| | <style> |
| | @keyframes scanline { |
| | 0% { transform: translateY(-100%); } |
| | 100% { transform: translateY(100%); } |
| | } |
| | .scanlines { |
| | position: relative; |
| | overflow: hidden; |
| | } |
| | .scanlines::after { |
| | content: ""; |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | right: 0; |
| | bottom: 0; |
| | background: linear-gradient( |
| | to bottom, |
| | rgba(255,255,255,0) 0%, |
| | rgba(255,255,255,0.05) 50%, |
| | rgba(255,255,255,0) 100% |
| | ); |
| | background-size: 100% 4px; |
| | animation: scanline 5s linear infinite; |
| | pointer-events: none; |
| | } |
| | </style> |
| | </head> |
| | <body class="bg-black text-white font-sans scanlines"> |
| | <div class="min-h-screen flex flex-col" style="background: linear-gradient(135deg, #000000 0%, #1e0033 100%)"> |
| | <div class="container mx-auto px-4 py-8 flex-1 flex flex-col"> |
| | <header class="mb-8 text-center"> |
| | <h1 class="text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-vaporTeal to-vaporPink mb-2"> |
| | MACINTOSH PLUS |
| | </h1> |
| | <p class="text-vaporTeal">LLM CHAT INTERFACE</p> |
| | </header> |
| | <div class="flex-1 flex flex-col bg-black bg-opacity-70 border-2 border-vaporPurple rounded-lg p-4 mb-6"> |
| | <div id="chat-container" class="flex-1 overflow-y-auto mb-4 font-mono text-sm"> |
| | <div class="text-vaporTeal">SYSTEM: Ready for input</div> |
| | </div> |
| | <div class="flex items-center border-t-2 border-vaporPink pt-4"> |
| | <input |
| | id="api-key" |
| | type="password" |
| | placeholder="Enter API Key..." |
| | class="flex-1 bg-black border-2 border-vaporTeal text-white px-4 py-2 mr-2 focus:outline-none focus:border-vaporPink" |
| | > |
| | <input |
| | id="user-input" |
| | type="text" |
| | placeholder="Type your message..." |
| | class="flex-1 bg-black border-2 border-vaporTeal text-white px-4 py-2 mr-2 focus:outline-none focus:border-vaporPink" |
| | > |
| | <button |
| | id="send-btn" |
| | class="bg-vaporPurple hover:bg-vaporPink text-white font-bold py-2 px-6 transition-all duration-300" |
| | > |
| | SEND |
| | </button> |
| | </div> |
| | </div> |
| | <div class="text-center text-vaporTeal text-xs"> |
| | <p>Floral Shoppe aesthetic interface for LLM communication</p> |
| | <p class="text-vaporPink mt-1">Using OpenAI GPT-3.5 Turbo API</p> |
| | </div> |
| | </div> |
| | </div> |
| | <script> |
| | let selectedMessageId = null; |
| | const messages = []; |
| | function renderMessages() { |
| | const chatContainer = document.getElementById('chat-container'); |
| | chatContainer.innerHTML = ''; |
| | messages.forEach((msg, idx) => { |
| | const msgDiv = document.createElement('div'); |
| | msgDiv.id = `msg-${idx}`; |
| | msgDiv.className = `mb-2 ${msg.role === 'user' ? 'text-vaporPink' : 'text-vaporTeal'} relative group`; |
| | |
| | const msgContent = document.createElement('div'); |
| | msgContent.textContent = `${msg.role === 'user' ? 'YOU:' : 'AI:'} ${msg.content}`; |
| | msgDiv.appendChild(msgContent); |
| | |
| | if (msg.role !== 'system') { |
| | const btnContainer = document.createElement('div'); |
| | btnContainer.className = 'absolute right-0 top-0 opacity-100 flex gap-1 bg-black bg-opacity-70 p-1 rounded'; |
| | const branchBtn = document.createElement('button'); |
| | branchBtn.className = 'bg-vaporPurple hover:bg-vaporPink text-white text-xs px-2 py-1'; |
| | branchBtn.textContent = 'Branch'; |
| | branchBtn.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | document.getElementById('user-input').value = msg.content; |
| | }); |
| | |
| | const editBtn = document.createElement('button'); |
| | editBtn.className = 'bg-vaporPurple hover:bg-vaporPink text-white text-xs px-2 py-1'; |
| | editBtn.textContent = 'Edit'; |
| | editBtn.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | const newContent = prompt('Edit message:', msg.content); |
| | if (newContent !== null) { |
| | msg.content = newContent; |
| | renderMessages(); |
| | } |
| | }); |
| | |
| | const deleteBtn = document.createElement('button'); |
| | deleteBtn.className = 'bg-vaporPurple hover:bg-vaporPink text-white text-xs px-2 py-1'; |
| | deleteBtn.textContent = 'Delete'; |
| | deleteBtn.addEventListener('click', (e) => { |
| | e.stopPropagation(); |
| | if (confirm('Delete this message?')) { |
| | messages.splice(idx, 1); |
| | renderMessages(); |
| | } |
| | }); |
| | |
| | btnContainer.appendChild(branchBtn); |
| | btnContainer.appendChild(editBtn); |
| | btnContainer.appendChild(deleteBtn); |
| | msgDiv.appendChild(btnContainer); |
| | } |
| | |
| | msgDiv.addEventListener('click', () => { |
| | document.querySelectorAll('.selected').forEach(el => el.classList.remove('selected')); |
| | msgDiv.classList.add('selected'); |
| | selectedMessageId = idx; |
| | }); |
| | chatContainer.appendChild(msgDiv); |
| | }); |
| | } |
| | document.getElementById('send-btn').addEventListener('click', async () => { |
| | const apiKey = document.getElementById('api-key').value; |
| | const userInput = document.getElementById('user-input').value; |
| | const chatContainer = document.getElementById('chat-container'); |
| | |
| | if (!apiKey || !userInput) return; |
| | |
| | messages.push({ role: 'user', content: userInput }); |
| | renderMessages(); |
| | messages.push({ role: 'system', content: 'Processing...' }); |
| | renderMessages(); |
| | document.getElementById('user-input').value = ''; |
| | |
| | try { |
| | |
| | const response = await fetch('https://api.openai.com/v1/chat/completions', { |
| | method: 'POST', |
| | headers: { |
| | 'Content-Type': 'application/json', |
| | 'Authorization': `Bearer ${apiKey}` |
| | }, |
| | body: JSON.stringify({ |
| | model: "gpt-3.5-turbo", |
| | messages: [{"role": "user", "content": userInput}], |
| | temperature: 0.7 |
| | }) |
| | }); |
| | |
| | const data = await response.json(); |
| | messages.pop(); |
| | messages.push({ role: 'ai', content: data.choices[0].message.content }); |
| | renderMessages(); |
| | } catch (error) { |
| | messages.pop(); |
| | messages.push({ role: 'system', content: `ERROR: ${error.message}` }); |
| | renderMessages(); |
| | } |
| | }); |
| | |
| | |
| | messages.push( |
| | { role: 'system', content: 'Ready for input' }, |
| | { role: 'ai', content: 'Welcome to the vaporwave chat interface! Try branching, editing or deleting messages.' } |
| | ); |
| | renderMessages(); |
| | </script> |
| | <style> |
| | .selected { |
| | border-left: 3px solid #ff6ad5; |
| | padding-left: 5px; |
| | background-color: rgba(157, 0, 255, 0.1); |
| | } |
| | #chat-container div:hover { |
| | background-color: rgba(157, 0, 255, 0.05); |
| | } |
| | </style> |
| | </body> |
| | </html> |