Download index.html from EYEDOL/TEST: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/EYEDOL/TEST/resolve/main/index.html
- Command line
-
hf download hf://spaces/EYEDOL/TEST/index.html
-
curl -L -o index.html https://huggingface.co/spaces/EYEDOL/TEST/resolve/main/index.html
10.6 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <title>Open-Source Voice Agent</title> | |
| <style> | |
| /* ββ Reset & base ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --bg0: #0f1117; | |
| --bg1: #161b27; | |
| --bg2: #1e2436; | |
| --bg3: #252d42; | |
| --border: #2e3a52; | |
| --text0: #e8eaf0; | |
| --text1: #9aa3b8; | |
| --text2: #5c677e; | |
| --accent: #4f8ef7; | |
| --green: #22c55e; | |
| --amber: #f59e0b; | |
| --blue: #3b82f6; | |
| --red: #ef4444; | |
| --radius: 12px; | |
| --font: 'Inter', system-ui, -apple-system, sans-serif; | |
| } | |
| body { | |
| background: var(--bg0); | |
| color: var(--text0); | |
| font-family: var(--font); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| /* ββ Layout ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .page { | |
| width: 100%; | |
| max-width: 680px; | |
| padding: 24px 16px 48px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| /* ββ Header ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .header { text-align: center; padding: 8px 0 4px; } | |
| .header h1 { font-size: 22px; font-weight: 600; letter-spacing: -0.3px; } | |
| .header p { font-size: 13px; color: var(--text1); margin-top: 6px; } | |
| .badges { | |
| display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; | |
| margin-top: 10px; | |
| } | |
| .badge { | |
| font-size: 11px; font-weight: 500; | |
| padding: 3px 10px; | |
| border-radius: 20px; | |
| border: 1px solid var(--border); | |
| color: var(--text1); | |
| background: var(--bg2); | |
| } | |
| .badge span { color: var(--accent); } | |
| /* ββ Card ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .card { | |
| background: var(--bg1); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| overflow: hidden; | |
| } | |
| /* ββ Visualiser ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .vis-wrap { | |
| padding: 0; | |
| background: var(--bg0); | |
| border-bottom: 1px solid var(--border); | |
| } | |
| #visualiser { | |
| display: block; | |
| width: 100%; | |
| height: 90px; | |
| } | |
| /* ββ Controls ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .controls { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 14px; | |
| padding: 24px 20px 20px; | |
| } | |
| #btn-toggle { | |
| width: 120px; height: 120px; | |
| border-radius: 50%; | |
| border: 2px solid var(--border); | |
| background: var(--bg2); | |
| color: var(--text0); | |
| font-size: 32px; | |
| cursor: pointer; | |
| transition: background .15s, transform .1s, border-color .15s; | |
| display: flex; align-items: center; justify-content: center; | |
| flex-direction: column; | |
| gap: 4px; | |
| line-height: 1; | |
| } | |
| #btn-toggle:hover { background: var(--bg3); border-color: var(--accent); } | |
| #btn-toggle:active { transform: scale(0.95); } | |
| #btn-toggle.active { | |
| background: #1a2440; | |
| border-color: var(--blue); | |
| box-shadow: 0 0 0 4px rgba(79,142,247,0.15); | |
| } | |
| #btn-label { font-size: 11px; color: var(--text1); font-weight: 500; } | |
| /* ββ Status bar ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .status-bar { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 10px 20px; | |
| background: var(--bg2); | |
| border-top: 1px solid var(--border); | |
| justify-content: space-between; | |
| } | |
| .status-left { display: flex; align-items: center; gap: 8px; } | |
| #status-dot { | |
| width: 8px; height: 8px; border-radius: 50%; | |
| background: #6b7280; | |
| transition: background .3s; | |
| flex-shrink: 0; | |
| } | |
| #status-dot.pulse { animation: pulse 1.2s infinite; } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.4; } | |
| } | |
| #status-text { font-size: 13px; color: var(--text1); } | |
| #latency { font-size: 12px; color: var(--text2); } | |
| /* ββ Transcript ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .transcript-header { | |
| padding: 12px 16px 8px; | |
| font-size: 11px; | |
| font-weight: 600; | |
| letter-spacing: .06em; | |
| text-transform: uppercase; | |
| color: var(--text2); | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| #btn-clear { | |
| font-size: 11px; color: var(--text2); | |
| background: none; border: none; cursor: pointer; | |
| padding: 2px 6px; border-radius: 4px; | |
| } | |
| #btn-clear:hover { color: var(--text1); background: var(--bg3); } | |
| #transcript { | |
| min-height: 120px; | |
| max-height: 320px; | |
| overflow-y: auto; | |
| padding: 12px 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| scroll-behavior: smooth; | |
| } | |
| #transcript:empty::before { | |
| content: 'Conversation will appear hereβ¦'; | |
| color: var(--text2); | |
| font-size: 13px; | |
| } | |
| .msg { display: flex; flex-direction: column; gap: 3px; } | |
| .msg-role { | |
| font-size: 10px; | |
| font-weight: 600; | |
| letter-spacing: .05em; | |
| text-transform: uppercase; | |
| } | |
| .msg-text { font-size: 14px; line-height: 1.55; color: var(--text0); } | |
| .msg-user .msg-role { color: var(--green); } | |
| .msg-agent .msg-role { color: var(--blue); } | |
| .msg-user .msg-text { color: var(--text0); } | |
| .msg-agent .msg-text { color: var(--text0); } | |
| /* ββ Model info footer βββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| .model-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1px; | |
| background: var(--border); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| overflow: hidden; | |
| } | |
| .model-cell { | |
| background: var(--bg1); | |
| padding: 12px 14px; | |
| text-align: center; | |
| } | |
| .model-cell .mc-label { | |
| font-size: 10px; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| letter-spacing: .06em; | |
| color: var(--text2); | |
| margin-bottom: 4px; | |
| } | |
| .model-cell .mc-name { font-size: 12px; font-weight: 500; color: var(--text0); } | |
| .model-cell .mc-size { font-size: 11px; color: var(--text2); margin-top: 2px; } | |
| /* ββ Scrollbar βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ | |
| ::-webkit-scrollbar { width: 4px; } | |
| ::-webkit-scrollbar-track { background: transparent; } | |
| ::-webkit-scrollbar-thumb { background: var(--bg3); border-radius: 2px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="page"> | |
| <!-- Header --> | |
| <div class="header"> | |
| <h1>π€ Open-Source Voice Agent</h1> | |
| <p>100% open-source models Β· streaming overlap pipeline Β· English</p> | |
| <div class="badges"> | |
| <div class="badge"><span>STT</span> Whisper base.en</div> | |
| <div class="badge"><span>LLM</span> SmolLM2-1.7B</div> | |
| <div class="badge"><span>TTS</span> MMS-TTS-eng</div> | |
| <div class="badge"><span>VAD</span> Silero</div> | |
| </div> | |
| </div> | |
| <!-- Voice card --> | |
| <div class="card"> | |
| <!-- Waveform visualiser --> | |
| <div class="vis-wrap"> | |
| <canvas id="visualiser"></canvas> | |
| </div> | |
| <!-- Mic button --> | |
| <div class="controls"> | |
| <button id="btn-toggle" aria-label="Start recording"> | |
| <span id="btn-icon">π€</span> | |
| <span id="btn-label">START</span> | |
| </button> | |
| </div> | |
| <!-- Status bar --> | |
| <div class="status-bar"> | |
| <div class="status-left"> | |
| <div id="status-dot"></div> | |
| <span id="status-text">Ready β click Start to begin</span> | |
| </div> | |
| <span id="latency"></span> | |
| </div> | |
| </div> | |
| <!-- Transcript --> | |
| <div class="card"> | |
| <div class="transcript-header"> | |
| Conversation | |
| <button id="btn-clear">Clear</button> | |
| </div> | |
| <div id="transcript"></div> | |
| </div> | |
| <!-- Model info --> | |
| <div class="model-grid"> | |
| <div class="model-cell"> | |
| <div class="mc-label">Speech-to-Text</div> | |
| <div class="mc-name">Whisper base.en</div> | |
| <div class="mc-size">74 M params Β· GPU/CPU</div> | |
| </div> | |
| <div class="model-cell"> | |
| <div class="mc-label">Language Model</div> | |
| <div class="mc-name">SmolLM2-1.7B-Instruct</div> | |
| <div class="mc-size">1.7 B params Β· GPU/CPU</div> | |
| </div> | |
| <div class="model-cell"> | |
| <div class="mc-label">Text-to-Speech</div> | |
| <div class="mc-name">MMS-TTS-eng</div> | |
| <div class="mc-size">VITS Β· 16 kHz Β· CPU</div> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="/app.js" defer></script> | |
| <script> | |
| // Wire up the Clear button after DOM is ready | |
| document.getElementById('btn-clear').addEventListener('click', () => { | |
| document.getElementById('transcript').innerHTML = ''; | |
| }); | |
| // Reflect active state on button | |
| const _setState_orig = window.setState; | |
| // We patch setState in app.js via a MutationObserver hack instead: | |
| const _btnToggle = document.getElementById('btn-toggle'); | |
| const _btnIcon = document.getElementById('btn-icon'); | |
| const _btnLabel = document.getElementById('btn-label'); | |
| const _dot = document.getElementById('status-dot'); | |
| // Observe #status-dot background changes to mirror active state on button | |
| const obs = new MutationObserver(() => { | |
| const bg = _dot.style.background; | |
| if (bg === '#22c55e' || bg === '#3b82f6') { | |
| _btnToggle.classList.add('active'); | |
| _btnIcon.textContent = 'βΉ'; | |
| _btnLabel.textContent = 'STOP'; | |
| } else if (bg === '#f59e0b') { | |
| _btnToggle.classList.add('active'); | |
| _btnIcon.textContent = 'π'; | |
| _btnLabel.textContent = 'THINKING'; | |
| } else { | |
| _btnToggle.classList.remove('active'); | |
| _btnIcon.textContent = 'π€'; | |
| _btnLabel.textContent = 'START'; | |
| } | |
| // Pulse dot when not idle | |
| _dot.classList.toggle('pulse', bg !== '#6b7280'); | |
| }); | |
| obs.observe(_dot, { attributes: true, attributeFilter: ['style'] }); | |
| </script> | |
| </body> | |
| </html> | |