Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>VisMem AI // Recursive Visual Memory</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <!-- Markdown & Syntax Highlighting --> | |
| <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=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap'); | |
| body { background-color: #050505; color: #a29bfe; font-family: 'Space Mono', monospace; } | |
| .pixelated { | |
| image-rendering: pixelated; | |
| width: 100%; | |
| border: 1px solid #6c5ce7; | |
| box-shadow: 0 0 15px rgba(108, 92, 231, 0.2); | |
| background: #000; | |
| } | |
| .panel { background: #111; border: 1px solid #333; padding: 15px; border-radius: 4px; } | |
| .btn { background: #1a1a1a; border: 1px solid #6c5ce7; color: #6c5ce7; padding: 6px 12px; font-size: 10px; transition: 0.2s; text-transform: uppercase; } | |
| .btn:hover { background: #6c5ce7; color: #000; cursor: pointer; } | |
| /* Chat Styling */ | |
| .chat-bubble { padding: 10px; margin-bottom: 12px; font-size: 12px; border-left: 2px solid #6c5ce7; background: #0a0a0a; overflow-wrap: break-word;} | |
| .system-msg { color: #fab1a0; font-style: italic; display: block; margin-top: 5px; border-top: 1px dashed #333; padding-top: 5px; } | |
| /* Markdown Overrides */ | |
| .prose p { margin-bottom: 0.5em; } | |
| .prose pre { | |
| background: #1e1e1e ; | |
| padding: 10px; | |
| border-radius: 4px; | |
| overflow-x: auto; | |
| border: 1px solid #333; | |
| margin: 10px 0; | |
| } | |
| .prose code { | |
| font-family: 'Courier New', monospace; | |
| font-size: 11px; | |
| color: #e0e0e0; | |
| } | |
| .prose ul { list-style-type: disc; margin-left: 20px; } | |
| .prose ol { list-style-type: decimal; margin-left: 20px; } | |
| .prose strong { color: #fff; } | |
| .prose a { color: #6c5ce7; text-decoration: underline; } | |
| /* Modal for Inspection */ | |
| #inspector { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; height: 80%; background: #000; border: 2px solid #6c5ce7; z-index: 50; display: none; padding: 20px; overflow: auto; } | |
| .close-btn { position: absolute; top: 10px; right: 10px; color: red; cursor: pointer; } | |
| </style> | |
| </head> | |
| <body class="p-4 md:p-8 max-w-7xl mx-auto"> | |
| <!-- Header --> | |
| <div class="flex justify-between items-end mb-6 border-b border-[#333] pb-4"> | |
| <div> | |
| <h1 class="text-2xl font-bold text-white tracking-widest">VISMEM_AI <span class="text-xs text-[#6c5ce7]">v1.0</span></h1> | |
| <p class="text-gray-500 text-[10px]">RECURSIVE VISUAL STORAGE // MARKDOWN ENABLED</p> | |
| </div> | |
| <div class="text-right"> | |
| <div class="text-[10px] text-green-500">ENGINE: ACTIVE</div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 lg:grid-cols-12 gap-6"> | |
| <!-- VISUALIZERS --> | |
| <div class="lg:col-span-4 flex flex-col gap-6"> | |
| <!-- History --> | |
| <div class="panel"> | |
| <div class="flex justify-between items-center mb-2"> | |
| <h2 class="text-xs font-bold text-white">EPISODIC LOG</h2> | |
| <button onclick="inspect('history')" class="btn">DECODE</button> | |
| </div> | |
| <div class="relative w-full aspect-square mb-2"> | |
| <img id="img-history" src="" class="pixelated"> | |
| </div> | |
| <div class="text-[10px] text-gray-500 flex justify-between"> | |
| <span id="stat-history">--</span> | |
| <span id="usage-history">--</span> | |
| </div> | |
| </div> | |
| <button onclick="wipe()" class="btn border-red-900 text-red-500 hover:bg-red-900 hover:text-white"> | |
| HARD RESET (WIPE ALL) | |
| </button> | |
| </div> | |
| <!-- CHAT --> | |
| <div class="lg:col-span-8 flex flex-col h-[85vh]"> | |
| <div id="chat-log" class="panel flex-1 overflow-y-auto mb-4 font-mono text-gray-300"> | |
| <div class="text-center text-gray-700 text-xs mt-20"> | |
| // SYSTEM READY. WAITING FOR INPUT. | |
| </div> | |
| </div> | |
| <form id="chat-form" class="flex gap-2"> | |
| <input type="text" id="user-input" class="bg-[#111] border border-[#333] text-white p-3 flex-1 outline-none focus:border-[#6c5ce7]" placeholder="Type your message..." autocomplete="off"> | |
| <button type="submit" class="btn px-6 font-bold text-white bg-[#6c5ce7] text-black hover:bg-white">SEND</button> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- Inspector Modal --> | |
| <div id="inspector"> | |
| <div class="close-btn" onclick="closeInspect()">[X] CLOSE</div> | |
| <h2 class="text-xl font-bold mb-4 text-white" id="inspect-title">MEMORY DUMP</h2> | |
| <div id="inspect-content" class="font-mono text-xs text-green-400 whitespace-pre-wrap"></div> | |
| </div> | |
| <script> | |
| const chatLog = document.getElementById('chat-log'); | |
| const userInput = document.getElementById('user-input'); | |
| // Configure Marked.js with Highlight.js | |
| marked.setOptions({ | |
| highlight: function(code, lang) { | |
| const language = hljs.getLanguage(lang) ? lang : 'plaintext'; | |
| return hljs.highlight(code, { language }).value; | |
| }, | |
| langPrefix: 'hljs language-', | |
| breaks: true, | |
| gfm: true | |
| }); | |
| setInterval(refreshVisuals, 2000); | |
| async function refreshVisuals() { | |
| try { | |
| const r2 = await fetch('/visualize/history'); | |
| const d2 = await r2.json(); | |
| document.getElementById('img-history').src = d2.image; | |
| document.getElementById('stat-history').textContent = d2.stats; | |
| document.getElementById('usage-history').textContent = d2.usage; | |
| } catch(e) {} | |
| } | |
| async function inspect(type) { | |
| document.getElementById('inspector').style.display = 'block'; | |
| document.getElementById('inspect-title').innerText = `DECODING ${type.toUpperCase()} SECTOR...`; | |
| document.getElementById('inspect-content').innerText = "Reading bytes..."; | |
| const res = await fetch(`/inspect/${type}`); | |
| const data = await res.json(); | |
| document.getElementById('inspect-content').innerText = data.content.join('\n\n'); | |
| } | |
| function closeInspect() { | |
| document.getElementById('inspector').style.display = 'none'; | |
| } | |
| document.getElementById('chat-form').addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const text = userInput.value.trim(); | |
| if(!text) return; | |
| // Add User Message (No MD for user to keep it raw/clean) | |
| addMsg("USER", text); | |
| userInput.value = ""; | |
| // Create AI Message Container | |
| const aiDiv = addMsg("AI", "..."); | |
| const aiContentDiv = document.createElement("div"); | |
| aiContentDiv.className = "prose"; // Tailwind typography class hook | |
| aiDiv.innerHTML = "<strong>AI:</strong> "; | |
| aiDiv.appendChild(aiContentDiv); | |
| const res = await fetch('/chat', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({message: text}) | |
| }); | |
| const reader = res.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let rawBuffer = ""; | |
| while(true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| const chunk = decoder.decode(value); | |
| rawBuffer += chunk; | |
| // 1. Separate actual content from [SYSTEM: ...] logs | |
| // We use Regex to isolate the system logs so Markdown doesn't mess them up | |
| // and so we can style them differently. | |
| // Temporary placeholder for rendering | |
| let renderText = rawBuffer; | |
| // Regex to find system messages | |
| const sysRegex = /\[SYSTEM:.*?\]/g; | |
| // We let marked parse everything, but we pre-format the system tags slightly | |
| // to ensure they survive properly or post-process. | |
| // Simpler approach: Render Markdown, then replace the text matching system logs with styled HTML. | |
| let html = marked.parse(renderText); | |
| // Highlight System Logs after markdown conversion | |
| html = html.replace(sysRegex, (match) => { | |
| return `<span class="system-msg">${match}</span>`; | |
| }); | |
| aiContentDiv.innerHTML = html; | |
| chatLog.scrollTop = chatLog.scrollHeight; | |
| } | |
| refreshVisuals(); | |
| }); | |
| function addMsg(role, text) { | |
| const div = document.createElement('div'); | |
| div.className = "chat-bubble"; | |
| if(role === "USER") { | |
| div.innerHTML = `<strong>${role}:</strong> ${text}`; | |
| } else { | |
| div.innerHTML = `<strong>${role}:</strong> ${text}`; | |
| } | |
| chatLog.appendChild(div); | |
| chatLog.scrollTop = chatLog.scrollHeight; | |
| return div; | |
| } | |
| async function wipe() { | |
| if(confirm("Really wipe memory?")) { | |
| await fetch('/wipe', {method:'POST'}); | |
| refreshVisuals(); | |
| } | |
| } | |
| refreshVisuals(); | |
| </script> | |
| </body> | |
| </html> |