<!DOCTYPE html>
<html data-theme="dark">
<head>
<meta charset="UTF-8" />
<title>FGD Stack</title>
<style>
:root { --bg: #0d1117; --card: #161b22; --text: #f0f6fc; --accent: #58a6ff; --border: #30363d; --log-bg: #010409; }
[data-theme="light"] { --bg: #f7f9fc; --card: #ffffff; --text: #24292f; --accent: #0969da; --border: #d0d7de; --log-bg: #f6f8fa; }
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: var(--bg); color: var(--text); font-family: -apple-system, sans-serif; padding: 2rem; }
.container { max-width: 800px; margin: 0 auto; }
.card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; margin-bottom: 1.5rem; }
button, input, select { padding: 0.65rem; margin: 0.4rem 0; border-radius: 8px; width: 100%; font-size: 1rem; }
button { background: var(--accent); color: white; border: none; cursor: pointer; }
.suggestion { background: rgba(88,166,255,0.1); padding: 0.6rem; border-radius: 6px; margin: 0.3rem 0; cursor: pointer; }
#logViewer { background: var(--log-bg); height: 300px; overflow-y: auto; padding: 0.75rem; font-family: monospace; font-size: 0.85rem; white-space: pre-wrap; }
.theme-toggle { position: fixed; top: 1rem; right: 1rem; width: 40px; height: 40px; background: var(--card); border: 1px solid var(--border); border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; }
</style>
</head>
<body>
<div class="theme-toggle" id="themeToggle">Dark Mode</div>
<div class="container">
<h1>FGD Stack</h1>
<div class="card">
<h3>1. Select Directory</h3>
<input type="file" id="dirPicker" webkitdirectory directory style="display:none">
<button onclick="document.getElementById('dirPicker').click()">Browse...</button>
<div id="selectedPath"></div>
</div>
<div class="card">
<h3>2. Git Repos</h3>
<div id="suggestions"></div>
</div>
<div class="card">
<h3>3. LLM</h3>
<select id="provider"><option value="grok">Grok</option><option value="openai">ChatGPT</option></select>
</div>
<button onclick="startServer()" id="startBtn">Launch Server</button>
<div id="status"></div>
<div class="card" id="logCard" style="display:none;">
<h3>Live Logs
<span style="float:right;font-size:0.8rem;">
<select id="levelFilter" style="padding:0.3rem;"><option value="">All</option><option>INFO</option><option>ERROR</option></select>
<input id="searchFilter" placeholder="Search..." style="width:120px;padding:0.3rem;margin-left:0.5rem;">
<button onclick="clearFilters()" style="padding:0.3rem 0.5rem;margin-left:0.3rem;">Clear</button>
</span>
</h3>
<div id="logViewer"></div>
</div>
</div>
<script>
let allLogLines = [], logInterval;
const themeToggle = document.getElementById('themeToggle');
themeToggle.onclick = () => {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
themeToggle.textContent = isDark ? 'Light Mode' : 'Dark Mode';
};
document.getElementById('dirPicker').onchange = () => {
const entry = document.getElementById('dirPicker').webkitEntries?.[0];
if (entry) window.selectedDir = entry.fullPath || entry.name;
document.getElementById('selectedPath').innerHTML = `<strong>Selected:</strong> ${window.selectedDir || 'None'}`;
};
async function loadSuggestions() {
const res = await fetch('/api/suggest');
const paths = await res.json();
document.getElementById('suggestions').innerHTML = paths.map(p =>
`<div class="suggestion" onclick="window.selectedDir='${p}'; document.getElementById('selectedPath').innerHTML='<strong>Selected:</strong> ${p}'">${p}</div>`
).join('');
}
async function startServer() {
const dir = window.selectedDir || document.getElementById('selectedPath').innerText.replace(/.*:\s*/, '');
const provider = document.getElementById('provider').value;
if (!dir || dir === 'None') return alert("Select a directory");
const res = await fetch('/api/start', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({watch_dir: dir, default_provider: provider}) });
const result = await res.json();
document.getElementById('status').innerHTML = result.success ?
`<div style="background:#e8f5e9;color:#2e7d32;padding:1rem;border-radius:8px;">
Server running!<br>Memory: ${result.memory_file}<br>
<a href="#" onclick="showLogs('${result.log_file}'); return false;">View Live Logs</a>
</div>` :
`<div style="background:#ffebee;color:#c62828;padding:1rem;border-radius:8px;">Error: ${result.error}</div>`;
}
function showLogs(file) {
const card = document.getElementById('logCard');
const viewer = document.getElementById('logViewer');
card.style.display = 'block';
viewer.innerHTML = 'Loading...\n';
allLogLines = [];
if (logInterval) clearInterval(logInterval);
logInterval = setInterval(async () => {
const res = await fetch(`/api/logs?file=${encodeURIComponent(file)}&t=${Date.now()}`);
const text = await res.text();
const lines = text.trim().split('\n');
const newLines = lines.slice(allLogLines.length);
allLogLines.push(...newLines);
applyFilters();
}, 1500);
}
function applyFilters() {
const level = document.getElementById('levelFilter').value;
const search = document.getElementById('searchFilter').value.toLowerCase();
const viewer = document.getElementById('logViewer');
const filtered = allLogLines.filter(l => {
if (level && !l.includes(level)) return false;
if (search && !l.toLowerCase().includes(search)) return false;
return true;
});
viewer.innerHTML = filtered.map(l => search && l.toLowerCase().includes(search) ?
`<span style="background:#fffbe6;padding:0 2px;">${l.replace(/</g, '<')}</span>` :
l.replace(/</g, '<')).join('\n');
viewer.scrollTop = viewer.scrollHeight;
}
function clearFilters() {
document.getElementById('levelFilter').value = '';
document.getElementById('searchFilter').value = '';
applyFilters();
}
document.getElementById('levelFilter').onchange = applyFilters;
document.getElementById('searchFilter').oninput = () => setTimeout(applyFilters, 300);
loadSuggestions();
</script>
</body>
</html>