feat: website analysis pipeline, voice agent, configurator improvements
- Site analysis: cheerio HTML parsing, inline tech stack detection (~20 CMS/framework/analytics signatures), Google PageSpeed API integration
- Gemini Live voice agent: WebSocket-based real-time voice mode with live transcript, selection chips, and mid-conversation website analysis
- Type/Talk mode toggle with silent capability detection
- Stepped progress animation during brief generation (4 animated steps)
- URL + thoughts fields in Step 2, phone + contact preference in Step 3
- AI prompt improvements: dedicated website analysis section, 30-min call, concrete benefits, industry depth
- Email redesign: branded templates with logo, proper markdown rendering for both client and admin
- French locale support for AI-generated briefs
- Smaller checkmark, compact booking CTA, expanded brief area
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 13:41:35 +01:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
import { useTranslations } from 'next-intl';
|
|
|
|
|
import { Keyboard, Mic } from 'lucide-react';
|
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
|
|
|
|
|
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
interface ModeToggleProps {
|
|
|
|
|
mode: 'type' | 'talk';
|
|
|
|
|
onChange: (mode: 'type' | 'talk') => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Component ───────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export default function ModeToggle({ mode, onChange }: ModeToggleProps) {
|
|
|
|
|
const t = useTranslations('configurator');
|
|
|
|
|
const [voiceSupported, setVoiceSupported] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
async function check() {
|
|
|
|
|
if (typeof WebSocket === 'undefined') return;
|
|
|
|
|
if (!navigator.mediaDevices?.getUserMedia) return;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-28 14:02:06 +01:00
|
|
|
const res = await fetch('/api/gemini-token');
|
feat: website analysis pipeline, voice agent, configurator improvements
- Site analysis: cheerio HTML parsing, inline tech stack detection (~20 CMS/framework/analytics signatures), Google PageSpeed API integration
- Gemini Live voice agent: WebSocket-based real-time voice mode with live transcript, selection chips, and mid-conversation website analysis
- Type/Talk mode toggle with silent capability detection
- Stepped progress animation during brief generation (4 animated steps)
- URL + thoughts fields in Step 2, phone + contact preference in Step 3
- AI prompt improvements: dedicated website analysis section, 30-min call, concrete benefits, industry depth
- Email redesign: branded templates with logo, proper markdown rendering for both client and admin
- French locale support for AI-generated briefs
- Smaller checkmark, compact booking CTA, expanded brief area
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 13:41:35 +01:00
|
|
|
const data = (await res.json()) as { success: boolean };
|
|
|
|
|
if (data.success) setVoiceSupported(true);
|
|
|
|
|
} catch {
|
|
|
|
|
// silent — toggle stays hidden
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void check();
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
if (!voiceSupported) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex items-center gap-1 rounded-xl bg-surface-low p-1 border border-outline-variant/30">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => onChange('type')}
|
|
|
|
|
className={cn(
|
|
|
|
|
'flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all duration-200',
|
|
|
|
|
mode === 'type'
|
|
|
|
|
? 'bg-white text-on-surface shadow-card'
|
|
|
|
|
: 'text-outline hover:text-on-surface',
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<Keyboard size={13} />
|
|
|
|
|
{t('mode.type')}
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => onChange('talk')}
|
|
|
|
|
className={cn(
|
|
|
|
|
'flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all duration-200',
|
|
|
|
|
mode === 'talk'
|
|
|
|
|
? 'bg-white text-on-surface shadow-card'
|
|
|
|
|
: 'text-outline hover:text-on-surface',
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<Mic size={13} />
|
|
|
|
|
{t('mode.talk')}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|