frontend mvp
This commit is contained in:
+28
-52
@@ -41,16 +41,16 @@ export default function Home() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 text-white">
|
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 text-white">
|
||||||
{/* Header */}
|
<div className="flex flex-col mx-auto min-h-[30vh] items-center justify-center">
|
||||||
<div className="container mx-auto px-6 py-8">
|
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
<h1 className="text-5xl font-bold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent mb-4">
|
<h1 className="text-7xl font-bold gradient-text mb-4">
|
||||||
🎤 Voice Assistant
|
🎤 Voice Assistant
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Main Interface */}
|
{/* Main Interface */}
|
||||||
<div className="flex flex-col lg:flex-row items-center justify-center gap-12 max-w-6xl mx-auto">
|
<div className="flex flex-col lg:flex-row items-center justify-center min-h-[50vh] gap-12 max-w-6xl mx-auto my-auto">
|
||||||
|
|
||||||
{/* Recording Section */}
|
{/* Recording Section */}
|
||||||
<div className="flex flex-col items-center space-y-6">
|
<div className="flex flex-col items-center space-y-6">
|
||||||
@@ -70,23 +70,21 @@ export default function Home() {
|
|||||||
onTouchStart={handleStartRecording}
|
onTouchStart={handleStartRecording}
|
||||||
onTouchEnd={handleStopRecording}
|
onTouchEnd={handleStopRecording}
|
||||||
disabled={isProcessing}
|
disabled={isProcessing}
|
||||||
className={`record-button w-40 h-40 rounded-full border-none text-white text-lg font-semibold
|
className={`record-button w-60 h-60 rounded-full border-none text-white text-lg font-semibold cursor-pointer
|
||||||
${recording
|
${recording
|
||||||
? 'bg-gradient-to-r from-red-500 to-red-600 recording-pulse shadow-red-500/50'
|
? 'record-button--recording'
|
||||||
: isProcessing
|
: isProcessing
|
||||||
? 'bg-gradient-to-r from-yellow-500 to-orange-500 shadow-yellow-500/50'
|
? 'record-button--processing'
|
||||||
: 'bg-gradient-to-r from-blue-500 to-purple-600 shadow-blue-500/50'
|
: 'record-button--idle'
|
||||||
}
|
}
|
||||||
${isProcessing ? 'cursor-not-allowed opacity-75' : 'cursor-pointer hover:shadow-xl'}
|
|
||||||
disabled:cursor-not-allowed disabled:opacity-75
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<div className="text-3xl mb-2">
|
<div className="text-4xl mb-2">
|
||||||
{recording ? '🎙️' : isProcessing ? '⏳' : '🎤'}
|
{recording ? '🎙️' : isProcessing ? '⏳' : '🎤'}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">
|
<div className="text-2xl">
|
||||||
{recording ? 'Recording...' : isProcessing ? 'Processing...' : 'Hold / Click'}
|
{recording ? 'Recording...' : isProcessing ? 'Processing...' : 'Hold to Record'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
@@ -94,30 +92,30 @@ export default function Home() {
|
|||||||
|
|
||||||
{/* Status indicators */}
|
{/* Status indicators */}
|
||||||
<div className="flex space-x-4">
|
<div className="flex space-x-4">
|
||||||
<div className={`flex items-center space-x-2 px-4 py-2 rounded-full transition-all duration-300 ${
|
<div className={`status-indicator ${
|
||||||
recording ? 'bg-red-500/20 text-red-300' : 'bg-gray-700/50 text-gray-400'
|
recording ? 'status-indicator--recording' : ''
|
||||||
}`}>
|
}`}>
|
||||||
<div className={`w-2 h-2 rounded-full ${recording ? 'bg-red-400 animate-pulse' : 'bg-gray-500'}`}></div>
|
<div className={`pulse-dot ${recording ? 'pulse-dot--error' : 'pulse-dot--inactive'}`}></div>
|
||||||
<span className="text-sm font-medium">Recording</span>
|
<span className="text-xl font-medium">Recording</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`flex items-center space-x-2 px-4 py-2 rounded-full transition-all duration-300 ${
|
<div className={`status-indicator ${
|
||||||
isProcessing ? 'bg-yellow-500/20 text-yellow-300' : 'bg-gray-700/50 text-gray-400'
|
isProcessing ? 'status-indicator--processing' : ''
|
||||||
}`}>
|
}`}>
|
||||||
<div className={`w-2 h-2 rounded-full ${isProcessing ? 'bg-yellow-400 animate-pulse' : 'bg-gray-500'}`}></div>
|
<div className={`pulse-dot ${isProcessing ? 'pulse-dot--warning' : 'pulse-dot--inactive'}`}></div>
|
||||||
<span className="text-sm font-medium">Processing</span>
|
<span className="text-xl font-medium">Processing</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Audio Playback Section */}
|
{/* Audio Playback Section */}
|
||||||
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 shadow-2xl border border-white/20 min-w-[400px]">
|
<div className="glass-panel p-8 min-w-2/3">
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<h3 className="text-2xl font-semibold text-white mb-2">🔊 Audio Response</h3>
|
<h3 className="text-2xl font-semibold text-white mb-2">🔊 Audio Response</h3>
|
||||||
<p className="text-gray-300">Your AI assistant's response will play here</p>
|
<p className="text-theme-muted">Your AI assistant's response will play here</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="audio-visualizer bg-gradient-to-r from-blue-500/10 to-purple-500/10 rounded-xl p-6">
|
<div className="audio-visualizer p-6">
|
||||||
<audio
|
<audio
|
||||||
ref={audioRef}
|
ref={audioRef}
|
||||||
controls
|
controls
|
||||||
@@ -128,13 +126,13 @@ export default function Home() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{playing && (
|
{playing && (
|
||||||
<div className="flex items-center justify-center space-x-3 text-green-400">
|
<div className="flex items-center justify-center space-x-3 text-theme-success">
|
||||||
<div className="flex space-x-1">
|
<div className="flex space-x-1">
|
||||||
<div className="w-1 h-6 bg-green-400 rounded animate-bounce"></div>
|
<div className="w-1 h-6 bg-theme-success rounded animate-bounce"></div>
|
||||||
<div className="w-1 h-8 bg-green-400 rounded animate-bounce" style={{animationDelay: '0.1s'}}></div>
|
<div className="w-1 h-8 bg-theme-success rounded animate-bounce" style={{animationDelay: '0.1s'}}></div>
|
||||||
<div className="w-1 h-6 bg-green-400 rounded animate-bounce" style={{animationDelay: '0.2s'}}></div>
|
<div className="w-1 h-6 bg-theme-success rounded animate-bounce" style={{animationDelay: '0.2s'}}></div>
|
||||||
<div className="w-1 h-10 bg-green-400 rounded animate-bounce" style={{animationDelay: '0.3s'}}></div>
|
<div className="w-1 h-10 bg-theme-success rounded animate-bounce" style={{animationDelay: '0.3s'}}></div>
|
||||||
<div className="w-1 h-6 bg-green-400 rounded animate-bounce" style={{animationDelay: '0.4s'}}></div>
|
<div className="w-1 h-6 bg-theme-success rounded animate-bounce" style={{animationDelay: '0.4s'}}></div>
|
||||||
</div>
|
</div>
|
||||||
<span className="font-medium">Playing response...</span>
|
<span className="font-medium">Playing response...</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,28 +140,6 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Instructions */}
|
|
||||||
<div className="mt-16 text-center">
|
|
||||||
<div className="bg-white/5 backdrop-blur-sm rounded-xl p-6 max-w-3xl mx-auto border border-white/10">
|
|
||||||
<h4 className="text-lg font-semibold text-white mb-4">💡 How to Use</h4>
|
|
||||||
<div className="grid md:grid-cols-3 gap-4 text-sm text-gray-300">
|
|
||||||
<div className="flex flex-col items-center space-y-2">
|
|
||||||
<div className="w-12 h-12 bg-blue-500/20 rounded-full flex items-center justify-center text-2xl">1️⃣</div>
|
|
||||||
<p><strong>Press & Hold</strong><br/>Hold the button down while speaking</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center space-y-2">
|
|
||||||
<div className="w-12 h-12 bg-purple-500/20 rounded-full flex items-center justify-center text-2xl">2️⃣</div>
|
|
||||||
<p><strong>Or Click Toggle</strong><br/>Click once to start, click again to stop</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center space-y-2">
|
|
||||||
<div className="w-12 h-12 bg-pink-500/20 rounded-full flex items-center justify-center text-2xl">3️⃣</div>
|
|
||||||
<p><strong>Listen</strong><br/>Your AI assistant will respond with audio</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+219
-12
@@ -1,39 +1,246 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Primary Theme Colors */
|
||||||
|
--color-primary: #3b82f6; /* Blue-500 */
|
||||||
|
--color-primary-dark: #2563eb; /* Blue-600 */
|
||||||
|
--color-primary-light: #60a5fa; /* Blue-400 */
|
||||||
|
|
||||||
|
--color-secondary: #8b5cf6; /* Purple-500 */
|
||||||
|
--color-secondary-dark: #7c3aed; /* Purple-600 */
|
||||||
|
--color-secondary-light: #a78bfa; /* Purple-400 */
|
||||||
|
|
||||||
|
--color-accent: #ec4899; /* Pink-500 */
|
||||||
|
--color-accent-light: #f472b6; /* Pink-400 */
|
||||||
|
|
||||||
|
/* Status Colors */
|
||||||
|
--color-success: #10b981; /* Emerald-500 */
|
||||||
|
--color-success-light: #34d399; /* Emerald-400 */
|
||||||
|
--color-warning: #f59e0b; /* Yellow-500 */
|
||||||
|
--color-warning-light: #fbbf24; /* Yellow-400 */
|
||||||
|
--color-error: #ef4444; /* Red-500 */
|
||||||
|
--color-error-light: #f87171; /* Red-400 */
|
||||||
|
|
||||||
|
/* Background Colors */
|
||||||
|
--color-bg-primary: #0f172a; /* Slate-900 */
|
||||||
|
--color-bg-secondary: #1e293b; /* Slate-800 */
|
||||||
|
--color-bg-tertiary: #334155; /* Slate-700 */
|
||||||
|
--color-bg-overlay: rgba(255, 255, 255, 0.1);
|
||||||
|
--color-bg-overlay-dark: rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
|
/* Text Colors */
|
||||||
|
--color-text-primary: #ffffff;
|
||||||
|
--color-text-secondary: #cbd5e1; /* Slate-300 */
|
||||||
|
--color-text-muted: #94a3b8; /* Slate-400 */
|
||||||
|
--color-text-inverse: #1e293b; /* Slate-800 */
|
||||||
|
|
||||||
|
/* Border Colors */
|
||||||
|
--color-border-light: rgba(255, 255, 255, 0.2);
|
||||||
|
--color-border-medium: rgba(255, 255, 255, 0.3);
|
||||||
|
--color-border-dark: rgba(255, 255, 255, 0.1);
|
||||||
|
|
||||||
|
/* Gradient Definitions */
|
||||||
|
--gradient-primary: linear-gradient(135deg, var(--color-primary), var(--color-secondary));
|
||||||
|
--gradient-secondary: linear-gradient(135deg, var(--color-secondary), var(--color-accent));
|
||||||
|
--gradient-background: linear-gradient(135deg, var(--color-bg-primary) 0%, var(--color-secondary-dark) 50%, var(--color-bg-primary) 100%);
|
||||||
|
--gradient-text: linear-gradient(135deg, var(--color-primary-light), var(--color-secondary-light), var(--color-accent-light));
|
||||||
|
|
||||||
|
/* Shadow Definitions */
|
||||||
|
--shadow-primary: 0 25px 50px -12px rgba(59, 130, 246, 0.25);
|
||||||
|
--shadow-secondary: 0 25px 50px -12px rgba(139, 92, 246, 0.25);
|
||||||
|
--shadow-error: 0 25px 50px -12px rgba(239, 68, 68, 0.25);
|
||||||
|
--shadow-success: 0 25px 50px -12px rgba(16, 185, 129, 0.25);
|
||||||
|
--shadow-warning: 0 25px 50px -12px rgba(245, 158, 11, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
body {
|
body {
|
||||||
@apply bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900;
|
background: var(--gradient-background);
|
||||||
@apply min-h-screen;
|
@apply min-h-screen;
|
||||||
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
|
/* Recording Button Styles */
|
||||||
.record-button {
|
.record-button {
|
||||||
@apply relative overflow-hidden;
|
position: relative;
|
||||||
@apply transition-all duration-300 ease-in-out;
|
overflow: hidden;
|
||||||
@apply transform hover:scale-105 active:scale-95;
|
transition: all 0.3s ease-in-out;
|
||||||
@apply shadow-2xl;
|
transform-origin: center;
|
||||||
|
box-shadow: var(--shadow-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-button:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-button::before {
|
.record-button::before {
|
||||||
content: '';
|
content: '';
|
||||||
@apply absolute inset-0 bg-gradient-to-r from-transparent via-white to-transparent;
|
position: absolute;
|
||||||
@apply opacity-0 translate-x-[-100%];
|
top: 0;
|
||||||
@apply transition-all duration-700;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
transition: all 0.7s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-button:hover::before {
|
.record-button:hover::before {
|
||||||
@apply opacity-20 translate-x-[100%];
|
opacity: 1;
|
||||||
|
transform: translateX(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Recording Button States */
|
||||||
|
.record-button--idle {
|
||||||
|
background: var(--gradient-primary);
|
||||||
|
box-shadow: var(--shadow-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-button--recording {
|
||||||
|
background: linear-gradient(135deg, var(--color-error), var(--color-error-light));
|
||||||
|
box-shadow: var(--shadow-error);
|
||||||
|
animation: recording-pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-button--processing {
|
||||||
|
background: linear-gradient(135deg, var(--color-warning), var(--color-warning-light));
|
||||||
|
box-shadow: var(--shadow-warning);
|
||||||
|
opacity: 0.8;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Audio Visualizer Styles */
|
||||||
.audio-visualizer {
|
.audio-visualizer {
|
||||||
@apply relative overflow-hidden rounded-xl;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: var(--color-bg-overlay);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--color-border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-visualizer::after {
|
.audio-visualizer::after {
|
||||||
content: '';
|
content: '';
|
||||||
@apply absolute inset-0 bg-gradient-to-r from-blue-500/20 to-purple-500/20;
|
position: absolute;
|
||||||
@apply animate-pulse;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
rgba(59, 130, 246, 0.1),
|
||||||
|
rgba(139, 92, 246, 0.1)
|
||||||
|
);
|
||||||
|
animation: pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status Indicators */
|
||||||
|
.status-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border: 1px solid var(--color-border-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator--active {
|
||||||
|
background: var(--color-bg-overlay);
|
||||||
|
border-color: var(--color-border-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator--recording {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
color: var(--color-error-light);
|
||||||
|
border-color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator--processing {
|
||||||
|
background: rgba(245, 158, 11, 0.1);
|
||||||
|
color: var(--color-warning-light);
|
||||||
|
border-color: var(--color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utility Classes */
|
||||||
|
.glass-panel {
|
||||||
|
background: var(--color-bg-overlay);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid var(--color-border-light);
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-text {
|
||||||
|
background: var(--gradient-text);
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse-dot {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse-dot--error {
|
||||||
|
background-color: var(--color-error-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse-dot--warning {
|
||||||
|
background-color: var(--color-warning-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse-dot--inactive {
|
||||||
|
background-color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes recording-pulse {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: var(--shadow-error);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: var(--shadow-error), 0 0 0 10px rgba(239, 68, 68, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Theme Color Utility Classes */
|
||||||
|
.bg-theme-primary { background-color: var(--color-primary); }
|
||||||
|
.bg-theme-secondary { background-color: var(--color-secondary); }
|
||||||
|
.bg-theme-accent { background-color: var(--color-accent); }
|
||||||
|
.bg-theme-success { background-color: var(--color-success); }
|
||||||
|
.bg-theme-warning { background-color: var(--color-warning); }
|
||||||
|
.bg-theme-error { background-color: var(--color-error); }
|
||||||
|
|
||||||
|
.text-theme-primary { color: var(--color-primary); }
|
||||||
|
.text-theme-secondary { color: var(--color-secondary); }
|
||||||
|
.text-theme-accent { color: var(--color-accent); }
|
||||||
|
.text-theme-success { color: var(--color-success); }
|
||||||
|
.text-theme-warning { color: var(--color-warning); }
|
||||||
|
.text-theme-error { color: var(--color-error); }
|
||||||
|
.text-theme-muted { color: var(--color-text-muted); }
|
||||||
|
|
||||||
|
.border-theme-light { border-color: var(--color-border-light); }
|
||||||
|
.border-theme-medium { border-color: var(--color-border-medium); }
|
||||||
|
.border-theme-primary { border-color: var(--color-primary); }
|
||||||
|
.border-theme-error { border-color: var(--color-error); }
|
||||||
|
.border-theme-warning { border-color: var(--color-warning); }
|
||||||
|
.border-theme-success { border-color: var(--color-success); }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user