index.html•17.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voice Code Assistant - SCS-MCP</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
color: #333;
}
.header {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
padding: 1rem 2rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
display: flex;
align-items: center;
gap: 1rem;
}
.logo-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
}
.logo-text {
font-size: 1.5rem;
font-weight: 600;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.status {
display: flex;
align-items: center;
gap: 1rem;
}
.status-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: #f8f9fa;
border-radius: 20px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #dc3545;
animation: pulse 2s infinite;
}
.status-dot.connected {
background: #28a745;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.main-container {
flex: 1;
max-width: 1200px;
width: 100%;
margin: 2rem auto;
padding: 0 2rem;
display: flex;
gap: 2rem;
}
.voice-panel {
flex: 1;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.voice-controls {
text-align: center;
margin-bottom: 2rem;
}
.mic-button {
width: 120px;
height: 120px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
border: none;
color: white;
font-size: 48px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
position: relative;
overflow: hidden;
}
.mic-button:hover {
transform: scale(1.05);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
}
.mic-button.listening {
animation: listening 1.5s ease-in-out infinite;
background: linear-gradient(135deg, #f093fb, #f5576c);
}
@keyframes listening {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.mic-button.continuous {
background: linear-gradient(135deg, #fa709a, #fee140);
}
.waveform {
height: 60px;
margin: 2rem 0;
display: flex;
align-items: center;
justify-content: center;
gap: 3px;
}
.wave-bar {
width: 4px;
height: 20px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 2px;
animation: wave 1s ease-in-out infinite;
}
.wave-bar:nth-child(2) { animation-delay: 0.1s; }
.wave-bar:nth-child(3) { animation-delay: 0.2s; }
.wave-bar:nth-child(4) { animation-delay: 0.3s; }
.wave-bar:nth-child(5) { animation-delay: 0.4s; }
@keyframes wave {
0%, 100% { height: 20px; }
50% { height: 40px; }
}
.mode-toggle {
display: flex;
justify-content: center;
gap: 1rem;
margin: 1rem 0;
}
.mode-button {
padding: 0.5rem 1rem;
border: 2px solid #667eea;
background: white;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s ease;
}
.mode-button.active {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.transcript-box {
background: #f8f9fa;
border-radius: 10px;
padding: 1rem;
min-height: 100px;
margin: 1rem 0;
font-family: 'Courier New', monospace;
white-space: pre-wrap;
word-wrap: break-word;
}
.quick-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
margin: 2rem 0;
}
.action-button {
padding: 1rem;
background: white;
border: 2px solid #e9ecef;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
}
.action-button:hover {
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.2);
}
.action-icon {
font-size: 24px;
margin-bottom: 0.5rem;
}
.conversation-panel {
width: 400px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 1.5rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
.conversation-header {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 2px solid #e9ecef;
}
.conversation-history {
flex: 1;
overflow-y: auto;
max-height: 500px;
margin-bottom: 1rem;
}
.message {
margin: 1rem 0;
padding: 0.75rem;
border-radius: 10px;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message.user {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
margin-left: 2rem;
}
.message.assistant {
background: #f8f9fa;
margin-right: 2rem;
}
.message-time {
font-size: 0.75rem;
opacity: 0.7;
margin-top: 0.25rem;
}
.context-info {
background: #f8f9fa;
border-radius: 10px;
padding: 1rem;
margin-top: 1rem;
}
.context-item {
display: flex;
justify-content: space-between;
margin: 0.5rem 0;
font-size: 0.9rem;
}
.context-label {
font-weight: 600;
color: #666;
}
.context-value {
color: #333;
font-family: 'Courier New', monospace;
}
.settings-panel {
position: fixed;
top: 0;
right: -400px;
width: 400px;
height: 100vh;
background: white;
box-shadow: -5px 0 20px rgba(0,0,0,0.1);
transition: right 0.3s ease;
z-index: 1000;
padding: 2rem;
overflow-y: auto;
}
.settings-panel.open {
right: 0;
}
.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.settings-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
.setting-group {
margin: 2rem 0;
}
.setting-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}
.setting-input {
width: 100%;
padding: 0.5rem;
border: 2px solid #e9ecef;
border-radius: 5px;
}
.voice-select {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
margin-top: 0.5rem;
}
.voice-option {
padding: 0.5rem;
border: 2px solid #e9ecef;
border-radius: 5px;
cursor: pointer;
text-align: center;
transition: all 0.3s ease;
}
.voice-option.selected {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border-color: #667eea;
}
@media (max-width: 1024px) {
.main-container {
flex-direction: column;
}
.conversation-panel {
width: 100%;
}
}
</style>
</head>
<body>
<header class="header">
<div class="header-content">
<div class="logo">
<div class="logo-icon">🎙️</div>
<div class="logo-text">Voice Code Assistant</div>
</div>
<div class="status">
<div class="status-indicator">
<div class="status-dot" id="scsStatus"></div>
<span>SCS-MCP</span>
</div>
<div class="status-indicator">
<div class="status-dot" id="voiceStatus"></div>
<span>Voice</span>
</div>
<div class="status-indicator">
<div class="status-dot" id="vscodeStatus"></div>
<span>VS Code</span>
</div>
<button class="action-button" onclick="toggleSettings()" style="width: auto; padding: 0.5rem 1rem;">
⚙️ Settings
</button>
</div>
</div>
</header>
<main class="main-container">
<div class="voice-panel">
<div class="voice-controls">
<button class="mic-button" id="micButton" onclick="toggleVoice()">
🎤
</button>
<div class="waveform" id="waveform" style="display: none;">
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<div class="wave-bar"></div>
</div>
<div class="mode-toggle">
<button class="mode-button active" onclick="setMode('push')" id="pushMode">
Push to Talk
</button>
<button class="mode-button" onclick="setMode('continuous')" id="continuousMode">
Continuous
</button>
<button class="mode-button" onclick="setMode('wake')" id="wakeMode">
Wake Word
</button>
</div>
</div>
<div class="transcript-box" id="transcript">
Ready to listen... Click the microphone or say "Hey Assistant"
</div>
<div class="quick-actions">
<button class="action-button" onclick="quickAction('review')">
<div class="action-icon">📝</div>
<div>Review Code</div>
</button>
<button class="action-button" onclick="quickAction('explain')">
<div class="action-icon">💡</div>
<div>Explain This</div>
</button>
<button class="action-button" onclick="quickAction('similar')">
<div class="action-icon">🔍</div>
<div>Find Similar</div>
</button>
<button class="action-button" onclick="quickAction('test')">
<div class="action-icon">🧪</div>
<div>Generate Tests</div>
</button>
<button class="action-button" onclick="quickAction('debt')">
<div class="action-icon">🏗️</div>
<div>Tech Debt</div>
</button>
<button class="action-button" onclick="quickAction('model')">
<div class="action-icon">🤖</div>
<div>Model Info</div>
</button>
</div>
<div class="context-info">
<div class="context-item">
<span class="context-label">Current File:</span>
<span class="context-value" id="currentFile">Not connected</span>
</div>
<div class="context-item">
<span class="context-label">Language:</span>
<span class="context-value" id="currentLanguage">-</span>
</div>
<div class="context-item">
<span class="context-label">Line:</span>
<span class="context-value" id="currentLine">-</span>
</div>
<div class="context-item">
<span class="context-label">Symbol:</span>
<span class="context-value" id="currentSymbol">-</span>
</div>
</div>
</div>
<div class="conversation-panel">
<div class="conversation-header">
💬 Conversation History
</div>
<div class="conversation-history" id="conversationHistory">
<div class="message assistant">
<div>👋 Hello! I'm your AI pair programmer. I can help you review code, find patterns, generate tests, and more. Just speak naturally or click the quick action buttons.</div>
<div class="message-time">Now</div>
</div>
</div>
<button class="action-button" onclick="exportConversation()">
📥 Export Conversation
</button>
</div>
</main>
<div class="settings-panel" id="settingsPanel">
<div class="settings-header">
<h2>Settings</h2>
<button class="settings-close" onclick="toggleSettings()">✕</button>
</div>
<div class="setting-group">
<label class="setting-label">Wake Word</label>
<input type="text" class="setting-input" id="wakeWord" value="hey assistant">
</div>
<div class="setting-group">
<label class="setting-label">Voice Model</label>
<div class="voice-select">
<div class="voice-option selected" onclick="selectVoice('rachel')">Rachel</div>
<div class="voice-option" onclick="selectVoice('domi')">Domi</div>
<div class="voice-option" onclick="selectVoice('bella')">Bella</div>
<div class="voice-option" onclick="selectVoice('antoni')">Antoni</div>
<div class="voice-option" onclick="selectVoice('elli')">Elli</div>
<div class="voice-option" onclick="selectVoice('josh')">Josh</div>
</div>
</div>
<div class="setting-group">
<label class="setting-label">Response Style</label>
<select class="setting-input" id="responseStyle">
<option value="concise">Concise</option>
<option value="detailed">Detailed</option>
<option value="teaching">Teaching</option>
</select>
</div>
<div class="setting-group">
<label class="setting-label">Auto-play Responses</label>
<input type="checkbox" id="autoPlay" checked>
</div>
</div>
<script src="/app.js"></script>
</body>
</html>