import React, { useState, useEffect, useRef, useCallback } from 'react'; import { MousePointer2, Fish, Music, Home, Settings, Battery, Wifi } from 'lucide-react'; // --- 유틸리티 및 오디오 --- const playTone = (frequency) => { try { const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); // 고양이 울음소리와 비슷하게 부드러운 사인파 사용 oscillator.type = 'sine'; oscillator.frequency.value = frequency; oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); oscillator.start(); // 소리가 서서히 줄어들도록 설정 (야옹~ 하는 느낌) gainNode.gain.setValueAtTime(1, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.5); setTimeout(() => { oscillator.stop(); audioCtx.close(); }, 500); } catch (e) { console.log("Audio not supported or interaction required first."); } }; // --- 앱 컴포넌트들 --- // 1. 레이저 포인터 앱 const LaserApp = () => { const [position, setPosition] = useState({ x: 50, y: 50 }); const [score, setScore] = useState(0); const containerRef = useRef(null); const moveLaser = useCallback(() => { setPosition({ x: Math.random() * 90 + 5, // 5% ~ 95% y: Math.random() * 90 + 5, }); }, []); useEffect(() => { const interval = setInterval(moveLaser, 1200); // 1.2초마다 이동 return () => clearInterval(interval); }, [moveLaser]); const handleCatch = (e) => { e.stopPropagation(); // 부모 터치 이벤트 방지 setScore(s => s + 1); moveLaser(); // 잡으면 바로 도망감 playTone(800); // 삑! 소리 }; return (
잡은 횟수: {score}
{/* 레이저 점 */}
); }; // 2. 물고기 연못 앱 const FishApp = () => { const [fishes, setFishes] = useState([]); const [ripples, setRipples] = useState([]); useEffect(() => { // 초기 물고기 생성 const initialFishes = Array.from({ length: 5 }).map((_, i) => ({ id: i, x: Math.random() * 100, y: Math.random() * 100, speed: Math.random() * 0.5 + 0.2, direction: Math.random() > 0.5 ? 1 : -1, // 1: 오른쪽, -1: 왼쪽 size: Math.random() * 20 + 40 // 40px ~ 60px })); setFishes(initialFishes); let animationFrame; const animate = () => { setFishes(prevFishes => prevFishes.map(fish => { let newX = fish.x + (fish.speed * fish.direction); let newDirection = fish.direction; // 화면 밖으로 나가면 방향 전환 if (newX > 110) newDirection = -1; if (newX < -10) newDirection = 1; return { ...fish, x: newX, direction: newDirection }; })); animationFrame = requestAnimationFrame(animate); }; animationFrame = requestAnimationFrame(animate); return () => cancelAnimationFrame(animationFrame); }, []); const handleTap = (e) => { const rect = e.currentTarget.getBoundingClientRect(); const x = (e.clientX || (e.touches && e.touches[0].clientX)) - rect.left; const y = (e.clientY || (e.touches && e.touches[0].clientY)) - rect.top; const newRipple = { id: Date.now(), x, y }; setRipples(prev => [...prev, newRipple]); // 물장구 소리 (낮은 주파수) playTone(200); // 1초 후 파문 제거 setTimeout(() => { setRipples(prev => prev.filter(r => r.id !== newRipple.id)); }, 1000); }; return (
{/* 파문 효과 */} {ripples.map(ripple => (
))} {/* 물고기들 */} {fishes.map(fish => (
🐟
))}
); }; // 3. 야옹 피아노 앱 const PianoApp = () => { const keys = [ { color: 'bg-rose-400', freq: 261.63, label: '도' }, // C4 { color: 'bg-orange-400', freq: 293.66, label: '레' }, // D4 { color: 'bg-yellow-400', freq: 329.63, label: '미' }, // E4 { color: 'bg-green-400', freq: 349.23, label: '파' }, // F4 { color: 'bg-blue-400', freq: 392.00, label: '솔' }, // G4 ]; const [activeKey, setActiveKey] = useState(null); const handlePress = (index, freq) => { setActiveKey(index); playTone(freq); setTimeout(() => setActiveKey(null), 200); }; return (
{keys.map((key, index) => (
handlePress(index, key.freq)} onTouchStart={() => handlePress(index, key.freq)} > {activeKey === index && ( 야옹! )} {key.label}
))}
); }; // --- 메인 운영체제 컴포넌트 --- export default function App() { const [isLocked, setIsLocked] = useState(true); const [currentApp, setCurrentApp] = useState(null); // null = 바탕화면 const [time, setTime] = useState(new Date()); // 시계 업데이트 useEffect(() => { const timer = setInterval(() => setTime(new Date()), 1000); return () => clearInterval(timer); }, []); const apps = [ { id: 'laser', name: '레이저 잡기', icon: , bgColor: 'bg-slate-800' }, { id: 'fish', name: '연못 낚시', icon: , bgColor: 'bg-blue-900' }, { id: 'piano', name: '야옹 피아노', icon: , bgColor: 'bg-purple-900' }, ]; // 잠금화면 if (isLocked) { return (

Nyang OS

{time.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' })}

발바닥을 터치하여 잠금해제

); } return (
{/* 상단 상태 표시줄 */}
Nyang OS
{time.toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' })}
{/* 메인 콘텐츠 영역 */}
{/* 바탕화면 */} {!currentApp && (
{apps.map(app => ( ))}
)} {/* 앱 실행 화면 */} {currentApp === 'laser' && } {currentApp === 'fish' && } {currentApp === 'piano' && }
{/* 하단 네비게이션 바 (집사용 - 고양이가 잘 안누르게 작게 배치) */}
); }