/** * GMIIE Voice Desk — Listen | Ask | Voice across xxxiii.io hub pages. * Depends on grok-voice.js for TTS (institutional HD voice synthesis /api/tts + speechSynthesis fallback). */ (function (global) { const DEFAULTS = { askEndpoint: '/api/gmiie/ask', ttsEndpoint: '/api/tts', contentSelector: 'main', voiceId: 'daniel', }; let cfg = { ...DEFAULTS }; let panelEl = null; let fabEl = null; let narratives = null; let recognition = null; let listening = false; function injectStyles() { if (document.getElementById('gvd-styles')) return; const s = document.createElement('style'); s.id = 'gvd-styles'; s.textContent = ` .gvd-fab{position:fixed;bottom:20px;right:20px;z-index:9998;font-family:var(--mono,'JetBrains Mono',Consolas,monospace);font-size:9px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;background:var(--band,#1a1a1a);color:var(--band-ink,rgba(255,255,255,.9));border:2px solid var(--gold,#b8953a);padding:12px 16px;cursor:pointer;box-shadow:0 8px 32px rgba(0,0,0,.35);transition:transform .2s,border-color .2s;} .gvd-fab:hover{transform:translateY(-2px);border-color:var(--gold2,#d4af55);} .gvd-panel{position:fixed;bottom:72px;right:20px;width:min(380px,calc(100vw - 32px));max-height:min(520px,calc(100vh - 100px));z-index:9999;background:var(--surface,var(--paper2,#f3f0e8));border:2px solid var(--rule,#d4c9a8);box-shadow:0 16px 48px rgba(0,0,0,.25);display:none;flex-direction:column;font-family:var(--sans,'Helvetica Neue',sans-serif);color:var(--ink,#1a1a1a);} .gvd-panel.open{display:flex;} .gvd-hd{padding:12px 14px;border-bottom:1px solid var(--rule,#d4c9a8);display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--paper2,var(--surface,#ece8dc));} .gvd-title{font-family:var(--mono,'JetBrains Mono',monospace);font-size:9px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--gold,#b8953a);} .gvd-close{background:none;border:1px solid var(--rule,#d4c9a8);cursor:pointer;font-size:14px;line-height:1;padding:2px 8px;color:var(--ink3,#777);} .gvd-tabs{display:flex;border-bottom:1px solid var(--rule,#d4c9a8);} .gvd-tab{flex:1;font-family:var(--mono,'JetBrains Mono',monospace);font-size:8px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:10px 6px;background:transparent;border:none;border-bottom:2px solid transparent;cursor:pointer;color:var(--ink3,#777);} .gvd-tab.active{color:var(--ink,#1a1a1a);border-bottom-color:var(--gold,#b8953a);} .gvd-body{padding:14px;overflow-y:auto;flex:1;font-size:13px;line-height:1.6;} .gvd-pane{display:none;}.gvd-pane.active{display:block;} .gvd-btn{font-family:var(--mono,'JetBrains Mono',monospace);font-size:8px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:8px 12px;cursor:pointer;border:1px solid var(--rule,#d4c9a8);background:var(--paper,var(--paper2,#faf8f3));color:var(--ink,#1a1a1a);margin:4px 4px 4px 0;} .gvd-btn.primary{background:var(--fraud,#7a1f2e);color:#fff;border-color:var(--fraud,#7a1f2e);} .gvd-btn.on,.gvd-listen-btn.on{background:var(--gold-bg,rgba(184,149,58,.15));border-color:var(--gold,#b8953a);color:var(--gold,#b8953a);} .gvd-listen-btn,.gvd-ask-btn{font-family:var(--mono,'JetBrains Mono',monospace);font-size:7px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:4px 8px;cursor:pointer;border:1px solid var(--rule,#d4c9a8);background:var(--paper2,#f3f0e8);color:var(--ink2,#444);margin-top:6px;margin-right:6px;} .gvd-input{width:100%;min-height:72px;padding:10px;border:1px solid var(--rule,#d4c9a8);background:var(--paper,#faf8f3);font-family:var(--sans);font-size:13px;color:var(--ink,#1a1a1a);resize:vertical;} .gvd-answer{margin-top:12px;padding:10px;border-left:3px solid var(--gold,#b8953a);background:var(--gold-bg,rgba(184,149,58,.08));font-size:12px;line-height:1.65;white-space:pre-wrap;} .gvd-meta{font-family:var(--mono,'JetBrains Mono',monospace);font-size:7px;color:var(--ink3,#777);margin-top:8px;} .gvd-mic{width:48px;height:48px;border-radius:50%;border:2px solid var(--gold,#b8953a);background:var(--band,#1a1a1a);color:var(--gold,#b8953a);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;margin:12px auto;} .gvd-mic.listening{border-color:var(--red,#c0392b);color:var(--red,#c0392b);animation:gvd-pulse 1.2s infinite;} @keyframes gvd-pulse{0%,100%{opacity:1}50%{opacity:.5}} .gvd-report-actions{margin-top:12px;display:flex;flex-wrap:wrap;gap:8px;} .gvd-voice-label{font-family:var(--mono,'JetBrains Mono',monospace);font-size:7px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ink3,#777);margin-left:8px;} .gvd-narrative-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;} `; document.head.appendChild(s); } function narrativeToSpeech(n) { if (!n) return ''; const parts = []; if (n.headline) parts.push(n.headline + '.'); if (n.category) parts.push('Category: ' + n.category + '.'); if (n.summary) parts.push(n.summary); if (n.what_happened) parts.push('What happened. ' + n.what_happened); if (n.what_we_found && n.what_we_found.length) { parts.push('What we found. ' + n.what_we_found.join('. ') + '.'); } if (n.awareness && n.awareness.length) { parts.push('Awareness. ' + n.awareness.join('. ') + '.'); } if (n.gmiie_signal) parts.push('GMIIE signal. ' + n.gmiie_signal); if (n.lesson) parts.push('Lesson. ' + n.lesson); return parts.join('\n\n'); } function extractElementText(el) { if (!el) return ''; const clone = el.cloneNode(true); clone.querySelectorAll('script,style,button,.gvd-fab,.gvd-panel').forEach((n) => n.remove()); return (clone.innerText || '').replace(/\s+/g, ' ').trim(); } function pageContext() { const page = cfg.page || document.body.getAttribute('data-gmiie-page') || location.pathname.replace(/^\//, '').replace(/\.html$/, '') || 'hub'; const sel = global.getSelection?.(); const selectedText = sel && !sel.isCollapsed ? String(sel.toString()).trim() : ''; const ctx = { page, url: location.pathname }; if (selectedText) ctx.selectedText = selectedText.slice(0, 2000); if (cfg.fraudScore != null) ctx.fraudScore = cfg.fraudScore; if (cfg.fraudLevel) ctx.fraudLevel = cfg.fraudLevel; if (cfg.engine) ctx.engine = cfg.engine; return ctx; } function narrationContext(overrides) { const page = cfg.page || document.body.getAttribute('data-gmiie-page') || location.pathname.replace(/^\//, '').replace(/\.html$/, '') || 'hub'; const base = { page, url: location.pathname }; if (cfg.fraudScore != null) base.score = cfg.fraudScore; if (cfg.fraudLevel) base.level = cfg.fraudLevel; if (cfg.engine) base.engine = cfg.engine; if (cfg.typology) base.typology = cfg.typology; if (cfg.scamFamily) base.scam_family = cfg.scamFamily; return { ...base, ...(overrides || {}) }; } function speak(text, btn, speakOpts) { if (!text) return; const ctx = speakOpts?.context || narrationContext({ type: inferNarrationType(speakOpts) }); if (global.GrokVoice) { global.GrokVoice.speak(text, btn, { voice_id: cfg.voiceId, endpoint: cfg.ttsEndpoint, provider: 'auto', smart: speakOpts?.smart !== false, context: ctx, }); return; } const u = new SpeechSynthesisUtterance(String(text).slice(0, 5000)); u.rate = 0.87; global.speechSynthesis?.speak(u); } function inferNarrationType(speakOpts) { if (speakOpts?.type) return speakOpts.type; if (speakOpts?.narrativeId || speakOpts?.narrative) return 'narrative'; if (cfg.fraudScore != null || speakOpts?.report) return 'fraud_report'; if (cfg.page === 'legislative' || location.pathname.includes('legislative')) return 'legislative'; if (cfg.page === 'global-governance' || location.pathname.includes('global-governance')) return 'legislative'; if (cfg.page === 'fraud' || location.pathname.includes('fraud')) return 'fraud_report'; return 'narrative'; } async function ask(question, extraContext, answerEl, metaEl) { if (!question) return; if (answerEl) answerEl.textContent = 'Analyzing…'; if (metaEl) metaEl.textContent = ''; const context = { ...pageContext(), ...(extraContext || {}) }; try { const r = await fetch(cfg.askEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ question, context, mode: 'text' }), signal: AbortSignal.timeout(35000), }); const data = await r.json().catch(() => ({})); if (!r.ok) throw new Error(data.error || 'Ask failed ' + r.status); if (answerEl) answerEl.textContent = data.answer || 'No answer returned.'; if (metaEl) { const src = (data.sources || []).map((s) => s.ref || s.type).filter(Boolean).slice(0, 4); metaEl.textContent = (data.model || 'GMIIE') + (data.tools?.length ? ' · tools: ' + data.tools.join(', ') : '') + (src.length ? ' · sources: ' + src.join(', ') : ''); } return data; } catch (e) { if (answerEl) answerEl.textContent = 'Could not reach GMIIE agent. ' + (e.message || e); return null; } } function getNarrativeById(id) { if (!narratives || !id) return null; return (narratives.narratives || []).find((n) => n.id === id) || null; } function loadNarratives() { if (narratives) return Promise.resolve(narratives); return fetch('/data/fraud-narratives.json') .then((r) => (r.ok ? r.json() : null)) .then((d) => { narratives = d; return d; }) .catch(() => null); } function switchTab(name) { if (!panelEl) return; panelEl.querySelectorAll('.gvd-tab').forEach((t) => { t.classList.toggle('active', t.dataset.tab === name); }); panelEl.querySelectorAll('.gvd-pane').forEach((p) => { p.classList.toggle('active', p.dataset.pane === name); }); } function buildPanel() { injectStyles(); fabEl = document.createElement('button'); fabEl.type = 'button'; fabEl.className = 'gvd-fab'; fabEl.setAttribute('aria-label', 'Open GMIIE Voice Desk'); fabEl.textContent = 'GMIIE Voice Desk'; fabEl.addEventListener('click', () => { panelEl.classList.toggle('open'); }); panelEl = document.createElement('div'); panelEl.className = 'gvd-panel'; panelEl.setAttribute('role', 'dialog'); panelEl.setAttribute('aria-label', 'GMIIE Voice Desk'); panelEl.innerHTML = `
Read aloud page content, case narratives, or your text selection.
Speak your question — answers play via GMIIE Analyst Voice (institutional HD voice synthesis).