diff --git a/llm_team_ui.py b/llm_team_ui.py index 8dc5036..85a1e20 100644 --- a/llm_team_ui.py +++ b/llm_team_ui.py @@ -645,15 +645,16 @@ HTML = r""" .status-bar { display: flex; align-items: center; gap: 8px; padding: 10px 14px; background: rgba(0,0,0,0.3); border: 2px solid var(--border); border-radius: 2px; font-size: 11px; color: var(--text2); font-family: 'JetBrains Mono', monospace; text-transform: uppercase; letter-spacing: 0.5px; } .spinner { width: 14px; height: 14px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.7s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } - .progress-panel { background: rgba(0,0,0,0.3); border: 2px solid var(--border); border-radius: 2px; padding: 14px; margin-bottom: 10px; } - .progress-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; } + .progress-panel { background: rgba(8,9,12,0.95); border: 2px solid var(--border); border-radius: 2px; padding: 12px 14px; position: sticky; top: 0; z-index: 50; backdrop-filter: blur(20px); margin-bottom: 10px; transition: opacity 2s, box-shadow 0.3s; box-shadow: 0 4px 20px rgba(0,0,0,0.5); } + .progress-panel.done { box-shadow: 0 2px 10px rgba(0,0,0,0.3); } + .progress-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .progress-header .prog-mode { font-family: 'JetBrains Mono', monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 1.5px; color: var(--accent); font-weight: 700; } .progress-header .prog-time { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: var(--text2); letter-spacing: 0.5px; } - .progress-track { height: 6px; background: rgba(0,0,0,0.4); border: 1px solid var(--border); border-radius: 1px; overflow: hidden; margin-bottom: 8px; } + .progress-track { height: 6px; background: rgba(0,0,0,0.4); border: 1px solid var(--border); border-radius: 1px; overflow: hidden; margin-bottom: 6px; } .progress-fill { height: 100%; background: var(--accent); transition: width 0.4s ease; box-shadow: 0 0 10px rgba(226,181,90,0.3); position: relative; } .progress-fill::after { content: ''; position: absolute; right: 0; top: 0; bottom: 0; width: 20px; background: linear-gradient(90deg, transparent, var(--accent2)); animation: progress-shimmer 1.5s ease-in-out infinite; } @keyframes progress-shimmer { 0%,100% { opacity: 0.3; } 50% { opacity: 1; } } - .progress-steps { display: flex; gap: 4px; margin-bottom: 8px; } + .progress-steps { display: flex; gap: 4px; margin-bottom: 6px; } .progress-step { flex: 1; height: 3px; background: rgba(0,0,0,0.4); border-radius: 1px; transition: background 0.3s; } .progress-step.done { background: var(--accent); } .progress-step.active { background: var(--accent); animation: step-pulse 1s ease-in-out infinite; } @@ -661,6 +662,9 @@ HTML = r""" .progress-detail { font-family: 'JetBrains Mono', monospace; font-size: 10px; color: var(--text2); display: flex; justify-content: space-between; } .progress-detail .prog-substep { max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .progress-detail .prog-stats { color: var(--text2); opacity: 0.6; } + .phase-label { font-family: 'JetBrains Mono', monospace; font-size: 9px; text-transform: uppercase; letter-spacing: 2px; color: var(--accent); padding: 10px 0 6px; opacity: 0.6; display: flex; align-items: center; gap: 8px; } + .phase-label::before { content: ''; flex: 0 0 12px; height: 1px; background: var(--accent); opacity: 0.4; } + .phase-label::after { content: ''; flex: 1; height: 1px; background: var(--accent); opacity: 0.15; } .sample-prompts { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; } .sample-chip { background: rgba(0,0,0,0.3); border: 1px solid var(--border); border-radius: 2px; padding: 6px 12px; font-size: 11px; color: var(--text2); cursor: pointer; transition: all 0.15s; line-height: 1.4; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .sample-chip:hover { border-color: var(--accent); color: var(--accent); background: var(--glow); } @@ -1374,13 +1378,14 @@ async function runTeam() { clearInterval(_runTimer); const prog = document.getElementById('run-progress'); if (prog) { + prog.classList.add('done'); const fillEl = document.getElementById('prog-fill'); if (fillEl) { fillEl.style.width = '100%'; fillEl.style.boxShadow = '0 0 16px rgba(74,222,128,0.4)'; fillEl.style.background = 'var(--green)'; } const sub = document.getElementById('prog-substep'); - if (sub) sub.textContent = 'Complete — ' + formatElapsed(Date.now() - _runStartTime); + if (sub) sub.textContent = 'Complete — ' + formatElapsed(Date.now() - _runStartTime) + ' — ' + _runResponseCount + ' responses'; const allSteps = prog.querySelectorAll('.progress-step'); allSteps.forEach(function(s) { s.className = 'progress-step done'; }); - setTimeout(function() { if (prog.parentNode) { prog.style.opacity = '0.4'; prog.style.transition = 'opacity 2s'; } }, 3000); + setTimeout(function() { if (prog.parentNode) { prog.style.opacity = '0.5'; } }, 5000); } btn.disabled = false; btn.textContent = 'Run Team'; } @@ -1390,6 +1395,7 @@ function handleEvent(evt) { if (evt.type === 'clear') { const prog = document.getElementById('run-progress'); output.textContent = ''; + output.dataset.lastPhase = ''; if (prog) output.appendChild(prog); return; } @@ -1435,6 +1441,18 @@ function handleEvent(evt) { if (evt.type === 'response') { _runResponseCount++; const bar = output.querySelector('.status-bar'); if (bar) bar.remove(); + // Phase labels — show when role changes + const role = evt.role || 'response'; + const PHASE_MAP = {scout:'scouting',researcher:'researching',respondent:'models responding','fact-checker':'fact-checking',synthesis:'synthesizing',judge:'judging',error:'error',coder:'coding',reviewer:'reviewing',tester:'testing',attacker:'red teaming',patcher:'patching',survivor:'surviving','chaos-agent':'chaos round','mesh-360':'360 synthesis'}; + const phaseName = PHASE_MAP[role] || role; + const lastPhase = output.dataset.lastPhase || ''; + if (phaseName !== lastPhase && role !== 'error') { + output.dataset.lastPhase = phaseName; + var label = document.createElement('div'); + label.className = 'phase-label'; + label.textContent = phaseName; + output.appendChild(label); + } const mi = availableModels.findIndex(m => m.name === evt.model); const color = COLORS[(mi >= 0 ? mi : 0) % COLORS.length]; const displayName = mi >= 0 ? (availableModels[mi].display_name || evt.model) : evt.model; @@ -1449,6 +1467,8 @@ function handleEvent(evt) { card.dataset.role = evt.role || ''; card.dataset.displayName = displayName; output.appendChild(card); + // Auto-scroll to latest response + card.scrollIntoView({behavior: 'smooth', block: 'nearest'}); } }