import { connectToHost } from "@pollen-robotics/reachy-mini-sdk/host/embed"; import { ConversationManager } from "./conversation.ts"; import { AnimationEngine } from "./animations.ts"; import { SpeechManager } from "./speech.ts"; import { UIManager } from "./ui.ts"; import type { ReachyMini } from "@pollen-robotics/reachy-mini-sdk"; // Simple safe return to home pose async function safelyReturnToPose(reachy: ReachyMini) { try { reachy.setMotorMode("enabled"); reachy.setHeadRpyDeg(0, 0, 0); reachy.setAntennasDeg(0, 0); reachy.setBodyYawDeg(0); } catch (e) { console.error("Failed to return to safe pose:", e); } } export async function main() { const handle = await connectToHost(); const reachy = handle.reachy as ReachyMini; document.documentElement.setAttribute("data-theme", handle.theme); handle.onThemeChange((t) => document.documentElement.setAttribute("data-theme", t)); const animations = new AnimationEngine(reachy); const speech = new SpeechManager(); const conversation = new ConversationManager(); const ui = new UIManager({ onSendMessage: (text) => conversation.sendMessage(text), onStartListening: () => speech.startListening(), onStopListening: () => speech.stopListening(), onConnectOllama: (url, model) => conversation.connectOllama(url, model), }); // Handle incoming speech-to-text results speech.onResult((text) => { conversation.sendMessage(text); }); // When a new conversation message arrives conversation.onMessage(async (msg) => { ui.addMessage(msg.role, msg.content); if (msg.role === "assistant") { // Animate while speaking await animations.animateSpeaking(msg.content); // Speak the response await speech.speak(msg.content); animations.stopSpeaking(); } }); // Conversation lull → dance let lullTimer: ReturnType | null = null; const LULL_TIMEOUT_MS = 8000; // 8 seconds of inactivity function resetLullTimer() { if (lullTimer) clearTimeout(lullTimer); lullTimer = setTimeout(async () => { ui.showStatus("dancing"); await animations.doRandomDance(); ui.showStatus("idle"); }, LULL_TIMEOUT_MS); } conversation.onActivity(resetLullTimer); ui.onActivity(resetLullTimer); // Safe teardown handle.onLeave(() => { if (lullTimer) clearTimeout(lullTimer); speech.destroy(); safelyReturnToPose(reachy); }); // Initial greeting setTimeout(() => { conversation.sendSystem("You are Reachy, a friendly and expressive robot. Keep responses short and engaging."); }, 1000); } void main().catch((err) => { console.error("[embed] Boot failed:", err); window.parent.postMessage( { source: "reachy-mini", type: "embed:error", version: 1, message: String(err), fatal: true }, window.location.origin, ); });