import { useEffect, useRef } from "react"
import {
PanelSection,
PanelSectionRow,
staticClasses,
} from "@decky/ui"
import {
definePlugin,
} from "@decky/api"
import { FaChartLine } from "react-icons/fa"
import MainPanel from "./components/main-panel"
import { useSettings, useSession } from "./lib/store"
import {
readAndParseMangohudLog,
clearMangohudLog,
writeMangohudConfig,
startMangohudLogging,
stopMangohudLogging,
getHardwareInfo,
getOsVersion,
getProtonVersion,
getLaunchOptions,
} from "./lib/api"
function Content() {
const { settings, updateSetting, loaded } = useSettings()
const {
recordingState,
session,
recentSessions,
error,
setError,
startRecording,
stopRecording,
updateSession,
addToRecent,
reset,
onGameStart,
onGameStop,
setGameName,
} = useSession()
const gameStartedUnregRef = useRef<{ unregister: () => void } | null>(null)
// ── Register SteamClient game events ──────────────────────────
useEffect(() => {
try {
// Use RegisterForAppLifetimeNotifications — the correct API for
// detecting when games start/stop on Steam Deck.
const reg = SteamClient.GameSessions.RegisterForAppLifetimeNotifications(
(notification: AppLifetimeNotification) => {
if (notification.bRunning) {
// Game started — try to get the display name from appStore
let gameName = `App ${notification.unAppID}`
try {
const overview = window.appStore?.GetAppOverviewByAppID(notification.unAppID)
if (overview?.display_name) {
gameName = overview.display_name
}
} catch {
// fallback
}
onGameStart(notification.unAppID, gameName)
} else {
// Game stopped
onGameStop()
}
},
)
gameStartedUnregRef.current = reg
} catch (e) {
console.warn("[DeckyVault] SteamClient event registration failed:", e)
}
return () => {
try {
gameStartedUnregRef.current?.unregister()
} catch {
// ignore
}
}
}, [onGameStart, onGameStop])
// ── Handle start recording ────────────────────────────────────
async function handleStart() {
// Write MangoHud config with logging settings
await writeMangohudConfig()
// Clear any previous log file
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()
// Parse the MangoHud log
const logResult = await readAndParseMangohudLog()
if (logResult.error) {
setError(logResult.error)
return
}
// 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 (