import { useState, useMemo } from 'react'; import { Box, Container, Typography, TextField, InputAdornment, Accordion, AccordionSummary, AccordionDetails, Chip, Stack, IconButton, Fade, Button, } from '@mui/material'; import SearchIcon from '@mui/icons-material/Search'; import CloseIcon from '@mui/icons-material/Close'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import rehypeHighlight from 'rehype-highlight'; import 'highlight.js/styles/github.css'; import Layout from '../components/Layout'; import PageHero from '../components/PageHero'; // FAQ Data const faqSections = [ { id: 'getting-started', title: 'Getting Started', items: [ { question: 'How do I connect to Reachy Mini from Python?', answer: `To control Reachy Mini, you mainly use the ReachyMini class from the reachy_mini package: \`\`\`python from reachy_mini import ReachyMini with ReachyMini() as mini: # your code here ... \`\`\` This connects to the Reachy Mini daemon and initializes motors and sensors.`, tags: ['SDK', 'Python'], }, { question: 'Do I need to start the daemon manually?', answer: `Yes. All examples assume you have already started the Reachy Mini daemon: - Either via command line: \`reachy-mini-daemon\` - Or via Python: \`reachy_mini.daemon.app.main\``, tags: ['SDK'], }, { question: 'How long does assembly usually take?', answer: 'Most testers report 1.5–2 hours, with some up to 4 hours depending on experience.', tags: ['Hardware'], }, { question: "The dashboard at http://localhost:8000 doesn't work — what should I check?", answer: `Typical checks: 1. You are using a proper Python virtual environment (.venv) 2. You installed/updated the Reachy Mini SDK: \`pip install -U reachy-mini\` 3. The daemon is running`, tags: ['SDK'], }, ], }, { id: 'movement', title: 'Moving the Robot', items: [ { question: "How do I move Reachy Mini's head to a specific pose?", answer: `Use goto_target with a pose created by create_head_pose: \`\`\`python from reachy_mini import ReachyMini from reachy_mini.utils import create_head_pose with ReachyMini() as mini: mini.goto_target(head=create_head_pose(y=-10, mm=True)) \`\`\``, tags: ['Movement', 'SDK'], }, { question: "What's the difference between goto_target and set_target?", answer: `**goto_target:** Interpolates motion over a duration (default 0.5s). Supports different interpolation methods (linear, minjerk, ease, cartoon). Ideal for smooth, timed motions. **set_target:** Sets the target immediately, without interpolation. Suited for high-frequency control (e.g. sinusoidal trajectories, teleoperation).`, tags: ['Movement', 'SDK'], }, { question: 'How do I move head, body, and antennas at the same time?', answer: `Use goto_target with multiple named arguments: \`\`\`python mini.goto_target( head=create_head_pose(y=-10, mm=True), antennas=np.deg2rad([45, 45]), duration=2.0, body_yaw=np.deg2rad(30), ) \`\`\``, tags: ['Movement'], }, ], }, { id: 'apps', title: 'Making & Sharing Apps', items: [ { question: 'How do I Make a Reachy Mini app?', answer: `Check this tutorial : https://huggingface.co/blog/pollen-robotics/make-and-publish-your-reachy-mini-apps`, tags: ['SDK', 'Python'], }, ], }, { id: 'hardware', title: 'Hardware & Motion Limits', items: [ { question: 'What are the safety limits of the head and body?', answer: `Physical & software limits include: - Body yaw: [-180°, 180°] - Head pitch/roll: [-40°, 40°] - Head yaw: [-180°, 180°] - Difference (body_yaw - head_yaw): [-65°, 65°] If you command a pose outside these limits, Reachy Mini will automatically clamp to the nearest safe pose.`, tags: ['Hardware'], }, ], }, { id: 'sensors', title: 'Sensors & Media', items: [ { question: 'How do I grab camera frames from Reachy Mini?', answer: `Use the media object: \`\`\`python with ReachyMini() as mini: frame = mini.media.get_frame() # frame is a numpy array \`\`\``, tags: ['Vision', 'SDK'], }, { question: 'How do I access microphone audio samples?', answer: `\`\`\`python with ReachyMini() as mini: sample = mini.media.get_audio_sample() \`\`\``, tags: ['Audio'], }, ], }, { id: 'motors', title: 'Motors & Compliancy', items: [ { question: 'How do I enable, disable, or make motors compliant?', answer: `Three main methods: 1. **enable_motors**: Powers motors ON. Robot holds position. 2. **disable_motors**: Powers motors OFF. Robot is limp. 3. **make_motors_compliant**: Motors ON but soft. Good for teaching-by-demonstration.`, tags: ['Hardware', 'SDK'], }, ], }, ]; // Tag color - all primary const tagColor = { bg: 'rgba(255, 149, 0, 0.1)', color: '#FF9500', border: 'rgba(255, 149, 0, 0.25)' }; // Extract all unique tags from FAQ data const allTags = [...new Set(faqSections.flatMap((s) => s.items.flatMap((i) => i.tags)))].sort(); export default function FAQ() { const [searchQuery, setSearchQuery] = useState(''); const [selectedTag, setSelectedTag] = useState(null); // Filter FAQ items based on search and tag const filteredSections = useMemo(() => { const query = searchQuery.toLowerCase().trim(); return faqSections .map((section) => ({ ...section, items: section.items.filter((item) => { // Tag filter if (selectedTag && !item.tags.includes(selectedTag)) { return false; } // Search filter if (query) { return ( item.question.toLowerCase().includes(query) || item.answer.toLowerCase().includes(query) || item.tags.some((tag) => tag.toLowerCase().includes(query)) ); } return true; }), })) .filter((section) => section.items.length > 0); }, [searchQuery, selectedTag]); const totalResults = filteredSections.reduce((acc, section) => acc + section.items.length, 0); const totalItems = faqSections.reduce((acc, section) => acc + section.items.length, 0); const hasActiveFilter = searchQuery.trim() || selectedTag; const handleTagClick = (tag) => { setSelectedTag((prev) => (prev === tag ? null : tag)); }; const clearFilters = () => { setSearchQuery(''); setSelectedTag(null); }; return ( {/* Search + HuggingChat CTA */} {/* Search + AI Button row */} setSearchQuery(e.target.value)} InputProps={{ startAdornment: ( ), endAdornment: searchQuery && ( setSearchQuery('')} edge="end"> ), }} sx={{ '& .MuiOutlinedInput-root': { borderRadius: 3, }, }} /> {/* HuggingChat CTA Button */} {/* Tag filters */} {allTags.map((tag) => ( handleTagClick(tag)} sx={{ cursor: 'pointer', fontWeight: 600, fontSize: 12, transition: 'all 0.2s ease', backgroundColor: selectedTag === tag ? tagColor.color : tagColor.bg, color: selectedTag === tag ? 'white' : tagColor.color, border: `1px solid ${ selectedTag === tag ? tagColor.color : tagColor.border }`, '&:hover': { backgroundColor: selectedTag === tag ? tagColor.color : tagColor.bg, transform: 'translateY(-1px)', }, }} /> ))} {/* Filter status */} {totalResults} of {totalItems} question{totalItems !== 1 ? 's' : ''} {selectedTag && ( setSelectedTag(null)} sx={{ fontSize: 11, height: 24, backgroundColor: tagColor.bg, color: tagColor.color, fontWeight: 600, '& .MuiChip-deleteIcon': { color: tagColor.color, '&:hover': { color: tagColor.color, }, }, }} /> )} Clear all {/* FAQ Content */} {filteredSections.length === 0 ? ( 🔍 No results found Try adjusting your search or filters ) : ( filteredSections.map((section) => ( {/* Section header */} {section.title} {/* FAQ items */} {section.items.map((item, index) => ( }> {item.question} {item.tags.map((tag) => ( { e.stopPropagation(); handleTagClick(tag); }} sx={{ fontSize: 11, height: 22, cursor: 'pointer', backgroundColor: tagColor.bg, color: tagColor.color, border: `1px solid ${ selectedTag === tag ? tagColor.color : 'transparent' }`, fontWeight: 600, transition: 'all 0.15s ease', '&:hover': { backgroundColor: tagColor.bg, transform: 'scale(1.05)', }, }} /> ))} ( {children} ), pre: ({ children }) => {children}, code: ({ inline, className, children, ...props }) => inline ? ( {children} ) : ( {children} ), a: ({ children, ...props }) => ( {children} ), ul: ({ children }) => {children}, li: ({ children }) => {children}, }} > {item.answer} ))} )) )} {/* Still have questions? - Discord CTA */} Need more help? Join our Discord to chat with 500+ makers and the Pollen team. Join Discord → ); }