Files
JasonHoku-ComfyUI-Ultimate-…/resources/logic_events.js
T
Jason HokuandClaude Opus 4.6 85ed723b21 feat: show 'Upscaling' phase label in dashboard ETA bar
Progress events with phase="upscaling" display "Upscaling 5/30"
instead of "Job 5/30". Handles missing finish_time gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 02:30:47 -07:00

100 lines
3.9 KiB
JavaScript

/**
* EVENT LISTENERS - INCREMENTAL UPDATES
* Only processes NEW data, doesn't refilter everything
*/
// Intercept Ctrl+S inside the dashboard iframe to prevent browser "Save As" dialog
// and forward the save intent to ComfyUI's parent window
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
e.stopPropagation();
window.parent.postMessage({ type: 'forward_ctrl_s' }, '*');
}
});
// Track last update to prevent duplicates
let lastUpdateId = 0;
// WebSocket Listener - OPTIMIZED
window.addEventListener('message', (event) => {
// Handle real-time ETA/progress updates
if (event.data && event.data.type === 'progress_update') {
const p = event.data.data;
if (!p) return;
const etaBar = document.getElementById('eta-bar');
if (etaBar) {
etaBar.style.display = 'flex';
const etaText = document.getElementById('eta-text');
const etaProgress = document.getElementById('eta-progress');
if (p.complete) {
// Generation complete
if (etaText) etaText.textContent = `Complete! ${p.total_generated} images in ${p.total_elapsed} (${p.avg_duration}s/job)`;
if (etaProgress) etaProgress.style.width = '100%';
// Auto-hide after 30 seconds
setTimeout(() => { if (etaBar) etaBar.style.display = 'none'; }, 30000);
} else {
if (etaText) {
const phaseLabel = p.phase === 'upscaling' ? 'Upscaling' : 'Job';
const finishPart = p.finish_time ? ` | ~${p.finish_time}` : '';
etaText.textContent = `${phaseLabel} ${p.current_job}/${p.total_jobs} (${p.progress_pct}%) | ETA: ${p.eta_str}${finishPart} | ${p.avg_duration}s/job`;
}
if (etaProgress) {
etaProgress.style.width = `${p.progress_pct}%`;
}
}
}
return;
}
if (event.data && event.data.type === 'update_data') {
const payload = event.data.data;
if (!payload) return;
// 1. Handle NEW ITEMS (incremental)
if (payload.new_items && payload.new_items.length > 0) {
console.log(`[Events] 📥 Received ${payload.new_items.length} new items`);
if (!fullManifest.items) fullManifest.items = [];
// Add to fullManifest data source only (processNewData will add to activeData)
fullManifest.items.unshift(...payload.new_items);
// CRITICAL: Process only new items, don't refilter everything
// processNewData handles: adding to activeData, refreshIndices, filter updates, and rendering
if (typeof processNewData === 'function') {
processNewData(payload.new_items);
} else {
// Fallback: sync activeData and do full reprocess
activeData = fullManifest.items;
if (typeof computeLabelGlobalValues === 'function' && labelMode && labelMode.enabled) {
computeLabelGlobalValues();
}
refreshIndices();
updateFiltersForNewData(payload.new_items);
updateDataPipeline();
}
lastUpdateId++;
return;
}
// 2. Handle FULL MANIFEST reload (rare)
if (payload.manifest) {
console.log('[Events] 📚 Full manifest reload');
fullManifest = payload.manifest;
activeData = fullManifest.items || [];
meta = payload.meta || {};
// Recompute label global values with new data
if (typeof computeLabelGlobalValues === 'function' && labelMode && labelMode.enabled) {
computeLabelGlobalValues();
}
refreshIndices();
// Full reprocess needed
updateDataPipeline();
}
}
});