/* ─── Config ──────────────────────────────────────────────────────────────── */ // Empty string = same origin (works on HF Spaces and any deployment). // Falls back to localhost only when running locally on a different port. const API_BASE = window.location.hostname === 'localhost' ? 'http://localhost:8000' : ''; /* ─── State ───────────────────────────────────────────────────────────────── */ const state = { currentTaskId: null, isRunning: false, taskHistory: JSON.parse(localStorage.getItem('taskHistory') || '[]'), }; /* ─── DOM refs ────────────────────────────────────────────────────────────── */ const $ = id => document.getElementById(id); const taskInput = $('taskInput'); const submitBtn = $('submitBtn'); const submitText = $('submitText'); const charCount = $('charCount'); const eventLog = $('eventLog'); const planCard = $('planCard'); const planSteps = $('planSteps'); const planProgress = $('planProgress'); const outputCard = $('outputCard'); const outputContent = $('outputContent'); const qualityBadge = $('qualityBadge'); const taskHistory = $('taskHistory'); const globalStatus = $('globalStatus'); const healthDot = $('healthDot'); const healthLabel = $('healthLabel'); const errorBanner = $('errorBanner'); const errorBannerMsg = $('errorBannerMsg'); $('errorBannerClose').addEventListener('click', () => { errorBanner.style.display = 'none'; }); /* ─── Health check ────────────────────────────────────────────────────────── */ async function checkHealth() { try { const res = await fetch(`${API_BASE}/api/health`, { signal: AbortSignal.timeout(3000) }); if (res.ok) { healthDot.className = 'health-indicator ok'; healthLabel.textContent = 'Backend online'; } else { throw new Error('non-ok'); } } catch { healthDot.className = 'health-indicator error'; healthLabel.textContent = 'Backend offline'; } } checkHealth(); setInterval(checkHealth, 15000); /* ─── Theme toggle ────────────────────────────────────────────────────────── */ const themeToggle = $('themeToggle'); let isDark = true; themeToggle.addEventListener('click', () => { isDark = !isDark; document.documentElement.setAttribute('data-theme', isDark ? '' : 'light'); themeToggle.textContent = isDark ? '☀' : '🌙'; }); /* ─── Char counter ────────────────────────────────────────────────────────── */ taskInput.addEventListener('input', () => { const len = taskInput.value.length; charCount.textContent = `${len} / 2000`; charCount.style.color = len > 1800 ? 'var(--amber)' : ''; }); /* ─── Sample chips ────────────────────────────────────────────────────────── */ document.querySelectorAll('.chip').forEach(chip => { chip.addEventListener('click', () => { taskInput.value = chip.textContent.trim(); taskInput.dispatchEvent(new Event('input')); taskInput.focus(); }); }); /* ─── Clear events ────────────────────────────────────────────────────────── */ $('clearEvents').addEventListener('click', () => { eventLog.innerHTML = '
Events will appear here
'; }); /* ─── Copy output ─────────────────────────────────────────────────────────── */ $('copyBtn').addEventListener('click', () => { navigator.clipboard.writeText(outputContent.textContent).then(() => { const btn = $('copyBtn'); btn.textContent = 'Copied!'; setTimeout(() => { btn.textContent = 'Copy'; }, 1500); }); }); /* ─── Agent state management ─────────────────────────────────────────────── */ const AGENT_ORDER = ['memory_retrieve', 'planner', 'executor', 'critic', 'memory_store']; const AGENT_ROLE_MAP = { memory: 'memory_retrieve', planner: 'planner', executor: 'executor', critic: 'critic', memory_store: 'memory_store', }; function setAgentState(agentId, stateStr) { const node = $(`node-${agentId}`); if (!node) return; node.className = `agent-node ${stateStr}`; const badge = node.querySelector('.node-state'); badge.className = `node-state ${stateStr}`; badge.textContent = stateStr; } function resetAgentNodes() { AGENT_ORDER.forEach(id => setAgentState(id, 'idle')); } /* ─── Event log ───────────────────────────────────────────────────────────── */ function addEvent(agent, eventType, message) { const empty = eventLog.querySelector('.empty-state'); if (empty) empty.remove(); const time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); const div = document.createElement('div'); const roleClass = (agent || '').toLowerCase().replace(' ', '_'); div.className = `event-item ${roleClass}`; div.innerHTML = `No tasks yet
'; return; } taskHistory.innerHTML = state.taskHistory.slice(0, 10).map(item => `