import { useState, useEffect, useCallback, useMemo, useRef } from 'react' import { LayoutDashboard, Sparkles, Terminal, Settings } from 'lucide-react' import api from '../api/client' import { getTheme, applyTheme } from '../themes' import { useI18n } from '../i18n' import Dashboard from './Dashboard' import Studio from './Studio' import Shell from './Shell' import Config from './Config' import OnboardingWizard from './OnboardingWizard' export default function App() { const [activeTab, setActiveTab] = useState('dash') const [info, setInfo] = useState({}) const [clock, setClock] = useState(new Date()) const [isSudo, setIsSudo] = useState(false) const [dashRefreshKey, setDashRefreshKey] = useState(0) const dashRefreshRef = useRef(null) const [updates, setUpdates] = useState([]) const [tools, setTools] = useState([]) const [config, setConfig] = useState(null) const [showOnboarding, setShowOnboarding] = useState(false) const { t, layout } = useI18n() const TABS = useMemo(() => [ { id: 'dash', label: t('tabs.dashboard'), icon: }, { id: 'studio', label: t('tabs.studio'), icon: }, { id: 'shell', label: t('tabs.shell'), icon: }, { id: 'config', label: t('tabs.config'), icon: }, ], [t]) useEffect(() => { api.getInfo().then(d => { setInfo(d); setIsSudo(!!d.sudo) }).catch(() => {}) api.getTools().then(d => setTools(d.tools || [])).catch(() => {}) api.getUpdates().then(d => setUpdates(d.updates || [])).catch(() => {}) api.getConfig().then(d => { setConfig(d) const theme = d.profile?.preferences?.theme || 'cyberpunk-red' applyTheme(getTheme(theme)) const hasProfile = d.profile?.name || d.profile?.pseudo if (!hasProfile) setShowOnboarding(true) }).catch(() => { applyTheme(getTheme('cyberpunk-red')) setShowOnboarding(true) }) }, []) useEffect(() => { const id = setInterval(() => setClock(new Date()), 1000) return () => clearInterval(id) }, []) useEffect(() => { const onKey = (e) => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return if (!e.ctrlKey && !e.metaKey) return const map = { Digit1: 'dash', Digit2: 'studio', Digit3: 'shell', Digit4: 'config', } if (map[e.code]) { e.preventDefault() setActiveTab(map[e.code]) return } if (e.ctrlKey && e.code === 'KeyR') { e.preventDefault() if (dashRefreshRef.current) dashRefreshRef.current() } } window.addEventListener('keydown', onKey) return () => window.removeEventListener('keydown', onKey) }, []) const switchTab = useCallback((tabId) => setActiveTab(tabId), []) useEffect(() => { const handler = () => setActiveTab('shell') window.addEventListener('navigate-to-shell', handler) return () => window.removeEventListener('navigate-to-shell', handler) }, []) const hasUpdates = updates.some(u => u.needsUpdate) const installed = tools.filter(tool => tool.installed).length const WINDOW_SHORTCUTS = useMemo(() => ({ dash: [], studio: [ { keys: layout.keys.enter, desc: t('statusbar.sendMessage') }, { keys: `${layout.keys.shift}+${layout.keys.enter}`, desc: t('statusbar.newLine') }, ], shell: [ { keys: `${layout.keys.ctrl}+${layout.keys.shift}+C`, desc: t('statusbar.copy') }, { keys: `${layout.keys.ctrl}+${layout.keys.shift}+V`, desc: t('statusbar.paste') }, { keys: layout.keys.enter, desc: t('statusbar.runCommand') }, { keys: `${layout.keys.up}/${layout.keys.down}`, desc: t('statusbar.commandHistory') }, ], config: [], }), [layout, t]) return (
MUYUE v{info.version || '...'}
0 ? 'ok' : 'off'}`} title={t('header.toolsInstalled', { count: installed })} />
{clock.toLocaleTimeString(layout.locale, { hour: '2-digit', minute: '2-digit' })}
{isSudo && ⚡ ROOT} {activeTab === 'dash' && ( {layout.keys.ctrl}+R refresh )}
{layout.keys.ctrl}+{layout.keys.range} {t('statusbar.switchWindow')}
{showOnboarding && setShowOnboarding(false)} />}
) } function FooterShortcuts({ shortcuts }) { return shortcuts.map((s, i) => ( {s.keys} {s.desc} )) }