/* @component-map * App — Main container, tab navigation [app.jsx] * BreathingSession — Core breathing exercise with animated circle [components/BreathingSession.jsx] * PatternSelector — Breathing pattern selection cards [components/PatternSelector.jsx] * SessionHistory — Past session log and stats [components/SessionHistory.jsx] * Settings — User preferences (sound, duration) [components/Settings.jsx] * @end-component-map */ // DEPLOY_CONFIG: {"cron": [{"name": "daily_breathing_reminder", "schedule": "0 9 * * *", "action": "event", "config": {"event_type": "breathing.reminder"}}]} import { useMemo, useState } from 'react'; import { BreathingSession } from './components/BreathingSession.jsx'; import { PatternSelector } from './components/PatternSelector.jsx'; import { SessionHistory } from './components/SessionHistory.jsx'; import { Settings } from './components/Settings.jsx'; function App() { const [tab, setTab] = useState('breathe'); const [selectedPattern, setSelectedPattern] = useState(null); const [isSessionActive, setIsSessionActive] = useState(false); const [recommendedSession, setRecommendedSession] = useState(null); const [shareMessage, setShareMessage] = useState(''); const [shareError, setShareError] = useState(''); const [shareCopied, setShareCopied] = useState(false); const buildChallengeShare = (pattern, session = null) => { if (!pattern) return null; const name = pattern.name || 'Breathing Challenge'; const title = `Join my breathing challenge: ${name}`; const duration = session?.duration || pattern.duration || pattern.totalDuration || 'a few minutes'; const cycles = session?.cycles || pattern.cycles || 'multiple'; const benefit = pattern.benefit || pattern.tag || 'calm and focus'; const phaseSummary = Array.isArray(pattern.phases) ? pattern.phases .map((phase) => `${phase.name || phase.label || phase.type || 'phase'} ${phase.duration || phase.seconds || ''}`.trim()) .filter(Boolean) .join(' • ') : ''; const challengeText = [ title, `Duration: ${duration}`, `Cycles: ${cycles}`, `Benefit: ${benefit}`, phaseSummary ? `Phases: ${phaseSummary}` : null, `Open the app and tap the ${name} pattern to start breathing with me.`, ] .filter(Boolean) .join('\n'); const link = `${window.location.origin}${window.location.pathname}?challenge=${encodeURIComponent( name )}&duration=${encodeURIComponent(duration)}&cycles=${encodeURIComponent(cycles)}`; return { title, text: challengeText, link }; }; const handleSelectPattern = (pattern) => { setSelectedPattern(pattern); setIsSessionActive(true); setRecommendedSession(null); setShareError(''); setShareMessage(''); setShareCopied(false); setTab('breathe'); }; const handleRecommendedSession = (session) => { if (!session || !session.pattern) return; setSelectedPattern(session.pattern); setRecommendedSession(session); setIsSessionActive(true); setShareError(''); setShareMessage(''); setShareCopied(false); setTab('breathe'); }; const handleEndSession = () => { setIsSessionActive(false); }; const handleBackToPatterns = () => { setSelectedPattern(null); setIsSessionActive(false); setRecommendedSession(null); setShareError(''); setShareMessage(''); setShareCopied(false); }; const handleShareChallenge = async (pattern = selectedPattern, session = recommendedSession) => { try { const shareData = buildChallengeShare(pattern, session); if (!shareData) { setShareError('Select a breathing pattern first to create a challenge link.'); setShareMessage(''); setShareCopied(false); return; } setShareError(''); setShareMessage(''); setShareCopied(false); if (navigator.share) { await navigator.share({ title: shareData.title, text: shareData.text, url: shareData.link, }); setShareMessage('Challenge share sheet opened.'); return; } const sharePayload = `${shareData.text}\n\nLink: ${shareData.link}`; if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(sharePayload); setShareCopied(true); setShareMessage('Challenge copied to clipboard.'); } else { setShareMessage(sharePayload); } } catch (error) { setShareError('Could not share right now. Try copying the challenge link instead.'); setShareCopied(false); } }; const currentShareData = useMemo(() => { if (!selectedPattern) return null; return buildChallengeShare(selectedPattern, recommendedSession); }, [selectedPattern, recommendedSession]); const tabs = [ { id: 'breathe', label: '🫁 Breathe', icon: '🫁' }, { id: 'patterns', label: '✦ Patterns', icon: '✦' }, { id: 'history', label: '📊 History', icon: '📊' }, { id: 'settings', label: '⚙ Settings', icon: '⚙' }, ]; return (