import { useEffect } from "react" import { PanelSection, PanelSectionRow, staticClasses, } from "@decky/ui" import { definePlugin, routerHook, } from "@decky/api" import { FaChartLine } from "react-icons/fa" import MainPanel from "./components/main-panel" import { registerLibraryAppPatch, setLibraryAppPanelProps } from "./patches/LibraryApp" import { useSettings, useSession, useGameDetection } from "./lib/store" import { readAndParseMangohudLog, clearMangohudLog, deleteLogFile, findMangohudLog, startMangohudLogging, stopMangohudLogging, getHardwareInfo, getOsVersion, getProtonVersion, getLaunchOptions, } from "./lib/api" function Content() { const { settings, updateSetting, loaded } = useSettings() // keep the library panel's props in sync with settings useEffect(() => { setLibraryAppPanelProps({ hardwareSlug: settings.hardwareSlug, baseUrl: settings.baseUrl }) }, [settings.hardwareSlug, settings.baseUrl]) const { recordingState, session, recentSessions, error, setError, startRecording, stopRecording, updateSession, addToRecent, reset, onGameStart, onGameStop, setGameName, setLastLogPath, } = useSession() // ── Game detection via polling ──────────────────────────────── useGameDetection(setGameName, recordingState) // ── Handle start recording ──────────────────────────────────── async function handleStart() { // Clear the previous session's specific log if we know it; else safe-clear. const prev = session.lastLogPath if (prev) { await deleteLogFile(prev) } else { await clearMangohudLog() } // Fire-and-forget: try to start MangoHud logging (retries until game launches) startMangohudLogging() startRecording() } // ── Handle stop recording: parse log + read system info ──────── async function handleStop() { try { // Try to stop MangoHud logging (best-effort, may fail if game already closed) await stopMangohudLogging() stopRecording() // Find the most recent MangoHud log, parse it, remember its path const logPath = await findMangohudLog() const logResult = await readAndParseMangohudLog(logPath.path ?? undefined) if (logResult.error) { setError(logResult.error) setLastLogPath(null) return } setLastLogPath(logPath.path ?? null) // Read system info in parallel const [hwInfo, osVersion] = await Promise.all([ getHardwareInfo(), getOsVersion(), ]) // Read Proton version + launch options if we have an app ID let protonVersion = "" let launchOptions = "" const currentAppId = session.appId if (currentAppId) { try { const [pv, lo] = await Promise.all([ getProtonVersion(currentAppId), getLaunchOptions(currentAppId), ]) protonVersion = pv launchOptions = lo } catch { // Non-critical, continue without } } // Use settings hardware override if set, otherwise auto-detected const hardwareSlug = settings.hardwareSlug || hwInfo.slug updateSession({ fpsAvg: logResult.fpsAvg ?? null, fpsLow: logResult.fpsLow ?? null, fpsHigh: logResult.fpsHigh ?? null, fpsOnePercentLow: logResult.fpsOnePercentLow ?? null, tdpWatts: logResult.tdpWatts ?? null, hardwareSlug, hardwareName: hwInfo.name, osVersion, protonVersion, launchOptions, }) } catch (e) { console.error("[DeckyVault] Error stopping recording:", e) setError("Failed to process recording. Check the MangoHud log.") } } if (!loaded) { return (
Loading...
) } return ( <> ) } // ── DeckyVault icon (flat SVG) ──────────────────────────────── function DeckyVaultIcon() { return ( ) } export default definePlugin(() => { const libraryAppPatch = registerLibraryAppPatch() return { name: "DeckyVault", titleView:
DeckyVault
, content: , icon: , alwaysRender: true, onDismount() { try { routerHook.removePatch("/library/app/:appid", libraryAppPatch) } catch (e) { console.error("[DeckyVault] removePatch failed:", e) } console.log("[DeckyVault] Plugin unloading") }, } })