/** * Deep Research Loader * Fullscreen 3-phase loading animation for Deep Research. * Phase 1: Rotating Earth with data arcs * Phase 2: Article with scanning magnifying glass * Phase 3: Skeleton report with charts */ import { Globe3D } from './globe3d.js'; export class DeepResearchLoader { constructor() { this.overlay = null; this.currentPhase = 0; this.statusTextEl = null; this.phaseLabelEl = null; this.dots = []; } /** Build and show the fullscreen overlay */ show() { if (this.overlay) this.hide(); this.overlay = document.createElement('div'); this.overlay.className = 'dr-loader-overlay'; this.overlay.innerHTML = this._buildHTML(); document.body.appendChild(this.overlay); // Cache elements this.statusTextEl = this.overlay.querySelector('.dr-status-text'); this.phaseLabelEl = this.overlay.querySelector('.dr-phase-label'); this.dots = Array.from(this.overlay.querySelectorAll('.dr-phase-dot')); const earthSvg = this.overlay.querySelector('.dr-earth-svg'); if (earthSvg) { this.globe3D = new Globe3D(earthSvg); } // Force reflow then fade in requestAnimationFrame(() => { requestAnimationFrame(() => { this.overlay.classList.add('visible'); this.setPhase(1); }); }); } /** Switch to a phase (1, 2, or 3) with crossfade */ setPhase(phase) { if (!this.overlay || phase === this.currentPhase) return; this.currentPhase = phase; // Update phase containers const phases = this.overlay.querySelectorAll('.dr-phase'); phases.forEach(el => el.classList.remove('active')); const target = this.overlay.querySelector(`.dr-phase-${phase}`); if (target) target.classList.add('active'); // Update dots this.dots.forEach((dot, i) => { dot.classList.remove('active', 'done'); if (i + 1 < phase) dot.classList.add('done'); else if (i + 1 === phase) dot.classList.add('active'); }); // Update phase label const labels = { 1: 'RECHERCHE', 2: 'QUELLEN LESEN', 3: 'BERICHT ERSTELLEN' }; if (this.phaseLabelEl) this.phaseLabelEl.textContent = labels[phase] || ''; } /** Update the status text */ setStatus(text) { if (this.statusTextEl) this.statusTextEl.textContent = text; } /** Hide and destroy the overlay */ hide() { if (!this.overlay) return; this.overlay.classList.remove('visible'); if (this.globe3D) { this.globe3D.destroy(); this.globe3D = null; } const el = this.overlay; setTimeout(() => el.remove(), 700); this.overlay = null; this.currentPhase = 0; } /** Build the full HTML for the overlay */ _buildHTML() { return `