Sticky progress bar, phase labels, auto-scroll
- Progress panel is now position:sticky at top of output — always visible - Phase labels (─── scouting ───, ─── researching ───, etc.) appear between response cards when the pipeline role changes - Auto-scroll to latest response card as they arrive - Completion state shows response count and fades after 5s - Clear previous errors: all 'input stream' errors were caused by service restarts during in-flight runs, not code bugs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c124b01681
commit
9eaac813df
@ -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'});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user