Wilker
feat: adiciona autenticaΓ§Γ£o Google OAuth com whitelist de e-mails e API key pessoal para a CLI
bb5add3 | <html lang="pt-BR"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> | |
| <meta name="mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | |
| <title>THC LLM</title> | |
| <style> | |
| * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; } | |
| :root { | |
| --bg: #0a0a0a; | |
| --surface: #111111; | |
| --surface2: #1a1a1a; | |
| --border: #222222; | |
| --accent: #00e5a0; | |
| --accent2: #00b37e; | |
| --text: #e8e8e8; | |
| --muted: #555555; | |
| --danger: #ff4d4d; | |
| --sidebar-w: 260px; | |
| } | |
| html, body { height: 100%; overflow: hidden; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: var(--bg); | |
| color: var(--text); | |
| display: flex; | |
| height: 100vh; | |
| height: 100dvh; | |
| } | |
| button, .conv-item, .suggestion { -webkit-user-select: none; user-select: none; } | |
| /* ββββββββββ SIDEBAR ββββββββββ */ | |
| #sidebar { | |
| width: var(--sidebar-w); min-width: var(--sidebar-w); | |
| background: var(--surface); | |
| border-right: 1px solid var(--border); | |
| display: flex; flex-direction: column; | |
| transition: margin-left 0.3s ease; | |
| z-index: 100; | |
| } | |
| #sidebar.collapsed { margin-left: calc(-1 * var(--sidebar-w)); } | |
| .sidebar-header { padding: 20px 16px 12px; border-bottom: 1px solid var(--border); } | |
| .sidebar-header h2 { font-size: 15px; font-weight: 700; color: var(--accent); margin-bottom: 12px; } | |
| #new-chat-btn { | |
| width: 100%; background: var(--accent); color: #000; | |
| border: none; border-radius: 8px; padding: 10px 12px; | |
| font-size: 13px; font-weight: 600; cursor: pointer; | |
| display: flex; align-items: center; gap: 6px; | |
| } | |
| #new-chat-btn:active { background: var(--accent2); } | |
| #conversations { flex: 1; overflow-y: auto; padding: 8px; -webkit-overflow-scrolling: touch; } | |
| .conv-item { | |
| padding: 12px; border-radius: 8px; cursor: pointer; | |
| font-size: 13px; display: flex; align-items: center; | |
| justify-content: space-between; gap: 8px; | |
| transition: background 0.15s; margin-bottom: 2px; | |
| } | |
| .conv-item:active, .conv-item.active { background: var(--surface2); } | |
| .conv-item.active { border-left: 2px solid var(--accent); } | |
| .conv-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; } | |
| .conv-delete { | |
| background: none; border: none; color: var(--danger); | |
| cursor: pointer; font-size: 16px; padding: 4px 8px; border-radius: 4px; | |
| opacity: 0.6; | |
| } | |
| /* ββββββββββ SETTINGS ββββββββββ */ | |
| .settings-panel { | |
| padding: 12px 16px; border-top: 1px solid var(--border); | |
| display: flex; flex-direction: column; gap: 14px; | |
| overflow-y: auto; -webkit-overflow-scrolling: touch; | |
| } | |
| .settings-panel h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); } | |
| .slider-group label { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 6px; } | |
| .slider-group label span { color: var(--accent); font-weight: 600; } | |
| input[type=range] { width: 100%; accent-color: var(--accent); height: 20px; } | |
| .settings-panel textarea { | |
| width: 100%; background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: 8px; color: var(--text); font-size: 13px; | |
| padding: 8px 10px; resize: none; outline: none; height: 72px; font-family: inherit; | |
| } | |
| .settings-panel textarea:focus { border-color: var(--accent); } | |
| .toggle-row { display: flex; align-items: center; justify-content: space-between; font-size: 12px; } | |
| .toggle { | |
| width: 40px; height: 24px; background: var(--border); | |
| border-radius: 20px; position: relative; cursor: pointer; | |
| transition: background 0.2s; border: none; flex-shrink: 0; | |
| } | |
| .toggle.on { background: var(--accent); } | |
| .toggle::after { | |
| content: ''; position: absolute; top: 2px; left: 2px; | |
| width: 20px; height: 20px; border-radius: 50%; background: #fff; | |
| transition: left 0.2s; | |
| } | |
| .toggle.on::after { left: 18px; } | |
| select { | |
| width: 100%; background: var(--surface2); border: 1px solid var(--border); | |
| color: var(--text); border-radius: 8px; padding: 8px; font-size: 13px; outline: none; | |
| } | |
| .ios-notice { | |
| font-size: 11px; color: #d9a441; background: #2a2010; | |
| border: 1px solid #4a3a1a; border-radius: 8px; padding: 8px 10px; | |
| display: none; line-height: 1.5; | |
| } | |
| .ios-notice.show { display: block; } | |
| /* ββββββββββ SIDEBAR OVERLAY (mobile) ββββββββββ */ | |
| #overlay { | |
| display: none; position: fixed; inset: 0; | |
| background: rgba(0,0,0,0.5); z-index: 90; | |
| } | |
| #overlay.show { display: block; } | |
| /* ββββββββββ MAIN ββββββββββ */ | |
| #main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; } | |
| #topbar { | |
| background: var(--surface); border-bottom: 1px solid var(--border); | |
| padding: 12px 16px; display: flex; align-items: center; gap: 10px; | |
| flex-shrink: 0; | |
| } | |
| #toggle-sidebar { | |
| background: none; border: none; color: var(--muted); cursor: pointer; | |
| font-size: 20px; border-radius: 6px; padding: 6px; flex-shrink: 0; | |
| } | |
| .topbar-info { flex: 1; min-width: 0; } | |
| .topbar-info h1 { font-size: 14px; font-weight: 700; color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .topbar-info p { font-size: 10px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .status-dot { | |
| width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; | |
| background: var(--accent); box-shadow: 0 0 6px var(--accent); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.4} } | |
| #stats-bar { | |
| background: var(--surface2); border-bottom: 1px solid var(--border); | |
| padding: 6px 16px; display: flex; gap: 16px; font-size: 10px; color: var(--muted); | |
| flex-wrap: wrap; flex-shrink: 0; overflow-x: auto; white-space: nowrap; | |
| -webkit-overflow-scrolling: touch; | |
| } | |
| #stats-bar span { color: var(--accent); font-weight: 600; } | |
| /* ββββββββββ QUICK BAR (Model + Mode) ββββββββββ */ | |
| #quick-bar { | |
| display: flex; gap: 8px; align-items: center; | |
| padding: 8px 12px; background: var(--surface); | |
| border-bottom: 1px solid var(--border); | |
| flex-wrap: wrap; flex-shrink: 0; | |
| } | |
| #model-select { | |
| background: var(--surface2); border: 1px solid var(--border); | |
| color: var(--text); border-radius: 8px; padding: 7px 10px; | |
| font-size: 12px; outline: none; cursor: pointer; flex: 1; min-width: 140px; | |
| } | |
| #model-select:focus { border-color: var(--accent); } | |
| .mode-pills { display: flex; gap: 4px; flex-shrink: 0; flex-wrap: wrap; } | |
| .mode-pill { | |
| background: var(--surface2); border: 1px solid var(--border); color: var(--muted); | |
| border-radius: 8px; padding: 7px 10px; font-size: 11px; cursor: pointer; | |
| transition: all 0.15s; white-space: nowrap; | |
| } | |
| .mode-pill.active { background: var(--accent); color: #000; border-color: var(--accent); font-weight: 600; } | |
| #web-toggle.active { background: #2b7fff; color: #fff; border-color: #2b7fff; } | |
| .model-loading-hint { | |
| font-size: 10px; color: var(--muted); padding: 0 12px 6px; | |
| display: none; font-style: italic; background: var(--surface); flex-shrink: 0; | |
| } | |
| .model-loading-hint.show { display: block; } | |
| /* ββββββββββ CHAT ββββββββββ */ | |
| #chat { | |
| flex: 1; overflow-y: auto; padding: 16px; | |
| display: flex; flex-direction: column; gap: 16px; | |
| -webkit-overflow-scrolling: touch; | |
| } | |
| #chat::-webkit-scrollbar { width: 4px; } | |
| #chat::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } | |
| .msg-wrapper { display: flex; flex-direction: column; gap: 4px; max-width: 100%; } | |
| .msg-wrapper.user { align-items: flex-end; } | |
| .msg-wrapper.assistant { align-items: flex-start; } | |
| .msg-meta { font-size: 10px; color: var(--muted); padding: 0 4px; display: flex; gap: 8px; align-items: center; } | |
| .msg { | |
| max-width: 85%; padding: 12px 14px; border-radius: 16px; | |
| font-size: 14px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; | |
| } | |
| .msg.user { background: var(--accent); color: #000; border-bottom-right-radius: 4px; font-weight: 500; } | |
| .msg.assistant { background: var(--surface2); color: var(--text); border: 1px solid var(--border); border-bottom-left-radius: 4px; } | |
| .msg.thinking { background: var(--surface2); color: var(--muted); border: 1px solid var(--border); border-bottom-left-radius: 4px; font-style: italic; } | |
| .dots::after { content:''; animation: dots 1.2s steps(3,end) infinite; } | |
| @keyframes dots { 0%{content:''} 33%{content:'.'} 66%{content:'..'} 100%{content:'...'} } | |
| .action-btn { | |
| background: none; border: none; color: var(--muted); | |
| cursor: pointer; font-size: 12px; padding: 4px 8px; | |
| border-radius: 4px; | |
| } | |
| .action-btn:active { color: var(--accent); } | |
| .welcome { margin: auto; text-align: center; color: var(--muted); padding: 20px; } | |
| .welcome h2 { font-size: 28px; color: var(--accent); margin-bottom: 8px; } | |
| .welcome p { font-size: 13px; margin-bottom: 20px; } | |
| .suggestions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; max-width: 480px; } | |
| .suggestion { | |
| background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: 20px; padding: 10px 14px; font-size: 12px; | |
| color: var(--text); cursor: pointer; | |
| } | |
| .suggestion:active { border-color: var(--accent); color: var(--accent); } | |
| /* ββββββββββ INPUT ββββββββββ */ | |
| #input-area { | |
| padding: 12px; background: var(--surface); | |
| border-top: 1px solid var(--border); | |
| display: flex; gap: 8px; align-items: flex-end; | |
| flex-shrink: 0; | |
| padding-bottom: max(12px, env(safe-area-inset-bottom)); | |
| } | |
| .input-wrap { flex: 1; display: flex; flex-direction: column; min-width: 0; } | |
| #msg { | |
| flex: 1; background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: 12px; color: var(--text); padding: 12px 14px; | |
| font-size: 16px; | |
| resize: none; outline: none; max-height: 120px; | |
| line-height: 1.5; font-family: inherit; | |
| } | |
| #msg:focus { border-color: var(--accent); } | |
| .input-hint { font-size: 10px; color: var(--muted); padding: 4px 4px 0; display: none; } | |
| #mic-transcript { font-size: 11px; color: var(--muted); padding: 4px 4px 0; min-height: 14px; font-style: italic; } | |
| .action-buttons { display: flex; gap: 8px; align-items: flex-end; flex-shrink: 0; } | |
| #mic-btn, #send-btn { | |
| width: 44px; height: 44px; border-radius: 12px; | |
| font-size: 19px; cursor: pointer; display: flex; | |
| align-items: center; justify-content: center; | |
| flex-shrink: 0; border: none; | |
| } | |
| #mic-btn { background: var(--surface2); border: 1px solid var(--border); color: var(--text); } | |
| #mic-btn.listening { background: var(--danger); border-color: var(--danger); color: #fff; animation: mic-pulse 1s infinite; } | |
| #mic-btn.unsupported { opacity: 0.35; } | |
| @keyframes mic-pulse { 0%,100%{box-shadow:0 0 0 0 rgba(255,77,77,.4)} 50%{box-shadow:0 0 0 8px rgba(255,77,77,0)} } | |
| #send-btn { background: var(--accent); color: #000; } | |
| #send-btn:disabled { opacity: 0.35; } | |
| /* ββββββββββ SPEAKING BAR ββββββββββ */ | |
| #speaking-bar { | |
| display: none; align-items: center; gap: 8px; | |
| padding: 8px 16px; background: #0d1f17; | |
| border-top: 1px solid #0a3326; font-size: 12px; color: var(--accent); | |
| flex-shrink: 0; | |
| } | |
| #speaking-bar.active { display: flex; } | |
| .wave { display: flex; gap: 3px; align-items: center; } | |
| .wave span { width: 3px; background: var(--accent); border-radius: 2px; animation: wave 0.8s ease-in-out infinite; } | |
| .wave span:nth-child(1){height:8px;animation-delay:0s} | |
| .wave span:nth-child(2){height:16px;animation-delay:.1s} | |
| .wave span:nth-child(3){height:12px;animation-delay:.2s} | |
| .wave span:nth-child(4){height:20px;animation-delay:.3s} | |
| .wave span:nth-child(5){height:10px;animation-delay:.4s} | |
| @keyframes wave { 0%,100%{transform:scaleY(1)} 50%{transform:scaleY(.3)} } | |
| #stop-speak-btn { | |
| margin-left: auto; background: none; border: 1px solid var(--accent); | |
| color: var(--accent); border-radius: 6px; padding: 5px 12px; | |
| font-size: 11px; cursor: pointer; | |
| } | |
| /* ββββββββββ TABS (Chat / Imagem) ββββββββββ */ | |
| .mode-tabs { display: flex; gap: 4px; margin-left: 8px; flex-shrink: 0; } | |
| .mode-tab { | |
| background: none; border: 1px solid var(--border); color: var(--muted); | |
| border-radius: 8px; padding: 6px 12px; font-size: 12px; cursor: pointer; | |
| transition: all 0.2s; white-space: nowrap; | |
| } | |
| .mode-tab.active { background: var(--accent); color: #000; border-color: var(--accent); font-weight: 600; } | |
| #image-view, #audio-view { display: none; flex: 1; flex-direction: column; overflow: hidden; } | |
| #image-view.active, #audio-view.active { display: flex; } | |
| #image-prompt-area, #audio-prompt-area { | |
| padding: 16px; background: var(--surface); border-bottom: 1px solid var(--border); | |
| display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; | |
| } | |
| #image-prompt, #audio-prompt { | |
| width: 100%; background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: 12px; color: var(--text); padding: 12px 14px; | |
| font-size: 16px; resize: none; outline: none; font-family: inherit; height: 60px; | |
| } | |
| #image-prompt:focus, #audio-prompt:focus { border-color: var(--accent); } | |
| .image-controls { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; } | |
| .image-controls .slider-group { flex: 1; min-width: 140px; } | |
| .image-controls label { font-size: 11px; } | |
| #generate-img-btn, #generate-audio-btn { | |
| background: var(--accent); color: #000; border: none; border-radius: 10px; | |
| padding: 10px 20px; font-size: 13px; font-weight: 600; cursor: pointer; flex-shrink: 0; | |
| } | |
| #generate-img-btn:disabled, #generate-audio-btn:disabled { opacity: 0.4; cursor: not-allowed; } | |
| #image-gallery { | |
| flex: 1; overflow-y: auto; padding: 16px; | |
| display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); | |
| gap: 16px; align-content: start; -webkit-overflow-scrolling: touch; | |
| } | |
| #audio-gallery { | |
| flex: 1; overflow-y: auto; padding: 16px; | |
| display: flex; flex-direction: column; gap: 12px; | |
| -webkit-overflow-scrolling: touch; | |
| } | |
| .audio-card { | |
| background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: 12px; padding: 12px; display: flex; flex-direction: column; gap: 8px; | |
| } | |
| .audio-card audio { width: 100%; } | |
| .audio-card-prompt { font-size: 12px; color: var(--muted); } | |
| .image-card { | |
| background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: 12px; overflow: hidden; display: flex; flex-direction: column; | |
| } | |
| .image-card img { width: 100%; display: block; aspect-ratio: 1; object-fit: cover; } | |
| .image-card-footer { | |
| padding: 8px 10px; display: flex; justify-content: space-between; | |
| align-items: center; gap: 8px; | |
| } | |
| .image-card-prompt { | |
| font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; | |
| white-space: nowrap; flex: 1; | |
| } | |
| .image-card-actions { display: flex; gap: 4px; flex-shrink: 0; } | |
| .img-loading-card { | |
| background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: 12px; aspect-ratio: 1; display: flex; flex-direction: column; | |
| align-items: center; justify-content: center; gap: 10px; color: var(--muted); font-size: 12px; | |
| } | |
| .spinner { | |
| width: 32px; height: 32px; border: 3px solid var(--border); | |
| border-top-color: var(--accent); border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .img-welcome { margin: auto; text-align: center; color: var(--muted); padding: 20px; grid-column: 1/-1; } | |
| .img-welcome h2 { font-size: 28px; color: var(--accent); margin-bottom: 8px; } | |
| /* ββββββββββ RESPONSIVE β TABLET / MOBILE ββββββββββ */ | |
| @media (max-width: 860px) { | |
| #sidebar { | |
| position: fixed; top: 0; bottom: 0; left: 0; | |
| margin-left: calc(-1 * var(--sidebar-w)); | |
| box-shadow: 4px 0 20px rgba(0,0,0,0.5); | |
| } | |
| #sidebar.mobile-open { margin-left: 0; } | |
| .input-hint { display: none; } | |
| .msg { max-width: 90%; } | |
| .welcome h2 { font-size: 24px; } | |
| #stats-bar { font-size: 9px; gap: 12px; padding: 6px 12px; } | |
| #topbar { padding: 10px 12px; flex-wrap: wrap; } | |
| .mode-tabs { order: 3; width: 100%; margin-left: 0; margin-top: 4px; } | |
| } | |
| @media (max-width: 480px) { | |
| .msg { max-width: 92%; font-size: 13.5px; } | |
| .sidebar-header, .settings-panel { padding-left: 14px; padding-right: 14px; } | |
| #chat { padding: 12px; gap: 12px; } | |
| #stats-bar > div:nth-child(n+4) { display: none; } | |
| #image-gallery { grid-template-columns: 1fr 1fr; gap: 10px; padding: 12px; } | |
| .image-controls { flex-direction: column; align-items: stretch; } | |
| #generate-img-btn { width: 100%; } | |
| #quick-bar { padding: 8px; } | |
| #model-select { font-size: 11px; } | |
| .mode-pill { padding: 6px 8px; font-size: 10px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="overlay" onclick="closeSidebarMobile()"></div> | |
| <!-- SIDEBAR --> | |
| <div id="sidebar"> | |
| <div class="sidebar-header"> | |
| <h2>π€ THC LLM</h2> | |
| <button id="new-chat-btn" onclick="newChat()">οΌ Nova conversa</button> | |
| </div> | |
| <div id="conversations"></div> | |
| <div class="settings-panel"> | |
| <h3>βοΈ ConfiguraΓ§Γ΅es</h3> | |
| <div class="slider-group"> | |
| <label>Temperature <span id="temp-val">0.7</span></label> | |
| <input type="range" id="temperature" min="0" max="2" step="0.1" value="0.7" | |
| oninput="document.getElementById('temp-val').textContent=this.value"> | |
| </div> | |
| <div class="slider-group"> | |
| <label>Max Tokens <span id="tokens-val">8192</span></label> | |
| <input type="range" id="max-tokens" min="256" max="16384" step="256" value="8192" | |
| oninput="document.getElementById('tokens-val').textContent=this.value"> | |
| </div> | |
| <div> | |
| <label style="font-size:12px;color:var(--muted);display:block;margin-bottom:6px;">System Prompt</label> | |
| <textarea id="system-prompt" placeholder="Ex: VocΓͺ Γ© o assistente da Tec Haze Circuit..."></textarea> | |
| </div> | |
| <h3>π Voz</h3> | |
| <div class="toggle-row"> | |
| <span>Auto-falar respostas</span> | |
| <button class="toggle on" id="toggle-tts" onclick="toggleTTS(this)"></button> | |
| </div> | |
| <h3>π Modo Livre</h3> | |
| <div class="toggle-row"> | |
| <span>Pular RAG e persona</span> | |
| <button class="toggle" id="free-toggle-sidebar" onclick="setFreeMode(!freeModeEnabled)"></button> | |
| </div> | |
| <div class="slider-group"> | |
| <label>Velocidade <span id="rate-val">1.0</span>x</label> | |
| <input type="range" id="voice-rate" min="0.5" max="2" step="0.1" value="1.0" | |
| oninput="document.getElementById('rate-val').textContent=this.value"> | |
| </div> | |
| <div class="slider-group"> | |
| <label>Tom <span id="pitch-val">1.0</span></label> | |
| <input type="range" id="voice-pitch" min="0.5" max="2" step="0.1" value="1.0" | |
| oninput="document.getElementById('pitch-val').textContent=this.value"> | |
| </div> | |
| <div> | |
| <label style="font-size:12px;color:var(--muted);display:block;margin-bottom:6px;">Voz</label> | |
| <select id="voice-select"><option>Carregando vozes...</option></select> | |
| </div> | |
| <div class="ios-notice" id="ios-notice"> | |
| π± No iPhone, o reconhecimento de voz (microfone) nΓ£o Γ© suportado pelo Safari β mas a fala do assistente funciona normalmente. Para ditar mensagens, use o microfone do teclado do iOS. | |
| </div> | |
| <h3>β¬ CLI THC</h3> | |
| <p style="font-size:12px;color:var(--muted);margin-bottom:10px;">Use o THC direto do seu terminal Linux.</p> | |
| <button onclick="navigator.clipboard.writeText('curl -sSL https://hulktoigo-thcllm.hf.space/install.sh | bash').then(()=>alert('Comando copiado!'))" style="width:100%;margin-bottom:8px;">π Copiar comando de instalaΓ§Γ£o</button> | |
| <a href="/download/thc-cli.tar.gz" download style="display:block;text-align:center;font-size:12px;color:var(--muted);text-decoration:underline;">ou baixar .tar.gz manualmente</a> | |
| <a href="/me" target="_blank" style="display:block;text-align:center;font-size:12px;color:var(--accent);margin-top:8px;">π Ver minha API key pessoal</a> | |
| </div> | |
| </div> | |
| <!-- MAIN --> | |
| <div id="main"> | |
| <div id="topbar"> | |
| <button id="toggle-sidebar" onclick="toggleSidebar()">β°</button> | |
| <div class="topbar-info"> | |
| <h1>Tec Haze Circuit</h1> | |
| <p id="model-label">google/gemma-3-1b-it β’ CPU</p> | |
| </div> | |
| <div class="mode-tabs"> | |
| <button class="mode-tab active" id="tab-chat" onclick="switchMode('chat')">π¬ Chat</button> | |
| <button class="mode-tab" id="tab-image" onclick="switchMode('image')">π¨ Imagem</button> | |
| <button class="mode-tab" id="tab-audio" onclick="switchMode('audio')">π΅ Γudio</button> | |
| </div> | |
| <div class="status-dot"></div> | |
| </div> | |
| <div id="stats-bar"> | |
| <div>Msgs: <span id="stat-msgs">0</span></div> | |
| <div>Enviados: <span id="stat-prompt">0</span></div> | |
| <div>Gerados: <span id="stat-completion">0</span></div> | |
| <div>Total: <span id="stat-total">0</span></div> | |
| <div>Tempo: <span id="stat-time">β</span></div> | |
| </div> | |
| <!-- CHAT VIEW --> | |
| <div id="chat-view" style="display:flex; flex-direction:column; flex:1; overflow:hidden;"> | |
| <div id="quick-bar"> | |
| <select id="model-select" onchange="onModelChange()"> | |
| <option value="gemma-1b">Gemma 3 1B</option> | |
| </select> | |
| <div class="mode-pills"> | |
| <button class="mode-pill" id="web-toggle" onclick="toggleWeb()">π Web</button> | |
| <button class="mode-pill" id="free-toggle" onclick="toggleFreeMode()">π Livre</button> | |
| <button class="mode-pill" data-mode="fast" onclick="setMode('fast')">β‘ Fast</button> | |
| <button class="mode-pill active" data-mode="medium" onclick="setMode('medium')">βοΈ MΓ©dio</button> | |
| <button class="mode-pill" data-mode="thinking" onclick="setMode('thinking')">π§ Thinking</button> | |
| </div> | |
| </div> | |
| <div class="model-loading-hint" id="model-loading-hint">β³ Trocando modelo β a primeira resposta pode demorar mais...</div> | |
| <div id="chat"> | |
| <div class="welcome"> | |
| <h2>THC LLM ποΈ</h2> | |
| <p>Fale ou escreva β o assistente responde em voz e texto!</p> | |
| <div class="suggestions"> | |
| <div class="suggestion" onclick="suggest(this)">Quais produtos vocΓͺs vendem?</div> | |
| <div class="suggestion" onclick="suggest(this)">Me ajude a escolher um produto</div> | |
| <div class="suggestion" onclick="suggest(this)">Formas de pagamento?</div> | |
| <div class="suggestion" onclick="suggest(this)">Como funciona a entrega?</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="speaking-bar"> | |
| <div class="wave"><span></span><span></span><span></span><span></span><span></span></div> | |
| <span>Assistente falando...</span> | |
| <button id="stop-speak-btn" onclick="stopSpeaking()">β Parar</button> | |
| </div> | |
| <div id="input-area"> | |
| <div class="input-wrap"> | |
| <textarea id="msg" rows="1" placeholder="Digite ou fale..."></textarea> | |
| <div id="mic-transcript"></div> | |
| <div class="input-hint">Enter β΅ envia β’ Shift+Enter quebra linha</div> | |
| </div> | |
| <div class="action-buttons"> | |
| <button id="mic-btn" onclick="toggleMic()" title="Falar">ποΈ</button> | |
| <button id="send-btn" onclick="sendMessage()" title="Enviar">β€</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- IMAGE VIEW --> | |
| <div id="image-view"> | |
| <div id="image-prompt-area"> | |
| <textarea id="image-prompt" placeholder="Descreva a imagem que quer gerar... ex: logo minimalista de tabacaria, estilo neon verde"></textarea> | |
| <div class="image-controls"> | |
| <div class="slider-group"> | |
| <label>Passos <span id="steps-val">2</span></label> | |
| <input type="range" id="img-steps" min="1" max="4" step="1" value="2" | |
| oninput="document.getElementById('steps-val').textContent=this.value"> | |
| </div> | |
| <div class="slider-group"> | |
| <label>Tamanho <span id="size-val">512</span>px</label> | |
| <input type="range" id="img-size" min="256" max="768" step="128" value="512" | |
| oninput="document.getElementById('size-val').textContent=this.value"> | |
| </div> | |
| <button id="generate-img-btn" onclick="generateImage()">π¨ Gerar</button> | |
| </div> | |
| </div> | |
| <div id="image-gallery"> | |
| <div class="img-welcome"> | |
| <h2>π¨ Gerador de Imagens</h2> | |
| <p>Descreva o que quer criar acima. No CPU, cada imagem leva ~30-90s.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- AUDIO VIEW --> | |
| <div id="audio-view"> | |
| <div id="audio-prompt-area"> | |
| <textarea id="audio-prompt" placeholder="Descreva a mΓΊsica... ex: lo-fi hip hop instrumental, chuva ao fundo, clima noturno"></textarea> | |
| <div class="image-controls"> | |
| <select id="audio-model"> | |
| <option value="lyria-pro-preview">Lyria 3 Pro (mΓΊsica completa) β pago ~$0.08</option> | |
| <option value="lyria-clip-preview">Lyria 3 Clip (30s) β pago ~$0.04</option> | |
| </select> | |
| <button id="generate-audio-btn" onclick="generateAudio()">π΅ Gerar</button> | |
| </div> | |
| </div> | |
| <div id="audio-gallery"> | |
| <div class="img-welcome"> | |
| <h2>π΅ Gerador de MΓΊsica</h2> | |
| <p>Modelos Lyria sΓ£o pagos β confirme o custo por geraΓ§Γ£o antes de usar.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| </script> | |
| </body> | |
| </html> | |