HELIKON VOICE v6.4
SYSBOOT
API...
VOICE...
PROVIDER...
MODEL...
CREDITS...
TTSOFF ☎Helikon anrufen
❚❚Pause 0:00
Text→Sprache Sprache→Sprache Sprache→Text Wiki-Suche
Stimme: Stimme 1 Stimme 2
● Aufnehmen 0s (function() { /* ============================================================ * HELIKON VOICE v6.4 — 2026-03-01 * Margin fix. Voice names from properties. Icon fix. * DiagLog height fix. Backward compat buddy.ttsvoiceid. * Provider-to-Voice mapping: LLMs speak with own voices. * ============================================================ */ var PF = '"Press Start 2P","Courier New",Courier,monospace'; var CRISP = '-webkit-font-smoothing:none; -moz-osx-font-smoothing:unset; font-smooth:never; text-rendering:optimizeSpeed;'; var css = '' /* === CONTAINER — FULL WIDTH === */ + '#helikonTTS { margin:0; font-family:' + PF + '; ' + CRISP + ' border:2px solid #ccc; border-radius:8px; background:#f8f8f8; padding:14px; position:relative; overflow:hidden; }' /* ============================================================ SYSTEM DIAGNOSTICS PANEL ============================================================ */ + '#ttsDiag { background:#f0f0f0; border:2px solid #ddd; border-radius:6px; padding:10px 12px; margin:0 0 10px 0; }' + '#diagTitle { font-size:8px; color:#0077aa; letter-spacing:2px; text-transform:uppercase; margin:0 0 8px 0; padding:0 0 6px 0; border-bottom:1px solid #ddd; }' /* LED Grid */ + '#diagGrid { display:flex; flex-wrap:wrap; gap:0; }' + '.diagCell { display:flex; align-items:center; gap:6px; padding:5px 12px 5px 0; min-width:100px; flex:1; }' + '.diagLed { display:inline-block; width:8px; height:8px; background:#ccc; border:1px solid #aaa; flex-shrink:0; transition:all 0.3s ease; }' + '.diagKey { font-size:7px; color:#999; letter-spacing:1px; text-transform:uppercase; min-width:42px; }' + '.diagVal { font-size:7px; color:#666; letter-spacing:0.5px; text-transform:uppercase; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:120px; }' /* LED states */ + '.diagCell.ok .diagLed { background:#3a8f3a; border-color:#2a6f2a; box-shadow:0 0 4px rgba(58,143,58,0.4); }' + '.diagCell.ok .diagVal { color:#3a8f3a; }' + '.diagCell.warn .diagLed { background:#cc8800; border-color:#aa6600; box-shadow:0 0 4px rgba(204,136,0,0.4); }' + '.diagCell.warn .diagVal { color:#cc8800; }' + '.diagCell.err .diagLed { background:#cc3333; border-color:#aa2222; box-shadow:0 0 6px rgba(204,51,51,0.5); animation:ledBlink 1s infinite; }' + '.diagCell.err .diagVal { color:#cc3333; }' + '.diagCell.off .diagLed { background:#ccc; border-color:#aaa; }' + '.diagCell.off .diagVal { color:#999; }' + '.diagCell.boot .diagLed { background:#0077aa; border-color:#005580; animation:ledBoot 0.6s infinite; }' + '.diagCell.boot .diagVal { color:#0077aa; }' + '@keyframes ledBlink { 0%,100% { opacity:1; } 50% { opacity:0.3; } }' + '@keyframes ledBoot { 0%,100% { opacity:0.3; } 50% { opacity:1; } }' /* Diag log — boot messages */ + '#diagLog { margin:6px 0 0 0; padding:4px 0 0 0; border-top:1px dashed #ddd; font-size:7px; color:#999; line-height:1.8; max-height:80px; overflow:hidden; }' + '#diagLog .dlog { opacity:0; transition:opacity 0.3s ease; }' + '#diagLog .dlog.vis { opacity:1; }' /* === WAVE VISUALIZER === */ + '#ttsWaveWrap { height:40px; margin:0 0 10px 0; overflow:hidden; border-radius:4px; background:#eee; border:1px solid #ddd; }' + '#ttsWave { display:flex; align-items:flex-end; justify-content:center; gap:3px; height:100%; padding:4px 8px; }' + '.ttsBar { display:inline-block; width:7px; background:#0077aa; border-radius:2px 2px 0 0; height:4px; opacity:0.2; transition:height 0.15s ease, opacity 0.15s ease; }' + '#helikonTTS.tts-on .ttsBar { opacity:0.5; }' + '@keyframes waveIdle { 0%,100% { height:4px; opacity:0.3; } 50% { height:12px; opacity:0.6; } }' + '#helikonTTS.tts-on .ttsBar { animation:waveIdle 1.5s ease-in-out infinite; }' + '#helikonTTS.tts-on .ttsBar:nth-child(1) { animation-delay:0s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(2) { animation-delay:0.1s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(3) { animation-delay:0.2s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(4) { animation-delay:0.3s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(5) { animation-delay:0.4s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(6) { animation-delay:0.5s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(7) { animation-delay:0.6s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(8) { animation-delay:0.7s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(9) { animation-delay:0.6s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(10) { animation-delay:0.5s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(11) { animation-delay:0.4s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(12) { animation-delay:0.3s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(13) { animation-delay:0.2s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(14) { animation-delay:0.1s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(15) { animation-delay:0s; }' + '#helikonTTS.tts-on .ttsBar:nth-child(16) { animation-delay:0.1s; }' /* Wave speaking */ + '@keyframes waveTalk { 0% { height:4px; } 15% { height:28px; } 30% { height:8px; } 45% { height:22px; } 60% { height:6px; } 75% { height:30px; } 90% { height:12px; } 100% { height:4px; } }' + '#helikonTTS.tts-speaking .ttsBar { opacity:1; animation:waveTalk 0.8s ease-in-out infinite; background:#0077aa; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(1) { animation-delay:0.05s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(2) { animation-delay:0.1s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(3) { animation-delay:0.15s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(4) { animation-delay:0.2s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(5) { animation-delay:0.25s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(6) { animation-delay:0.3s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(7) { animation-delay:0.35s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(8) { animation-delay:0.4s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(9) { animation-delay:0.35s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(10) { animation-delay:0.3s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(11) { animation-delay:0.25s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(12) { animation-delay:0.2s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(13) { animation-delay:0.15s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(14) { animation-delay:0.1s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(15) { animation-delay:0.05s; }' + '#helikonTTS.tts-speaking .ttsBar:nth-child(16) { animation-delay:0s; }' /* Call mode waves — green */ + '#helikonTTS.tts-call .ttsBar { opacity:0.5; animation:waveIdle 1.2s ease-in-out infinite; background:#3a8f3a; }' + '#helikonTTS.tts-call.tts-speaking .ttsBar { opacity:1; animation:waveTalk 0.8s ease-in-out infinite; background:#3a8f3a; }' /* ============================================================ CALL HERO SECTION ============================================================ */ + '#callHero { border:2px solid #ddd; border-radius:8px; background:#fff; padding:14px; margin:0 0 10px 0; display:flex; align-items:center; gap:10px; flex-wrap:wrap; transition:all 0.3s ease; }' + '#callHeroBtn { display:flex; align-items:center; gap:10px; padding:14px 22px; border:3px solid #3a8f3a; border-radius:8px; background:#f0fff0; color:#2a6f2a; cursor:pointer; font-family:' + PF + '; font-size:12px; transition:all 0.25s ease; flex:1; min-width:200px; justify-content:center; }' + '#callHeroBtn:hover { background:#e0ffe0; border-color:#2a6f2a; transform:translateY(-1px); box-shadow:0 4px 12px rgba(58,143,58,0.2); }' + '#callIcon { font-size:20px; display:inline-block; transition:transform 0.3s ease; }' + '#callBtnLabel { letter-spacing:0.5px; }' + '@keyframes phoneRing { 0% { transform:rotate(0deg); } 5% { transform:rotate(15deg); } 10% { transform:rotate(-15deg); } 15% { transform:rotate(12deg); } 20% { transform:rotate(-12deg); } 25% { transform:rotate(0deg); } 100% { transform:rotate(0deg); } }' + '@keyframes heroPulse { 0%,100% { box-shadow:0 0 0 0 rgba(58,143,58,0.3); } 50% { box-shadow:0 0 0 10px rgba(58,143,58,0); } }' + '#callHero.active { border-color:#3a8f3a; background:#f0fff0; }' + '#callHero.active #callHeroBtn { background:#3a8f3a; color:#fff; border-color:#2a6f2a; animation:heroPulse 2s infinite; }' + '#callHero.active #callHeroBtn:hover { background:#2a6f2a; }' + '#callHero.active #callIcon { animation:phoneRing 2s ease-in-out infinite; }' + '#callHero.paused { border-color:#cc8800; background:#fffbf0; }' + '#callHero.paused #callHeroBtn { background:#3a8f3a; color:#fff; border-color:#2a6f2a; animation:none; opacity:0.7; }' + '#callHero.paused #callIcon { animation:none; }' + '#callPauseBtn { display:none; align-items:center; gap:6px; padding:14px 16px; border:2px solid #cc8800; border-radius:8px; background:#fffbf0; color:#996600; cursor:pointer; font-family:' + PF + '; font-size:10px; transition:all 0.25s ease; }' + '#callPauseBtn:hover { background:#fff0d0; border-color:#996600; }' + '#pauseIcon { font-size:14px; }' + '#callHero.active #callPauseBtn { display:flex; }' + '#callHero.paused #callPauseBtn { display:flex; background:#cc8800; color:#fff; border-color:#996600; }' + '#callPhaseDisplay { display:none; align-items:center; gap:8px; width:100%; padding:8px 0 0 0; }' + '#callHero.active #callPhaseDisplay { display:flex; }' + '#callHero.paused #callPhaseDisplay { display:flex; }' + '#callPhaseDot { display:inline-block; width:12px; height:12px; border-radius:50%; background:#999; transition:all 0.3s ease; flex-shrink:0; }' + '#callPhaseText { font-size:9px; color:#666; flex:1; letter-spacing:0.5px; }' + '#callTimer { font-size:9px; color:#999; min-width:40px; text-align:right; }' + '@keyframes listenBreathe { 0%,100% { transform:scale(1); opacity:1; } 50% { transform:scale(1.4); opacity:0.5; } }' + '@keyframes procSpin { 0% { opacity:1; } 50% { opacity:0.3; } 100% { opacity:1; } }' + '@keyframes speakWave { 0%,100% { transform:scale(1); } 30% { transform:scale(1.3); } 60% { transform:scale(0.8); } }' + '#callPhaseDot.listening { background:#4CAF50; animation:listenBreathe 1.5s ease-in-out infinite; }' + '#callPhaseDot.processing { background:#ff9800; animation:procSpin 0.6s ease-in-out infinite; }' + '#callPhaseDot.speaking { background:#0077aa; animation:speakWave 0.8s ease-in-out infinite; }' + '#callPhaseDot.waiting { background:#ff9800; animation:procSpin 1s ease-in-out infinite; }' + '#callPhaseDot.paused { background:#cc8800; animation:none; }' /* === TOGGLE BUTTONS === */ + '#ttsControls { display:flex; align-items:center; gap:6px; padding:8px 0; flex-wrap:wrap; }' + '.voice-btn { background:#fff; border:2px solid #ccc; color:#555; padding:8px 12px; cursor:pointer; font-family:' + PF + '; font-size:9px; display:inline-flex; align-items:center; gap:6px; border-radius:4px; transition:all 0.2s ease; position:relative; }' + '.voice-btn:hover { border-color:#0077aa; color:#0077aa; }' + '.voice-btn::before { content:""; display:inline-block; width:8px; height:8px; border:2px solid #ccc; background:#eee; border-radius:0; transition:all 0.15s ease; }' + '.voice-btn.active { background:#e8f4ff; border-color:#0077aa; color:#0077aa; }' + '.voice-btn.active::before { background:#0077aa; border-color:#005580; box-shadow:0 0 6px rgba(0,119,170,0.5); }' + '.voice-btn.disabled { background:#f5f5f5; border-color:#ddd; color:#bbb; cursor:default; }' + '.voice-btn.disabled:hover { border-color:#ddd; color:#bbb; }' + '.voice-btn.disabled::before { background:#ddd; border-color:#ccc; }' + '#searchToggle:hover { border-color:#cc8800; color:#cc8800; }' + '#searchToggle.active { background:#fff8e8; border-color:#cc8800; color:#cc8800; }' + '#searchToggle.active::before { background:#cc8800; border-color:#aa6600; box-shadow:0 0 6px rgba(204,136,0,0.5); }' /* === VOICE SELECTOR === */ + '#ttsVoiceSel { display:flex; align-items:center; gap:6px; padding:6px 0; border-top:1px solid #eee; }' + '#voiceLabel { font-size:7px; color:#999; text-transform:uppercase; letter-spacing:0.5px; margin-right:2px; }' + '.voice-opt { background:#fff; border:2px solid #ddd; color:#888; padding:6px 10px; cursor:pointer; font-family:' + PF + '; font-size:8px; border-radius:4px; transition:all 0.2s ease; }' + '.voice-opt:hover { border-color:#0077aa; color:#0077aa; }' + '.voice-opt.active { background:#e8f4ff; border-color:#0077aa; color:#0077aa; }' /* === RECORDING BAR === */ + '#voiceRecBar { display:none; align-items:center; gap:10px; padding:8px 0; border-top:1px solid #eee; }' + '#voiceRecBar.visible { display:flex; }' + '#voiceRecBtn { background:#fff; border:2px solid #cc3333; color:#cc3333; padding:10px 14px; cursor:pointer; font-family:' + PF + '; font-size:9px; border-radius:4px; transition:all 0.2s ease; }' + '#voiceRecBtn:hover { background:#cc3333; color:#fff; }' + '#voiceRecBtn.recording { background:#cc3333; color:#fff; animation:recPulse 1s infinite; box-shadow:0 0 10px rgba(204,51,51,0.4); }' + '@keyframes recPulse { 0%,100% { opacity:1; } 50% { opacity:0.6; } }' + '#voiceRecTime { color:#cc3333; font-size:9px; min-width:30px; }' + '#voiceRecStatus { color:#999; font-size:7px; }' /* === PLAY BUTTONS === */ + '.tts-play-btn { background:#fff; border:1px solid #ddd; color:#999; padding:3px 8px; cursor:pointer; font-size:8px; margin-left:8px; font-family:' + PF + '; display:none; border-radius:3px; transition:all 0.2s ease; }' + '.tts-play-btn:hover { border-color:#0077aa; color:#0077aa; }' + '.tts-play-btn.playing { border-color:#0077aa; color:#0077aa; display:inline-block !important; box-shadow:0 0 6px rgba(0,119,170,0.3); }' + '.tts-active .tts-play-btn { display:inline-block; }' /* === CALL CHAT STATUS === */ + '.chatMsg.call-status { background:transparent !important; border:none !important; text-align:center !important; padding:6px 10px !important; margin:4px auto !important; max-width:100% !important; color:#3a8f3a !important; font-size:9px !important; opacity:0.8; }' + '.chatMsg.call-status::before { content:none !important; }' + '.chatMsg.call-status.phase-listening { color:#4CAF50 !important; }' + '.chatMsg.call-status.phase-processing { color:#ff9800 !important; }' + '.chatMsg.call-status.phase-speaking { color:#0077aa !important; }' + '.chatMsg.call-status.phase-paused { color:#cc8800 !important; }' + '.chatMsg.call-status.phase-end { color:#cc3333 !important; }' /* ============================================================ MOBILE ============================================================ */ + '@media (max-width:600px) {' + ' #helikonTTS { padding:10px; }' + ' #diagGrid { flex-direction:column; gap:0; }' + ' .diagCell { min-width:0; padding:3px 0; }' + ' #callHero { flex-direction:column; }' + ' #callHeroBtn { min-width:0; width:100%; font-size:11px; padding:16px 14px; }' + ' #callPauseBtn { width:100%; justify-content:center; padding:12px; }' + ' #ttsControls { gap:4px; }' + ' .voice-btn { font-size:8px; padding:6px 8px; flex:1; min-width:0; justify-content:center; }' + ' #ttsVoiceSel { flex-wrap:wrap; }' + ' .voice-opt { flex:1; text-align:center; }' + ' #callPhaseText { font-size:8px; }' + '}' /* ============================================================ DARK MODE — body.dark-mode ============================================================ */ + 'body.dark-mode #helikonTTS { background:#0a0a1a; border-color:#1a1a3a; }' + 'body.dark-mode #ttsDiag { background:#0d0d1e; border-color:#1a1a3a; }' + 'body.dark-mode #diagTitle { color:#0077aa; border-color:#1a1a3a; }' + 'body.dark-mode .diagKey { color:#555; }' + 'body.dark-mode .diagVal { color:#888; }' + 'body.dark-mode .diagCell.ok .diagVal { color:#5ab85a; }' + 'body.dark-mode .diagCell.warn .diagVal { color:#cc8800; }' + 'body.dark-mode .diagCell.err .diagVal { color:#ff4444; }' + 'body.dark-mode .diagCell.boot .diagVal { color:#00ccff; }' + 'body.dark-mode #diagLog { border-color:#1a1a3a; color:#444; }' + 'body.dark-mode #ttsWaveWrap { background:#0d0d24; border-color:#1a1a3a; }' + 'body.dark-mode .ttsBar { background:#0077aa; }' + 'body.dark-mode #helikonTTS.tts-speaking .ttsBar { background:#00ccff; }' + 'body.dark-mode #helikonTTS.tts-call .ttsBar { background:#66ff66; }' + 'body.dark-mode #callHero { background:#0d0d20; border-color:#1a2a1a; }' + 'body.dark-mode #callHeroBtn { background:#0a1a0a; border-color:#3a8f3a; color:#5ab85a; }' + 'body.dark-mode #callHeroBtn:hover { background:#0f2a0f; }' + 'body.dark-mode #callHero.active #callHeroBtn { background:#3a8f3a; color:#fff; }' + 'body.dark-mode #callPauseBtn { background:#1a1500; border-color:#cc8800; color:#cc8800; }' + 'body.dark-mode #callHero.paused #callPauseBtn { background:#cc8800; color:#000; }' + 'body.dark-mode #callPhaseText { color:#888; }' + 'body.dark-mode #callTimer { color:#666; }' + 'body.dark-mode .voice-btn { background:#111; border-color:#333; color:#888; }' + 'body.dark-mode .voice-btn:hover { border-color:#0077aa; color:#0077aa; }' + 'body.dark-mode .voice-btn::before { background:#222; border-color:#444; }' + 'body.dark-mode .voice-btn.active { background:#0a1a2a; border-color:#0077aa; color:#0077aa; }' + 'body.dark-mode .voice-btn.active::before { background:#0077aa; border-color:#005580; }' + 'body.dark-mode .voice-btn.disabled { background:#0a0a0a; border-color:#222; color:#444; }' + 'body.dark-mode #searchToggle.active { background:#1a1500; border-color:#cc8800; color:#cc8800; }' + 'body.dark-mode #searchToggle.active::before { background:#cc8800; border-color:#aa6600; }' + 'body.dark-mode #ttsVoiceSel { border-color:#1a1a3a; }' + 'body.dark-mode .voice-opt { background:#111; border-color:#333; color:#666; }' + 'body.dark-mode .voice-opt:hover { border-color:#0077aa; color:#0077aa; }' + 'body.dark-mode .voice-opt.active { background:#0a1a2a; border-color:#00ccff; color:#00ccff; }' + 'body.dark-mode #voiceRecBar { border-color:#1a1a3a; }' + 'body.dark-mode #voiceRecBtn { background:#111; }' + 'body.dark-mode .tts-play-btn { background:#111; border-color:#333; color:#666; }' + 'body.dark-mode .tts-play-btn:hover { border-color:#0077aa; color:#0077aa; }' + 'body.dark-mode .chatMsg.call-status { opacity:0.7; }'; var styleEl = document.createElement('style'); styleEl.type = 'text/css'; if (styleEl.styleSheet) { styleEl.styleSheet.cssText = css; } else { styleEl.appendChild(document.createTextNode(css)); } document.getElementsByTagName('head')[0].appendChild(styleEl); var fontLink = document.createElement('link'); fontLink.rel = 'stylesheet'; fontLink.href = '
https://fonts.googleapis.com/css2?fa...Press+Start+2P'; document.head.appendChild(fontLink); /* === DOM === */ var ttsWrap = document.getElementById('helikonTTS'); var ttsBtn = document.getElementById('ttsToggle'); var stsBtn = document.getElementById('stsToggle'); var sttBtn = document.getElementById('sttToggle'); var searchBtn = document.getElementById('searchToggle'); var recBar = document.getElementById('voiceRecBar'); var recBtn = document.getElementById('voiceRecBtn'); var recTime = document.getElementById('voiceRecTime'); var recStatus = document.getElementById('voiceRecStatus'); var voiceOpt1 = document.getElementById('voiceOpt1'); var voiceOpt2 = document.getElementById('voiceOpt2'); var callHero = document.getElementById('callHero'); var callHeroBtn = document.getElementById('callHeroBtn'); var callPauseBtn = document.getElementById('callPauseBtn'); var callPhaseDot = document.getElementById('callPhaseDot'); var callPhaseText= document.getElementById('callPhaseText'); var callTimerEl = document.getElementById('callTimer'); var pauseBtnLabel= document.getElementById('pauseBtnLabel'); var pauseIcon = document.getElementById('pauseIcon'); /* Diagnostics */ var dSys = document.getElementById('dSys'); var dApi = document.getElementById('dApi'); var dVoice = document.getElementById('dVoice'); var dModel = document.getElementById('dModel'); var dProvider = document.getElementById('dProvider'); var dCredits = document.getElementById('dCredits'); var dTts = document.getElementById('dTts'); var dSysVal = document.getElementById('dSysVal'); var dApiVal = document.getElementById('dApiVal'); var dVoiceVal = document.getElementById('dVoiceVal'); var dModelVal = document.getElementById('dModelVal'); var dProviderVal = document.getElementById('dProviderVal'); var dCreditsVal = document.getElementById('dCreditsVal'); var dTtsVal = document.getElementById('dTtsVal'); var diagLog = document.getElementById('diagLog'); if (!ttsBtn) return; ttsBtn.className = 'voice-btn'; stsBtn.className = 'voice-btn disabled'; sttBtn.className = 'voice-btn'; searchBtn.className = 'voice-btn active'; /* === DIAGNOSTICS HELPERS === */ function setDiag(cell, valEl, state, text) { if (!cell || !valEl) return; cell.className = 'diagCell ' + state; valEl.textContent = text; } var diagLogLines = []; var diagLogInited = false; function logDiag(msg) { if (!diagLog) return; if (!diagLogInited) { diagLog.innerHTML = ''; diagLogInited = true; } var line = document.createElement('div'); line.className = 'dlog'; line.textContent = '> ' + msg; diagLog.appendChild(line); setTimeout(function() { line.className = 'dlog vis'; }, 30); diagLogLines.push(line); /* Keep max 4 lines */ while (diagLogLines.length > 4) { var old = diagLogLines.shift(); if (old.parentNode) old.parentNode.removeChild(old); } } function updateDiagTts() { if (callActive) { setDiag(dTts, dTtsVal, 'ok', 'CALL'); } else if (ttsActive) { setDiag(dTts, dTtsVal, 'ok', 'ON'); } else if (sttActive) { setDiag(dTts, dTtsVal, 'ok', 'STT'); } else { setDiag(dTts, dTtsVal, 'off', 'OFF'); } } /* === VOICES === */ var VOICES = new Array(2); VOICES[0] = new Object(); VOICES[0].id = ''; VOICES[0].label = 'Stimme 1'; VOICES[1] = new Object(); VOICES[1].id = 'rKiu7lQ4c5P3az3745s3'; VOICES[1].label = 'Stimme 2'; var VOICE_ICON = '\u266A '; var activeVoiceIdx = 0; var providerVoiceMap = new Object(); providerVoiceMap['local'] = 1; providerVoiceMap['claude'] = 0; /* === STATE === */ var ttsActive = false, stsActive = false, sttActive = false, callActive = false; var callPaused = false, searchActive = true; var ttsApiKey = '', voiceId = '', modelId = 'eleven_multilingual_v2'; var currentAudio = null, isSpeaking = false, isRecording = false; var mediaRecorder = null, audioChunks = [], recTimer = null, recSeconds = 0; var creditsOk = true; var callStream = null, callAnalyser = null, callAudioCtx = null; var callPhase = 'idle'; var callSeconds = 0, callTimerId = null; var SILENCE_THRESHOLD = 15, SILENCE_DURATION = 1800; var CALL_TEXT_MAX = 250; var pageId = 0; if (typeof Deki !== 'undefined' && Deki.PageId) { pageId = Deki.PageId; } window.helikonSearchEnabled = true; window.helikonCallMode = false; /* Boot SYS immediately */ setDiag(dSys, dSysVal, 'boot', 'BOOT'); setDiag(dApi, dApiVal, 'boot', '...'); setDiag(dVoice, dVoiceVal, 'boot', '...'); setDiag(dProvider, dProviderVal, 'boot', '...'); setDiag(dModel, dModelVal, 'boot', '...'); setDiag(dCredits, dCreditsVal, 'boot', '...'); setDiag(dTts, dTtsVal, 'off', 'OFF'); logDiag('HELIKON VOICE v6.4 booting...'); /* === FETCH ALL PROPERTIES === */ function fetchAllProperties(callback) { if (!pageId) { callback(new Object()); return; } var url = '/@api/deki/pages/' + pageId + '/properties'; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onload = function() { var props = new Object(); if (xhr.status === 200) { try { var parser = new DOMParser(); var xml = parser.parseFromString(xhr.responseText, 'text/xml'); var entries = xml.getElementsByTagName('property'); for (var i = 0; i < entries.length; i++) { var nameEl = entries[i].getAttribute('name'); if (!nameEl) continue; var short = nameEl.replace('urn:custom.mindtouch.com#', ''); var contentsEl = entries[i].getElementsByTagName('contents'); if (contentsEl.length > 0) { var href = contentsEl[0].getAttribute('href'); if (href) { props['_href_' + short] = href; } var textNode = contentsEl[0].textContent; if (textNode) { props[short] = textNode.replace(/^\s+|\s+$/g, ''); } } } } catch(e) {} } var hrefKeys = []; for (var k in props) { if (k.indexOf('_href_') === 0) { hrefKeys.push(k.replace('_href_', '')); } } if (hrefKeys.length === 0) { callback(props); return; } var done = 0; for (var h = 0; h < hrefKeys.length; h++) { (function(propName) { var xh = new XMLHttpRequest(); xh.open('GET', props['_href_' + propName], true); xh.onload = function() { if (xh.status === 200) { props[propName] = xh.responseText.replace(/^\s+|\s+$/g, ''); } done++; if (done === hrefKeys.length) callback(props); }; xh.onerror = function() { done++; if (done === hrefKeys.length) callback(props); }; xh.send(); })(hrefKeys[h]); } }; xhr.onerror = function() { callback(new Object()); }; xhr.send(); } /* === CREDIT STATE === */ function setCreditState(state) { if (state === 'empty') { setDiag(dCredits, dCreditsVal, 'err', 'LEER!'); creditsOk = false; logDiag('CREDITS: Kein Guthaben!'); } else { setDiag(dCredits, dCreditsVal, 'ok', 'OK'); creditsOk = true; } } /* === UI HELPERS === */ function setRecStatus(msg) { if (recStatus) recStatus.textContent = msg; } function getActiveVoiceId() { return VOICES[activeVoiceIdx].id || voiceId; } function formatTime(sec) { var m = Math.floor(sec / 60); var s = sec % 60; return m + ':' + (s < 10 ? '0' : '') + s; } function setCallPhaseUI(phase) { callPhase = phase; if (callPhaseDot) callPhaseDot.className = phase; var labels = new Object(); labels.idle = ''; labels.listening = '\u{1F3A4} Sprich jetzt...'; labels.processing = '\u{1F504} Verarbeite...'; labels.speaking = '\u{1F50A} Helikon spricht...'; labels.waiting = '\u{23F3} Helikon denkt nach...'; labels.paused = '\u{23F8} Pausiert'; if (callPhaseText) callPhaseText.textContent = labels[phase] || ''; } function updateWrapClass() { var cls = ''; if (ttsActive || sttActive) cls = 'tts-on'; if (isSpeaking) cls = 'tts-speaking'; if (callActive && !isSpeaking) cls = 'tts-call'; if (callActive && isSpeaking) cls = 'tts-call tts-speaking'; ttsWrap.className = cls; } function updateButtons() { ttsBtn.className = 'voice-btn' + (ttsActive ? ' active' : ''); stsBtn.className = 'voice-btn disabled'; sttBtn.className = 'voice-btn' + (sttActive ? ' active' : ''); searchBtn.className = 'voice-btn' + (searchActive ? ' active' : ''); window.helikonSearchEnabled = searchActive; if (callActive && callPaused) { callHero.className = 'paused'; } else if (callActive) { callHero.className = 'active'; } else { callHero.className = ''; } if (sttActive) { recBar.className = 'visible'; } else { recBar.className = ''; } var chatLog = document.getElementById('chatLog'); if (chatLog) { if (ttsActive || callActive) { chatLog.className = chatLog.className.replace(' tts-active', '') + ' tts-active'; } else { chatLog.className = chatLog.className.replace(' tts-active', ''); stopAudio(); } } updateWrapClass(); updateDiagTts(); } /* === VOICE SELECTOR === */ function selectVoice(idx) { activeVoiceIdx = idx; voiceOpt1.textContent = VOICE_ICON + VOICES[0].label; voiceOpt2.textContent = VOICE_ICON + VOICES[1].label; voiceOpt1.className = 'voice-opt' + (idx === 0 ? ' active' : ''); voiceOpt2.className = 'voice-opt' + (idx === 1 ? ' active' : ''); logDiag('Voice: ' + VOICES[idx].label); } voiceOpt1.onclick = function() { selectVoice(0); }; voiceOpt2.onclick = function() { selectVoice(1); }; /* === CALL TIMER === */ function startCallTimer() { callSeconds = 0; callTimerEl.textContent = '0:00'; callTimerId = setInterval(function() { callSeconds++; callTimerEl.textContent = formatTime(callSeconds); }, 1000); } function stopCallTimer() { if (callTimerId) { clearInterval(callTimerId); callTimerId = null; } } /* === CHAT MESSAGES === */ function addChatMsg(text, type) { var chatLog = document.getElementById('chatLog'); if (!chatLog) return; var div = document.createElement('div'); div.className = 'chatMsg ' + type; div.textContent = text; chatLog.appendChild(div); chatLog.scrollTop = chatLog.scrollHeight; } function addCallStatus(text, phase) { var chatLog = document.getElementById('chatLog'); if (!chatLog) return; var old = chatLog.querySelectorAll('.chatMsg.call-status'); for (var o = 0; o < old.length; o++) { var cls = old[o].className || ''; if (cls.indexOf('phase-end') === -1) { old[o].parentNode.removeChild(old[o]); } } var div = document.createElement('div'); div.className = 'chatMsg call-status phase-' + phase; div.textContent = text; chatLog.appendChild(div); chatLog.scrollTop = chatLog.scrollHeight; } /* === TEXT HELPERS === */ function cleanText(text) { var LT = String.fromCharCode(60); var GT = String.fromCharCode(62); var LBRACE = String.fromCharCode(123); var RBRACE = String.fromCharCode(125); var htmlTagRe = new RegExp(LT + '[^' + GT + ']*' + GT, 'g'); var headingRe = new RegExp('#' + LBRACE + '1,6' + RBRACE + '\\s', 'g'); var linkRe = new RegExp('\\[([^\\]]+)\\]\\([^)]+\\)', 'g'); var clean = text.replace(htmlTagRe, '').replace(/\*\*([^*]+)\*\*/g, '$1').replace(/\*([^*]+)\*/g, '$1').replace(/`([^`]+)`/g, '$1').replace(headingRe, '').replace(linkRe, '$1').replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, ''); return clean; } function truncateForCall(text) { if (!callActive || text.length <= CALL_TEXT_MAX) return text; var cut = text.substring(0, CALL_TEXT_MAX); var lastDot = cut.lastIndexOf('.'); var lastExcl = cut.lastIndexOf('!'); var lastQ = cut.lastIndexOf('?'); var best = Math.max(lastDot, lastExcl, lastQ); if (best > CALL_TEXT_MAX * 0.3) return cut.substring(0, best + 1); var lastSpace = cut.lastIndexOf(' '); if (lastSpace > CALL_TEXT_MAX * 0.5) return cut.substring(0, lastSpace) + '...'; return cut + '...'; } /* === TTS === */ function speak(text, onDone) { var activeId = getActiveVoiceId(); if (!ttsApiKey || !activeId) { logDiag('TTS: nicht konfiguriert'); if (onDone) onDone(); return; } if (!creditsOk) { logDiag('TTS: Keine Credits!'); if (onDone) onDone(); return; } var clean = cleanText(text); if (!clean) { if (onDone) onDone(); return; } clean = truncateForCall(clean); if (clean.length > 5000) { clean = clean.substring(0, 5000); } if (typeof console !== 'undefined') { console.log('TTS (' + VOICES[activeVoiceIdx].label + '): "' + clean.substring(0, 60) + '..."'); } isSpeaking = true; updateWrapClass(); if (callActive) { setCallPhaseUI('speaking'); addCallStatus('\u{1F50A} Helikon spricht...', 'speaking'); } logDiag('TTS: spreche (' + clean.length + ' chars)'); var url = '
https://api.elevenlabs.io/v1/text-to-speech/' + encodeURIComponent(activeId) + '?output_format=mp3_44100_128'; var bodyObj = new Object(); bodyObj.text = clean; bodyObj.model_id = modelId; var xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.responseType = 'blob'; xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('xi-api-key', ttsApiKey); xhr.onload = function() { if (xhr.status === 200) { playBlob(xhr.response, onDone); setCreditState('ok'); logDiag('TTS: OK'); } else if (xhr.status === 401 || xhr.status === 403) { isSpeaking = false; updateWrapClass(); setCreditState('empty'); logDiag('TTS: 401/403 \u2014 Credits leer?'); if (onDone) onDone(); } else { isSpeaking = false; updateWrapClass(); logDiag('TTS: Fehler ' + xhr.status); setDiag(dTts, dTtsVal, 'warn', 'ERR ' + xhr.status); if (onDone) onDone(); } }; xhr.onerror = function() { isSpeaking = false; updateWrapClass(); logDiag('TTS: Netzwerkfehler'); setDiag(dTts, dTtsVal, 'err', 'OFFLINE'); if (onDone) onDone(); }; xhr.send(JSON.stringify(bodyObj)); } /* === TTS WITH PROVIDER VOICE === */ function speakAsProvider(text, provider, onDone) { var savedIdx = activeVoiceIdx; if (provider && providerVoiceMap[provider] !== undefined) { activeVoiceIdx = providerVoiceMap[provider]; logDiag('TTS: ' + provider + ' \u2192 ' + VOICES[activeVoiceIdx].label); } speak(text, function() { activeVoiceIdx = savedIdx; if (onDone) onDone(); }); } function playBlob(blob, onDone) { var audioUrl = URL.createObjectURL(blob); stopAudio(); currentAudio = new Audio(audioUrl); isSpeaking = true; updateWrapClass(); currentAudio.onended = function() { isSpeaking = false; updateWrapClass(); URL.revokeObjectURL(audioUrl); currentAudio = null; if (onDone) onDone(); }; currentAudio.onerror = function() { isSpeaking = false; updateWrapClass(); logDiag('Audio: Wiedergabefehler'); URL.revokeObjectURL(audioUrl); currentAudio = null; if (onDone) onDone(); }; currentAudio.play().catch(function() { logDiag('Audio: Autoplay blockiert'); isSpeaking = false; updateWrapClass(); if (onDone) onDone(); }); } function stopAudio() { if (currentAudio) { currentAudio.pause(); currentAudio.currentTime = 0; currentAudio = null; } isSpeaking = false; updateWrapClass(); } /* === RECORDING === */ function startRecording(onComplete) { if (isRecording) return; if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { setRecStatus('Mikrofon nicht verfuegbar'); return; } audioChunks = []; recSeconds = 0; recTime.textContent = '0s'; var constraints = new Object(); constraints.audio = true; constraints.video = false; navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { mediaRecorder = new MediaRecorder(stream); isRecording = true; recBtn.className = 'recording'; recBtn.textContent = '\u25A0 Stopp'; setRecStatus('aufnahme...'); mediaRecorder.ondataavailable = function(e) { if (e.data.size > 0) { audioChunks.push(e.data); } }; mediaRecorder.onstop = function() { isRecording = false; recBtn.className = ''; recBtn.textContent = '\u25CF Aufnehmen'; clearInterval(recTimer); recTimer = null; var tracks = stream.getTracks(); for (var t = 0; t < tracks.length; t++) { tracks[t].stop(); } if (audioChunks.length > 0) { var bt = new Object(); bt.type = 'audio/webm'; var blob = new Blob(audioChunks, bt); if (onComplete) onComplete(blob); } }; mediaRecorder.start(); recTimer = setInterval(function() { recSeconds++; recTime.textContent = recSeconds + 's'; if (recSeconds >= 30) { stopRecording(); } }, 1000); }).catch(function() { setRecStatus('Mikrofon verweigert'); }); } function stopRecording() { if (mediaRecorder && isRecording) { mediaRecorder.stop(); } if (recTimer) { clearInterval(recTimer); recTimer = null; } } /* === STT === */ function transcribeAudio(blob, callback) { var formData = new FormData(); formData.append('file', blob, 'recording.webm'); formData.append('model_id', 'scribe_v2'); formData.append('language_code', 'de'); var xhr = new XMLHttpRequest(); xhr.open('POST', '
https://api.elevenlabs.io/v1/speech-to-text', true); xhr.setRequestHeader('xi-api-key', ttsApiKey); xhr.onload = function() { if (xhr.status === 200) { try { var r = JSON.parse(xhr.responseText); callback(r.text || ''); } catch(e) { callback(''); } } else { setRecStatus('STT-Fehler ' + xhr.status); callback(''); } }; xhr.onerror = function() { setRecStatus('STT nicht erreichbar'); callback(''); }; xhr.send(formData); } /* === STS (disabled) === */ function transformVoice(blob) { setRecStatus('transformiere...'); var formData = new FormData(); formData.append('audio', blob, 'recording.webm'); formData.append('model_id', 'eleven_english_sts_v2'); var url = '
https://api.elevenlabs.io/v1/speech-to-speech/' + encodeURIComponent(getActiveVoiceId()) + '?output_format=mp3_44100_128'; var xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.responseType = 'blob'; xhr.setRequestHeader('xi-api-key', ttsApiKey); xhr.onload = function() { if (xhr.status === 200) { playBlob(xhr.response, function() { setRecStatus('bereit'); }); } else { setRecStatus('STS-Fehler ' + xhr.status); } }; xhr.onerror = function() { setRecStatus('STS nicht erreichbar'); }; xhr.send(formData); } /* ============================================================ CALL MODE ============================================================ */ function callStartListening() { if (!callActive || callPaused) return; if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { addCallStatus('\u274C Mikrofon nicht verfuegbar', 'end'); return; } setCallPhaseUI('listening'); audioChunks = []; addCallStatus('\u{1F3A4} Hoere zu... sprich jetzt', 'listening'); var constraints = new Object(); constraints.audio = true; constraints.video = false; navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { callStream = stream; callAudioCtx = new (window.AudioContext || window.webkitAudioContext)(); var source = callAudioCtx.createMediaStreamSource(stream); callAnalyser = callAudioCtx.createAnalyser(); callAnalyser.fftSize = 512; source.connect(callAnalyser); mediaRecorder = new MediaRecorder(stream); isRecording = true; mediaRecorder.ondataavailable = function(e) { if (e.data.size > 0) { audioChunks.push(e.data); } }; mediaRecorder.onstop = function() { isRecording = false; }; mediaRecorder.start(250); var silenceStart = null, hasSpoken = false; function checkVolume() { if (!callActive || callPhase !== 'listening' || callPaused) return; var dataArray = new Uint8Array(callAnalyser.frequencyBinCount); callAnalyser.getByteFrequencyData(dataArray); var sum = 0; for (var vi = 0; vi < dataArray.length; vi++) { sum += dataArray[vi]; } var avg = sum / dataArray.length; if (avg > SILENCE_THRESHOLD) { hasSpoken = true; silenceStart = null; } else if (hasSpoken) { if (!silenceStart) { silenceStart = Date.now(); } else if (Date.now() - silenceStart > SILENCE_DURATION) { callStopListening(); return; } } if (callActive && callPhase === 'listening' && !callPaused) { setTimeout(checkVolume, 100); } } setTimeout(checkVolume, 500); }).catch(function() { addCallStatus('\u274C Mikrofon verweigert', 'end'); callEndAll(); }); } function callStopListening() { if (mediaRecorder && isRecording) { mediaRecorder.stop(); } if (callStream) { var tracks = callStream.getTracks(); for (var t = 0; t < tracks.length; t++) { tracks[t].stop(); } callStream = null; } if (callAudioCtx) { callAudioCtx.close(); callAudioCtx = null; } setTimeout(function() { if (audioChunks.length === 0) { if (callActive && !callPaused) { callStartListening(); } return; } setCallPhaseUI('processing'); addCallStatus('\u{1F504} Verarbeite deine Sprache...', 'processing'); var bt = new Object(); bt.type = 'audio/webm'; var blob = new Blob(audioChunks, bt); transcribeAudio(blob, function(text) { if (!text || !callActive) { if (callActive && !callPaused) { callStartListening(); } return; } var chatInput = document.getElementById('chatInput'); var chatSend = document.getElementById('chatSend'); if (chatInput && chatSend) { chatInput.value = text; setCallPhaseUI('waiting'); addCallStatus('\u{23F3} Helikon denkt nach...', 'processing'); chatSend.click(); } }); }, 300); } function callReleaseMic() { if (mediaRecorder && isRecording) { try { mediaRecorder.stop(); } catch(e) {} } if (callStream) { var tracks = callStream.getTracks(); for (var t = 0; t < tracks.length; t++) { tracks[t].stop(); } callStream = null; } if (callAudioCtx) { try { callAudioCtx.close(); } catch(e) {} callAudioCtx = null; } } function callPause() { callPaused = true; callReleaseMic(); setCallPhaseUI('paused'); addCallStatus('\u{23F8} Anruf pausiert \u2014 Mikrofon aus', 'paused'); pauseBtnLabel.textContent = 'Weiter'; pauseIcon.innerHTML = '▶'; logDiag('CALL: Pausiert'); updateButtons(); } function callResume() { callPaused = false; pauseBtnLabel.textContent = 'Pause'; pauseIcon.innerHTML = '❚❚'; updateButtons(); addCallStatus('\u{1F3A4} Anruf fortgesetzt', 'listening'); logDiag('CALL: Fortgesetzt'); setTimeout(function() { if (callActive && !callPaused && !isSpeaking) { callStartListening(); } }, 300); } function callEndAll() { callActive = false; callPaused = false; callPhase = 'idle'; window.helikonCallMode = false; callReleaseMic(); stopAudio(); stopCallTimer(); updateButtons(); setCallPhaseUI('idle'); callTimerEl.textContent = '0:00'; pauseBtnLabel.textContent = 'Pause'; pauseIcon.innerHTML = '❚❚'; logDiag('CALL: Beendet (' + formatTime(callSeconds) + ')'); } /* === PLAY BUTTONS === */ function getBuddyText(el) { var clone = el.cloneNode(true); var btns = clone.querySelectorAll('.tts-play-btn'); for (var r = 0; r < btns.length; r++) { btns[r].parentNode.removeChild(btns[r]); } return clone.textContent || clone.innerText || ''; } function addPlayButton(msgEl) { var playBtn = document.createElement('span'); playBtn.className = 'tts-play-btn'; playBtn.textContent = '\u25B6 vorlesen'; playBtn.title = 'Nachricht vorlesen'; playBtn.onclick = function() { if (isSpeaking && playBtn.className.indexOf('playing') !== -1) { stopAudio(); playBtn.textContent = '\u25B6 vorlesen'; playBtn.className = 'tts-play-btn'; return; } stopAudio(); var allBtns = document.querySelectorAll('.tts-play-btn.playing'); for (var b = 0; b < allBtns.length; b++) { allBtns[b].textContent = '\u25B6 vorlesen'; allBtns[b].className = 'tts-play-btn'; } playBtn.textContent = '\u25A0 stopp'; playBtn.className = 'tts-play-btn playing'; var prov = msgEl.getAttribute('data-provider') || ''; speakAsProvider(getBuddyText(msgEl), prov, function() { playBtn.textContent = '\u25B6 vorlesen'; playBtn.className = 'tts-play-btn'; }); }; msgEl.appendChild(playBtn); } function isBuddyMessage(node) { if (!node.className) return false; var cls = node.className; return (cls.indexOf('chatMsg') !== -1 && cls.indexOf('buddy') !== -1 && cls.indexOf('typing') === -1); } /* === CHAT OBSERVER === */ function watchChat() { var chatLog = document.getElementById('chatLog'); if (!chatLog) { setTimeout(watchChat, 500); return; } var existing = chatLog.querySelectorAll('.chatMsg.buddy'); for (var e = 0; e < existing.length; e++) { if (!existing[e].querySelector('.tts-play-btn')) { addPlayButton(existing[e]); } } if (typeof MutationObserver !== 'undefined') { var observer = new MutationObserver(function(mutations) { for (var m = 0; m < mutations.length; m++) { var added = mutations[m].addedNodes; for (var n = 0; n < added.length; n++) { var node = added[n]; if (node.nodeType !== 1) continue; if (isBuddyMessage(node)) { (function(el) { setTimeout(function() { if (!el.querySelector('.tts-play-btn')) { addPlayButton(el); } var msgText = getBuddyText(el).replace(/^\s+|\s+$/g, ''); var msgProvider = el.getAttribute('data-provider') || ''; if (callActive && (callPhase === 'waiting' || callPhase === 'processing')) { if (msgText) { setCallPhaseUI('speaking'); speakAsProvider(msgText, msgProvider, function() { if (callActive && !callPaused) { addCallStatus('\u{1F3A4} Dein Turn...', 'listening'); setTimeout(function() { if (callActive && !callPaused) { callStartListening(); } }, 400); } else if (callActive && callPaused) { addCallStatus('\u{23F8} Pausiert \u2014 dr\u00FCcke Weiter', 'paused'); setCallPhaseUI('paused'); } }); } else { if (callActive && !callPaused) { callStartListening(); } } return; } if (ttsActive && ttsApiKey && getActiveVoiceId()) { if (msgText) { speakAsProvider(msgText, msgProvider); } } }, 200); })(node); } } } }); var obsConfig = new Object(); obsConfig.childList = true; obsConfig.subtree = false; observer.observe(chatLog, obsConfig); } } /* === TOGGLE HANDLERS === */ ttsBtn.onclick = function() { if (!ttsApiKey || !getActiveVoiceId()) { logDiag('TTS: API-Key/Voice fehlt!'); return; } if (callActive) return; ttsActive = !ttsActive; updateButtons(); logDiag('TTS: ' + (ttsActive ? 'aktiviert' : 'deaktiviert')); }; stsBtn.onclick = function() { logDiag('STS: deaktiviert'); }; sttBtn.onclick = function() { if (!ttsApiKey) { logDiag('STT: API-Key fehlt!'); return; } if (callActive) return; sttActive = !sttActive; updateButtons(); setRecStatus(sttActive ? 'bereit' : ''); logDiag('STT: ' + (sttActive ? 'aktiviert' : 'deaktiviert')); }; searchBtn.onclick = function() { searchActive = !searchActive; window.helikonSearchEnabled = searchActive; updateButtons(); logDiag('Wiki-Suche: ' + (searchActive ? 'an' : 'aus')); }; callHeroBtn.onclick = function() { if (!ttsApiKey || !getActiveVoiceId()) { logDiag('CALL: API-Key/Voice fehlt!'); return; } if (callActive) { callEndAll(); addCallStatus('\u260E Anruf beendet (' + formatTime(callSeconds) + ')', 'end'); addChatMsg('[Anruf beendet \u2014 ' + formatTime(callSeconds) + ']', 'system'); return; } callActive = true; callPaused = false; ttsActive = false; sttActive = false; stsActive = false; window.helikonCallMode = true; updateButtons(); startCallTimer(); addChatMsg('[Anruf gestartet \u2014 sprich jetzt...]', 'system'); addCallStatus('\u260E Verbunden! Sprich jetzt...', 'listening'); logDiag('CALL: Gestartet'); callStartListening(); }; callPauseBtn.onclick = function() { if (!callActive) return; if (callPaused) { callResume(); } else { callPause(); } }; recBtn.onclick = function() { if (isRecording) { stopRecording(); return; } if (sttActive) { startRecording(function(blob) { setRecStatus('transkribiere...'); transcribeAudio(blob, function(text) { if (text) { var ci = document.getElementById('chatInput'); if (ci) { ci.value = text; ci.focus(); } addChatMsg('[STT] ' + text, 'system'); setRecStatus(text.substring(0, 40) + (text.length > 40 ? '...' : '')); } else { setRecStatus('kein Text erkannt'); } }); }); } }; /* ============================================================ BOOT SEQUENCE ============================================================ */ logDiag('Lade Page Properties...'); fetchAllProperties(function(props) { ttsApiKey = props['buddy.ttsapikey'] || ''; voiceId = props['buddy.voice1.id'] || props['buddy.ttsvoiceid'] || ''; modelId = props['buddy.ttsmodel'] || 'eleven_multilingual_v2'; VOICES[0].id = voiceId; VOICES[0].label = props['buddy.voice1.name'] || 'Stimme 1'; VOICES[1].id = props['buddy.voice2.id'] || 'rKiu7lQ4c5P3az3745s3'; VOICES[1].label = props['buddy.voice2.name'] || 'Stimme 2'; voiceOpt1.textContent = VOICE_ICON + VOICES[0].label; voiceOpt2.textContent = VOICE_ICON + VOICES[1].label; /* Provider-to-Voice mapping */ var pv1 = parseInt(props['buddy.voice.local'] || '2', 10); var pv2 = parseInt(props['buddy.voice.claude'] || '1', 10); providerVoiceMap['local'] = (pv1 >= 1 && pv1 <= 2) ? pv1 - 1 : 1; providerVoiceMap['claude'] = (pv2 >= 1 && pv2 <= 2) ? pv2 - 1 : 0; logDiag('VOICE MAP: local\u2192' + VOICES[providerVoiceMap['local']].label + ', claude\u2192' + VOICES[providerVoiceMap['claude']].label); /* SYS */ setDiag(dSys, dSysVal, 'ok', 'ONLINE'); logDiag('SYS: Template geladen'); /* API — show key identifier */ setTimeout(function() { if (ttsApiKey) { var keyId = ttsApiKey.substring(0,4) + '..' + ttsApiKey.substring(ttsApiKey.length - 4); setDiag(dApi, dApiVal, 'ok', keyId); logDiag('API: Key ' + keyId + ' geladen'); } else { setDiag(dApi, dApiVal, 'err', 'FEHLT'); logDiag('API: KEIN KEY! buddy.ttsapikey setzen'); } }, 200); /* VOICE */ setTimeout(function() { if (voiceId) { var vId = voiceId.substring(0,6) + '..'; setDiag(dVoice, dVoiceVal, 'ok', VOICES[0].label.toUpperCase()); logDiag('VOICE: ' + VOICES[0].label + ' (' + vId + ')'); } else { setDiag(dVoice, dVoiceVal, 'err', 'FEHLT'); logDiag('VOICE: KEINE ID! buddy.voice1.id setzen'); } }, 400); /* PROVIDER */ setTimeout(function() { setDiag(dProvider, dProviderVal, 'ok', 'ELEVENLABS'); logDiag('PROVIDER: ElevenLabs API v1'); }, 500); /* MODEL */ setTimeout(function() { var shortModel = modelId.replace('eleven_', '').replace('multilingual_', 'multi_'); setDiag(dModel, dModelVal, 'ok', shortModel); logDiag('MODEL: ' + modelId); }, 600); /* CREDITS — initially unknown, validated on first TTS call */ setTimeout(function() { if (ttsApiKey && voiceId) { setDiag(dCredits, dCreditsVal, 'warn', 'UNGETESTET'); logDiag('CREDITS: Wird bei erstem TTS-Call geprueft'); } else { setDiag(dCredits, dCreditsVal, 'err', 'NICHT PRUEFBAR'); logDiag('CREDITS: Kann nicht pruefen ohne Key/Voice'); } }, 800); /* FINAL */ setTimeout(function() { if (ttsApiKey && voiceId) { logDiag('\u2705 System bereit. Alle Systeme operational.'); } else { logDiag('\u26A0 System teilweise bereit. Konfiguration pruefen.'); } }, 1000); if (typeof console !== 'undefined') { console.log('=== HELIKON VOICE v6.4 ==='); console.log('Key: ' + (ttsApiKey ? ttsApiKey.substring(0,8) + '...' : 'FEHLT')); console.log('Voice 1: ' + VOICES[0].label + ' (' + (VOICES[0].id ? VOICES[0].id.substring(0,8) + '...' : 'FEHLT') + ')'); console.log('Voice 2: ' + VOICES[1].label + ' (' + VOICES[1].id.substring(0,8) + '...)'); console.log('Model: ' + modelId); console.log('Voice Map: local=' + VOICES[providerVoiceMap['local']].label + ', claude=' + VOICES[providerVoiceMap['claude']].label); } window.helikonSearchEnabled = searchActive; window.helikonCallMode = false; window.helikonSpeak = function(text, provider, onDone) { speakAsProvider(text, provider, onDone); }; window.helikonProviderVoice = providerVoiceMap; updateButtons(); selectVoice(0); watchChat(); }); })();
NoElements {}