codecraft-haven / rosalinda.html
Abmacode12's picture
je veux que quan je parle au micro , la discussion s'écris dans écrivez votre message comme sa je vois ce que je disaussi
52c54ae verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rosalinda — Espace Codage</title>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body class="bg-gray-900 text-gray-100">
<div class="app-container h-screen flex">
<!-- Sidebar -->
<aside class="w-80 bg-gray-800 border-r border-gray-700 flex flex-col">
<div class="p-4 border-b border-gray-700 flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-blue-500 shadow-lg shadow-blue-500/30"></div>
<h1 class="font-bold">Espace Codage</h1>
</div>
<nav class="flex-1 overflow-y-auto p-2">
<h2 class="text-xs uppercase tracking-wider text-gray-500 px-3 py-2">Générateurs IA</h2>
<a href="baleine.html" class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-gray-750/50 border border-transparent hover:border-gray-700 text-gray-300 mb-1">
<div class="p-2 rounded-lg bg-gray-750 border border-gray-700">
<i data-feather="image" class="w-4 h-4"></i>
</div>
<span class="truncate">La Baleine IA</span>
</a>
<a href="rosalinda.html" class="flex items-center gap-3 px-3 py-2 rounded-lg bg-blue-900/30 border border-blue-800/50 text-blue-200 mb-1">
<div class="p-2 rounded-lg bg-blue-900/50 border border-blue-800/50">
<i data-feather="message-circle" class="w-4 h-4"></i>
</div>
<span class="truncate">Rosalinda IA</span>
</a>
</nav>
</aside>
<!-- Main Content -->
<main class="flex-1 flex flex-col bg-gray-850/50">
<div class="p-3 border-b border-gray-700 flex gap-2 bg-gray-850">
<span class="text-xs px-3 py-1 rounded-full bg-gray-800 border border-gray-700 text-gray-400">Rosalinda IA</span>
<span class="text-xs px-3 py-1 rounded-full bg-gray-800 border border-gray-700 text-gray-400">Chat</span>
</div>
<div class="flex-1 overflow-y-auto p-4 flex flex-col gap-3" id="msgs"></div>
<div class="p-3 border-t border-gray-700 bg-gray-850">
<div class="flex items-center gap-2 p-2 rounded-xl bg-gray-800 border border-gray-700">
<button id="micBtn" class="p-2 rounded-lg hover:bg-gray-700 text-gray-400 hover:text-gray-200">
<i data-feather="mic" class="w-5 h-5"></i>
</button>
<input id="inp" placeholder="Écris à Rosalinda…"
class="flex-1 bg-transparent px-3 py-2 focus:outline-none text-sm">
<button id="sendBtn" class="p-2 rounded-lg bg-blue-600 hover:bg-blue-500 text-white">
<i data-feather="send" class="w-5 h-5"></i>
</button>
<button id="speakBtn" title="Lire la dernière réponse"
class="p-2 rounded-lg hover:bg-gray-700 text-gray-400 hover:text-gray-200">
<i data-feather="volume-2" class="w-5 h-5"></i>
</button>
</div>
<div class="text-xs text-gray-500 mt-2 flex justify-between">
<span id="status">Micro: prêt</span>
<span>Rosalinda IA v1.0</span>
</div>
</div>
</main>
<div class="small text-gray-400 text-sm mt-4 leading-relaxed">
✅ Micro & voix = API du navigateur (Chrome/Edge).<br>
⚠️ Cette version répond avec une "IA locale simple" (règles). Étape suivante: brancher un vrai modèle IA (local ou API).
</div>
</div>
<script>
feather.replace();
const msgs = document.getElementById("msgs");
const inp = document.getElementById("inp");
const sendBtn = document.getElementById("sendBtn");
const micBtn = document.getElementById("micBtn");
const speakBtn = document.getElementById("speakBtn");
const statusEl = document.getElementById("status");
let lastAIText = "";
function addMsg(text, who){
const div = document.createElement("div");
div.className = `msg ${who === "me" ?
"ml-auto bg-blue-900/10 border-blue-800/50" :
"bg-gray-700/50 border-gray-700"} max-w-[80%] p-3 rounded-xl border`;
div.textContent = text;
msgs.appendChild(div);
msgs.scrollTop = msgs.scrollHeight;
}
function rosalindaBrain(userText){
const t = userText.toLowerCase();
if (t.includes("bonjour") || t.includes("salut")) return "Bonjour 😄 Je suis Rosalinda. Dis-moi ce que tu veux créer : site, thème, image, vidéo, plugin…";
if (t.includes("theme") || t.includes("thème")) return "Ok ✅ Dis-moi : (1) style (moderne, luxe, minimal, flashy), (2) couleurs, (3) 3 colonnes ou non, (4) Shopify/WooCommerce/autre.";
if (t.includes("image")) return "Je peux préparer une demande d'image. Dis-moi : sujet + style + format (1:1, 16:9, 9:16) + texte à afficher.";
if (t.includes("vidéo") || t.includes("video")) return "Je peux préparer une demande vidéo. Dis-moi : durée, style (réaliste/3D), texte à l'écran, musique oui/non.";
if (t.includes("micro")) return "Pour le micro : clique 🎤, autorise le micro dans ton navigateur, puis parle. Je transcris et je réponds.";
return "Compris ✅ Donne-moi plus de détails (objectif + plateforme + style), et je te génère une réponse claire.";
}
function speak(text){
if (!("speechSynthesis" in window)) {
alert("TTS non supporté sur ce navigateur.");
return;
}
const u = new SpeechSynthesisUtterance(text);
u.lang = "fr-FR";
window.speechSynthesis.cancel();
window.speechSynthesis.speak(u);
}
function handleSend(text){
const v = (text ?? inp.value).trim();
if (!v) return;
addMsg(v, "me");
inp.value = "";
const reply = rosalindaBrain(v);
lastAIText = reply;
addMsg(reply, "ai");
speak(reply);
}
sendBtn.onclick = () => handleSend();
inp.addEventListener("keydown", (e) => {
if (e.key === "Enter") handleSend();
});
speakBtn.onclick = () => lastAIText && speak(lastAIText);
let rec = null;
let listening = false;
function setupSTT(){
try {
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SR) {
statusEl.textContent = "Micro: non supporté";
return null;
}
const r = new SR();
r.lang = "fr-FR";
r.interimResults = true;
r.continuous = false;
return r;
} catch (e) {
statusEl.textContent = "Micro: erreur";
return null;
}
}
rec = setupSTT();
if (!rec) {
statusEl.textContent = "Micro: non supporté";
micBtn.disabled = true;
micBtn.classList.add("opacity-50");
return;
}
// Check microphone permission status
async function checkMicrophonePermission() {
try {
const permissionStatus = await navigator.permissions.query({ name: 'microphone' });
permissionStatus.onchange = () => {
if (permissionStatus.state === 'granted') {
statusEl.textContent = "Micro: prêt";
} else {
statusEl.textContent = "Micro: permission requise";
}
};
return permissionStatus.state;
} catch (e) {
console.error("Permission API not supported", e);
return 'prompt';
}
}
// Initialize microphone status
checkMicrophonePermission().then(state => {
if (state === 'granted') {
statusEl.textContent = "Micro: prêt";
} else {
statusEl.textContent = "Micro: cliquez pour autoriser";
}
});
micBtn.onclick = async () => {
if (!rec) return;
// Check if we need to request permission
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// Immediately close the stream as we just needed permission
stream.getTracks().forEach(track => track.stop());
} catch (err) {
console.error("Microphone access error:", err);
statusEl.textContent = "Micro: accès refusé";
return;
}
if (listening) {
rec.stop();
return;
}
listening = true;
statusEl.textContent = "Micro: écoute…";
micBtn.classList.add("listening");
let finalText = "";
let timeoutId;
rec.onresult = (e) => {
clearTimeout(timeoutId);
let interimTranscript = '';
let finalTranscript = '';
for (let i = e.resultIndex; i < e.results.length; i++) {
const transcript = e.results[i][0].transcript;
if (e.results[i].isFinal) {
finalTranscript += transcript + ' ';
} else {
interimTranscript += transcript;
}
}
// Show interim results in real-time
inp.value = interimTranscript || finalTranscript;
if (finalTranscript) {
finalText += finalTranscript;
}
// Reset timeout on new results
timeoutId = setTimeout(() => {
if (listening) rec.stop();
}, 2000);
};
rec.onerror = (e) => {
console.error("Speech recognition error", e);
stopListening();
statusEl.textContent = "Micro: erreur - " + e.error;
};
rec.onend = () => {
stopListening();
if (inp.value.trim()) {
handleSend(inp.value);
inp.value = "";
}
};
function stopListening() {
listening = false;
statusEl.textContent = "Micro: prêt";
micBtn.classList.remove("listening");
}
try {
rec.start();
// Auto-stop after 10 seconds if no speech detected
setTimeout(() => {
if (listening) rec.stop();
}, 10000);
} catch (e) {
console.error("Speech recognition error:", e);
stopListening();
statusEl.textContent = "Micro: erreur de démarrage";
}
};
addMsg("Bonjour 👋 Je suis Rosalinda. Clique 🎤 pour parler ou écris-moi.", "ai");
</script>
</body>
</html>