Spaces:
Running
Running
Download src/utils/deep-research-loader.js from Luca448/APP-Backend: direct link, hf CLI and curl.
- Browser
- Download file 9.7 kB
-
https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/src/utils/deep-research-loader.js
- Command line
-
hf download hf://spaces/Luca448/APP-Backend/src/utils/deep-research-loader.js
-
curl -L -o deep-research-loader.js https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/src/utils/deep-research-loader.js
9.7 kB
| /** | |
| * 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 ` | |
| <div class="dr-ambient-glow"></div> | |
| <!-- Top Status Text --> | |
| <div class="dr-status-text-container"> | |
| <div class="dr-status-text">Starte Deep Research...</div> | |
| </div> | |
| <!-- PHASE 1: Earth --> | |
| <div class="dr-phase dr-phase-1"> | |
| ${this._buildEarthSVG()} | |
| </div> | |
| <!-- PHASE 2: Article + Magnifier --> | |
| <div class="dr-phase dr-phase-2"> | |
| ${this._buildArticleSVG()} | |
| </div> | |
| <!-- PHASE 3: Skeleton Report --> | |
| <div class="dr-phase dr-phase-3"> | |
| ${this._buildReportSVG()} | |
| </div> | |
| <!-- Bottom Status Bar (Dots & Label) --> | |
| <div class="dr-status-bar"> | |
| <div class="dr-phase-dots"> | |
| <div class="dr-phase-dot"></div> | |
| <div class="dr-phase-dot"></div> | |
| <div class="dr-phase-dot"></div> | |
| </div> | |
| <div class="dr-phase-label">RECHERCHE</div> | |
| </div> | |
| `; | |
| } | |
| /** Phase 1: 3D rotating globe via Globe3D JS class */ | |
| _buildEarthSVG() { | |
| return ` | |
| <div class="dr-earth-container"> | |
| <svg class="dr-earth-svg" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg"> | |
| <defs> | |
| <clipPath id="earth-clip"> | |
| <circle cx="200" cy="200" r="175" /> | |
| </clipPath> | |
| <!-- 3D sphere shading: darken edges, highlight top-left --> | |
| <radialGradient id="earth-3d-shade" cx="35%" cy="32%" r="65%"> | |
| <stop offset="0%" stop-color="rgba(140,200,255,0.12)" /> | |
| <stop offset="60%" stop-color="rgba(0,0,0,0)" /> | |
| <stop offset="100%" stop-color="rgba(0,0,0,0.55)" /> | |
| </radialGradient> | |
| <!-- Atmosphere rim glow --> | |
| <radialGradient id="earth-atmosphere" cx="50%" cy="50%" r="50%"> | |
| <stop offset="85%" stop-color="rgba(0,0,0,0)" /> | |
| <stop offset="95%" stop-color="rgba(66,133,244,0.15)" /> | |
| <stop offset="100%" stop-color="rgba(66,133,244,0.3)" /> | |
| </radialGradient> | |
| </defs> | |
| <!-- Ocean background (stays fixed) --> | |
| <circle class="dr-earth-ocean" cx="200" cy="200" r="175" /> | |
| <!-- Globe3D class will inject the <g> with continents and arcs here --> | |
| <!-- 3D shading overlay (fixed, gives sphere depth) --> | |
| <circle cx="200" cy="200" r="175" fill="url(#earth-3d-shade)" /> | |
| <!-- Atmosphere rim --> | |
| <circle cx="200" cy="200" r="175" fill="url(#earth-atmosphere)" /> | |
| <!-- Edge ring --> | |
| <circle cx="200" cy="200" r="175" fill="none" stroke="rgba(122,186,255,0.25)" stroke-width="1.5" /> | |
| </svg> | |
| </div> | |
| `; | |
| } | |
| /** Phase 2: Scrolling article/website with scanning magnifying glass */ | |
| _buildArticleSVG() { | |
| // Build a tall content strip that scrolls vertically, duplicated for seamless loop | |
| const buildContentBlock = () => ` | |
| <div class="dr-article-line title"></div> | |
| <div class="dr-article-line subtitle"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line medium"></div> | |
| <div class="dr-article-line short"></div> | |
| <div class="dr-article-img-placeholder"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line medium"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line short"></div> | |
| <div class="dr-article-line title"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line medium"></div> | |
| <div class="dr-article-img-placeholder"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line medium"></div> | |
| <div class="dr-article-line short"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line long"></div> | |
| <div class="dr-article-line medium"></div> | |
| `; | |
| return ` | |
| <div class="dr-article-container"> | |
| <!-- Browser chrome --> | |
| <div class="dr-article-chrome"> | |
| <div class="dr-chrome-dot"></div> | |
| <div class="dr-chrome-dot"></div> | |
| <div class="dr-chrome-dot"></div> | |
| <div class="dr-chrome-urlbar"></div> | |
| </div> | |
| <!-- Scrolling content (duplicated for seamless loop) --> | |
| <div class="dr-article-scroll-content"> | |
| ${buildContentBlock()} | |
| ${buildContentBlock()} | |
| </div> | |
| <!-- Magnifying glass overlay --> | |
| <div class="dr-magnifier-overlay"> | |
| <svg viewBox="0 0 420 480" xmlns="http://www.w3.org/2000/svg"> | |
| <g class="dr-magnifier-group"> | |
| <circle class="dr-magnifier-lens" cx="0" cy="0" r="36" /> | |
| <path class="dr-magnifier-highlight" d="M-20,-26 A36,36 0 0,1 20,-26" /> | |
| <line class="dr-magnifier-handle" x1="25" y1="25" x2="50" y2="50" /> | |
| </g> | |
| </svg> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| /** Phase 3: Skeleton report with image placeholders, pie & bar charts (continuously loops) */ | |
| _buildReportSVG() { | |
| // Build a tall content strip that scrolls upward, duplicated for seamless loop | |
| const buildReportBlock = () => ` | |
| <div class="dr-report-skel-line title"></div> | |
| <div class="dr-report-skel-line subtitle"></div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-line medium"></div> | |
| <div class="dr-report-skel-line short"></div> | |
| <div class="dr-report-skel-image"></div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-line medium"></div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-line short"></div> | |
| <div class="dr-report-skel-pie"></div> | |
| <div class="dr-report-skel-line medium"></div> | |
| <div class="dr-report-skel-line short"></div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-bars"> | |
| <div class="dr-report-skel-bar"></div> | |
| <div class="dr-report-skel-bar"></div> | |
| <div class="dr-report-skel-bar"></div> | |
| <div class="dr-report-skel-bar"></div> | |
| <div class="dr-report-skel-bar"></div> | |
| </div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-line medium"></div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-image"></div> | |
| <div class="dr-report-skel-line long"></div> | |
| <div class="dr-report-skel-line short"></div> | |
| <div class="dr-report-skel-line medium"></div> | |
| `; | |
| return ` | |
| <div class="dr-report-container"> | |
| <div class="dr-report-scroll"> | |
| <div class="dr-report-block">${buildReportBlock()}</div> | |
| <div class="dr-report-block">${buildReportBlock()}</div> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| } | |
| export const deepResearchLoader = new DeepResearchLoader(); | |