mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
Add live voice transcript, keyboard shortcuts, agent topology map
#3 Live Voice Transcript: Real-time subtitle bar at bottom of screen shows what JARVIS hears as you speak. Interim results appear word-by-word via SpeechRecognition.onresult interim events; bar fades 3.2s after final result. #4 Keyboard Shortcuts: Global keydown handler (skips input fields): F5=refresh all, Esc=close modals/overlays, M=mute mic toggle, Space=focus chat input, 1/2/3/4=switch HOME/ALERTS/NEWS/AGENTS tabs. Shortcut hints added to Ctrl+K palette footer. #5 Agent Topology Map: TOPOLOGY button in AGENTS tab switches from card view to animated ring-based canvas showing all agents by type (Proxmox=green inner ring, HA=yellow mid ring, Linux/Windows=blue outer ring). Live particles flow hub→agents; offline nodes shown in red. Reads from rendered agent cards. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1216,6 +1216,11 @@ function initVoice() {
|
||||
|
||||
recognition.onresult = (e) => {
|
||||
if (isSpeaking) return;
|
||||
let interimText = '';
|
||||
for (let ri = e.resultIndex; ri < e.results.length; ri++) {
|
||||
if (!e.results[ri].isFinal) interimText += e.results[ri][0].transcript;
|
||||
}
|
||||
if (interimText && voiceMode && !voiceMuted) _showInterimTranscript(interimText);
|
||||
const transcript = (e.results[0][0].transcript || '').trim();
|
||||
if (!transcript) return;
|
||||
const lc = transcript.toLowerCase();
|
||||
@@ -1269,9 +1274,23 @@ function initVoice() {
|
||||
};
|
||||
}
|
||||
|
||||
let _transcriptTimer = null;
|
||||
function _showTranscript(text) {
|
||||
const el = document.getElementById('textInput');
|
||||
if (el) { el.placeholder = '▶ ' + text.substring(0, 60); setTimeout(() => { el.placeholder = 'Enter command or speak to JARVIS...'; }, 3000); }
|
||||
const bar = document.getElementById('voiceTranscriptBar');
|
||||
if (!bar) return;
|
||||
bar.textContent = text;
|
||||
bar.classList.add('vt-active');
|
||||
if (_transcriptTimer) clearTimeout(_transcriptTimer);
|
||||
_transcriptTimer = setTimeout(() => { bar.classList.remove('vt-active'); bar.textContent = ''; }, 3200);
|
||||
}
|
||||
function _showInterimTranscript(text) {
|
||||
const bar = document.getElementById('voiceTranscriptBar');
|
||||
if (!bar || !text) return;
|
||||
bar.textContent = text + '…';
|
||||
bar.classList.add('vt-active');
|
||||
if (_transcriptTimer) clearTimeout(_transcriptTimer);
|
||||
}
|
||||
|
||||
function enterVoiceMode(source) {
|
||||
@@ -1527,3 +1546,29 @@ async function triggerMorningBriefing() {
|
||||
speak(msg);
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// ── KEYBOARD SHORTCUTS ───────────────────────────────────────────────────────────────
|
||||
document.addEventListener('keydown', function(e) {
|
||||
const tag = (document.activeElement?.tagName || '').toLowerCase();
|
||||
const inInput = tag === 'input' || tag === 'textarea' || document.activeElement?.isContentEditable;
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') return; // handled by palette
|
||||
if (e.key === 'Escape') {
|
||||
['sitesModal','agentModal','searchModal'].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el && (el.style.display === 'flex' || el.style.display === 'block')) el.style.display = 'none';
|
||||
});
|
||||
if (document.getElementById('netMapOverlay')?.classList.contains('nm-open')) closeNetMap();
|
||||
return;
|
||||
}
|
||||
if (inInput) return;
|
||||
if (e.key === 'F5') { e.preventDefault(); refreshAll(); return; }
|
||||
if (e.key === 'm' || e.key === 'M') { toggleVoice(); return; }
|
||||
if (e.key === ' ') { e.preventDefault(); document.getElementById('textInput')?.focus(); return; }
|
||||
const tabMap = {'1':'ha','2':'alerts','3':'news','4':'agents'};
|
||||
if (tabMap[e.key]) {
|
||||
document.querySelectorAll('.tab').forEach(t => {
|
||||
const oc = t.getAttribute('onclick') || '';
|
||||
if (oc.includes("'" + tabMap[e.key] + "'")) t.click();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user