/* meowpanel.js — MeowPanel 前端入口(应用入口/路由/启动)。 * 由 mp_shared.js + mp_pages.js 组合;在两者之后作为经典脚本加载。 */ (function () { 'use strict'; var MP = window.MP; const { $, $$, LOG_GROUP_NAME, SPARK_MAX, api, apiDownload, applyTheme, authMode, authRequired, barHtml, bindLogin, bindPwDlg, bindSlider, bindThemeUi, browserMode, confirmDlg, dbPct, dbg, dbgErr, debounce, deciToDb, devStyleLabel, devThemeLabel, doLogin, drawSpark, emptyState, errText, esc, fmtBytes, fmtDb, fmtTime, fmtUptime, hideLogin, homeCard, homeKv, homeUnavailable, initTheme, isAvail, levelMeterHtml, loginErr, modePill, passwordDlg, pct, pushSpark, refreshAuthGate, resetSpark, resolveMode, resolveStyle, saveSetting, setConn, showLogin, slur, sparkPeak, sparkRms, sparklineHtml, sparklinePoints, stat, state, storageDel, storageGet, storageSet, storedMode, storedStyle, toast, toastTimer, updateLevelMeter, volTimer, absFor, appInfo, appStatePill, appUninstall, applyScreenDeltaDom, audioCard, audioCtlHtml, audioFlowHtml, audioPortName, audioRouteLabel, batteryCard, batteryCardBody, bindApps, bindAudio, bindBg, bindFiles, bindFonts, bindHome, bindLogs, bindNet, bindNotif, bindPin, bindPower, bindProc, bindScreen, bindSettings, bindSystem, breakdownCardBody, btCard, createImgFromBlob, crumbHtml, deviceCard, doDownload, ensureScreenCanvas, escSafeAttr, estimateCardBody, eventsBar, fetchAudioOnce, fetchHomeLevel, fetchHomeSystem, fetchNetConns, fetchNetLog, fetchNetOnce, fetchOverviewOnce, fetchPerfOnce, fetchPowerOnce, fetchProcOnce, fetchScreen, fmtDur, fmtFrames, groupName, handleRemoteGesture, healthCard, importFont, importFontPage, installFromFile, installFromPath, joinRel, loadFonts, loadFontsPage, loadPinStatus, logsCrashCard, mkdirFile, navFiles, netBadge, netBannerErr, netCard, netConnsBody, netDnsBody, netIfaceCards, netPolicyCard, netStateCls, netStatusCards, newestStdout, perfCard, pinEdit, pinEditDlg, powerEventsCard, powerHistoryCard, powerPolicyCard, powerProfileLabel, powerSummaryCard, pubBytesCard, remoteBtn, remotePadHtml, remotePointerDown, remotePointerUp, removeFile, renameFile, renderApps, renderAudio, renderBg, renderBgBody, renderBright, renderFiles, renderFontsPage, renderHome, renderHomeCards, renderLogs, renderLogsFiles, renderNet, renderNotif, renderPerf, renderPerfData, renderPower, renderPowerBody, renderProc, renderProcBody, renderScreen, renderSettings, renderSystem, restartScreenPoll, row, screenTap, seedScreenCanvas, segments, sendKey, sendKeyLabel, sendKeyRepeat, sendTheme, setBrightCtl, setPanelPassword, settingControl, srcPill, stopAudio, stopBg, stopHome, stopNet, stopPerf, stopPower, stopProc, stopScreenPoll, storageCard, svcStatePill, uploadFiles, viewAppStdout, viewLogsCrashTail, volSteps } = MP; const ROUTES = { home: { title: '首页仪表盘', render: renderHome, bind: bindHome }, perf: { title: '实时性能', render: renderPerf, bind: null }, bright: { title: '亮度 · 音量', render: renderBright, bind: null }, audio: { title: '音频状态', render: renderAudio, bind: bindAudio }, net: { title: '网络管理', render: renderNet, bind: bindNet }, files: { title: '文件管理', render: renderFiles, bind: bindFiles }, apps: { title: 'MeowAPPs 管理', render: renderApps, bind: bindApps }, fonts: { title: '字体管理', render: renderFontsPage, bind: bindFonts }, settings: { title: '设置镜像', render: renderSettings, bind: bindSettings }, screen: { title: '屏幕 · 遥控', render: renderScreen, bind: bindScreen }, system: { title: '系统信息 / 升级', render: renderSystem, bind: bindSystem }, logs: { title: '系统日志', render: renderLogs, bind: bindLogs }, notif: { title: '通知中心', render: renderNotif, bind: bindNotif }, power: { title: '电源 / 功耗', render: renderPower, bind: bindPower }, proc: { title: '进程 / 服务', render: renderProc, bind: bindProc }, bg: { title: '后台管理', render: renderBg, bind: bindBg } }; let started = false; function bindNav() { $$('.nav-item').forEach(b => b.addEventListener('click', () => { location.hash = '#/' + b.dataset.route; })); $('#menu-btn').addEventListener('click', () => $('#sidebar').classList.toggle('open')); } async function init() { bindNav(); bindThemeUi(); bindSlider(); bindFiles(); bindApps(); bindFonts(); bindSettings(); bindScreen(); bindSystem(); bindLogs(); bindNotif(); bindNet(); bindHome(); bindProc(); bindBg(); bindPin(); bindAudio(); bindLogin(); bindPwDlg(); // 首次进入:访问级 PIN 鉴权——已登录直接进;未设 PIN(且无旧密码)→ 首次设置流程;已设 → 登录 let st = null; try { st = await api('GET', '/api/auth/status'); } catch (e) { st = null; } if (st && st.session) { startApp(); return; } if (st && !st.pin_set && !st.legacy) { showLogin('setup'); return; } if (st) { showLogin('login'); return; } startApp(); // 后端不可达:仍启动(显示离线横幅) } function route() { if (state.unauth) { showLogin(MP.authMode); return; } // 未认证 → 登录页(保持当前设置/登录态) const id = (location.hash || '#/home').replace('#/', '').split('?')[0] || 'home'; const r = ROUTES[id] || ROUTES.home; state.route = id; dbg('route →', id); stopPerf(); stopAudio(); stopNet(); stopScreenPoll(); stopHome(); stopPower(); stopProc(); stopBg(); $$('.nav-item').forEach(b => b.classList.toggle('active', b.dataset.route === id)); $('#page-title').textContent = r.title; // 顶栏「一键清后台」仅后台管理页显示(内容由 fetchBgOnce 按数据更新) const bc = $('#bg-clear'); if (bc) { bc.hidden = (id !== 'bg'); if (id !== 'bg') bc.disabled = false; } r.render(); } function startApp() { if (started) { route(); return; } started = true; initTheme(); window.addEventListener('hashchange', route); route(); } document.addEventListener('DOMContentLoaded', init); })();