feat(plugin): restructure layout like LSFG-VK, use mangohudctl for logging
This commit is contained in:
@@ -4,15 +4,32 @@ import {
|
||||
PanelSection,
|
||||
PanelSectionRow,
|
||||
TextField,
|
||||
DropdownItem,
|
||||
staticClasses,
|
||||
} from "@decky/ui"
|
||||
import {
|
||||
FaPlay,
|
||||
FaStop,
|
||||
FaClock,
|
||||
FaGamepad,
|
||||
FaCopy,
|
||||
FaCheck,
|
||||
FaTimes,
|
||||
FaDownload,
|
||||
FaCog,
|
||||
FaFileExport,
|
||||
FaFileImport,
|
||||
FaSearch,
|
||||
} from "react-icons/fa"
|
||||
import type { RecordingState, SessionData, RecentSession, PluginSettings } from "../lib/store"
|
||||
import { KNOWN_HARDWARE_SLUGS } from "@deckyvault/shared"
|
||||
import {
|
||||
testApiKey,
|
||||
checkMangohud,
|
||||
writeMangohudConfig,
|
||||
getMangohudConfig,
|
||||
exportConfig,
|
||||
importConfig,
|
||||
} from "../lib/api"
|
||||
import SessionForm from "./session-form"
|
||||
|
||||
interface MainPanelProps {
|
||||
@@ -28,8 +45,14 @@ interface MainPanelProps {
|
||||
onReset: () => void
|
||||
setError: (msg: string) => void
|
||||
setGameName: (name: string, appId?: number) => void
|
||||
onUpdateSetting: <K extends keyof PluginSettings>(key: K, value: string | null) => void
|
||||
}
|
||||
|
||||
const HARDWARE_OPTIONS = [
|
||||
{ label: "Auto-detect", data: "" },
|
||||
...KNOWN_HARDWARE_SLUGS.map((slug) => ({ label: slug, data: slug })),
|
||||
]
|
||||
|
||||
export default function MainPanel({
|
||||
recordingState,
|
||||
session,
|
||||
@@ -43,8 +66,25 @@ export default function MainPanel({
|
||||
onReset,
|
||||
setError,
|
||||
setGameName,
|
||||
onUpdateSetting,
|
||||
}: MainPanelProps) {
|
||||
const [elapsed, setElapsed] = useState(0)
|
||||
const [mangohudStatus, setMangohudStatus] = useState<{
|
||||
checked: boolean
|
||||
installed: boolean
|
||||
path: string
|
||||
version: string
|
||||
}>({ checked: false, installed: false, path: "", version: "" })
|
||||
const [keyTestStatus, setKeyTestStatus] = useState<"idle" | "testing" | "valid" | "invalid">("idle")
|
||||
const [keyTestMessage, setKeyTestMessage] = useState("")
|
||||
const [copiedLaunchOpt, setCopiedLaunchOpt] = useState(false)
|
||||
const [configWritten, setConfigWritten] = useState(false)
|
||||
const [configVerified, setConfigVerified] = useState<{
|
||||
checked: boolean
|
||||
valid: boolean
|
||||
message: string
|
||||
}>({ checked: false, valid: false, message: "" })
|
||||
const [configStatus, setConfigStatus] = useState<{ message: string; isError: boolean } | null>(null)
|
||||
|
||||
// Timer for recording state
|
||||
useEffect(() => {
|
||||
@@ -64,6 +104,97 @@ export default function MainPanel({
|
||||
return `${m}:${s.toString().padStart(2, "0")}`
|
||||
}
|
||||
|
||||
async function handleCheckMangohud() {
|
||||
const result = await checkMangohud()
|
||||
setMangohudStatus({
|
||||
checked: true,
|
||||
installed: result.installed,
|
||||
path: result.path,
|
||||
version: result.version,
|
||||
})
|
||||
}
|
||||
|
||||
async function handleTestKey() {
|
||||
if (!settings.apiKey) {
|
||||
setKeyTestStatus("invalid")
|
||||
setKeyTestMessage("Enter an API key first")
|
||||
return
|
||||
}
|
||||
setKeyTestStatus("testing")
|
||||
setKeyTestMessage("")
|
||||
const result = await testApiKey(settings.apiKey, settings.baseUrl)
|
||||
if (result.valid) {
|
||||
setKeyTestStatus("valid")
|
||||
setKeyTestMessage("API key is valid")
|
||||
} else {
|
||||
setKeyTestStatus("invalid")
|
||||
setKeyTestMessage(result.error || "Invalid API key")
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCopyLaunchOption() {
|
||||
try {
|
||||
await navigator.clipboard.writeText("mangohud %command%")
|
||||
setCopiedLaunchOpt(true)
|
||||
setTimeout(() => setCopiedLaunchOpt(false), 2000)
|
||||
} catch {
|
||||
const ta = document.createElement("textarea")
|
||||
ta.value = "mangohud %command%"
|
||||
document.body.appendChild(ta)
|
||||
ta.select()
|
||||
document.execCommand("copy")
|
||||
document.body.removeChild(ta)
|
||||
setCopiedLaunchOpt(true)
|
||||
setTimeout(() => setCopiedLaunchOpt(false), 2000)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleWriteConfig() {
|
||||
const result = await writeMangohudConfig()
|
||||
setConfigWritten(result.success)
|
||||
}
|
||||
|
||||
async function handleVerifyConfig() {
|
||||
const result = await getMangohudConfig()
|
||||
if (!result.exists) {
|
||||
setConfigVerified({ checked: true, valid: false, message: "No MangoHud config found. Write one first." })
|
||||
return
|
||||
}
|
||||
const content = result.content
|
||||
const hasOutputFolder = content.includes("output_folder=/tmp")
|
||||
const hasOutputFile = content.includes("output_file=deckyvault-mangohud.log")
|
||||
const hasFps = content.includes("fps")
|
||||
if (hasOutputFolder && hasOutputFile && hasFps) {
|
||||
setConfigVerified({ checked: true, valid: true, message: "Config looks good" })
|
||||
} else {
|
||||
setConfigVerified({ checked: true, valid: false, message: "Config is missing required settings. Write it again." })
|
||||
}
|
||||
}
|
||||
|
||||
async function handleExportConfig() {
|
||||
setConfigStatus(null)
|
||||
const result = await exportConfig(settings)
|
||||
if (result.success) {
|
||||
setConfigStatus({ message: `Config saved to ${result.path}`, isError: false })
|
||||
} else {
|
||||
setConfigStatus({ message: result.error || "Export failed", isError: true })
|
||||
}
|
||||
}
|
||||
|
||||
async function handleImportConfig() {
|
||||
setConfigStatus(null)
|
||||
const result = await importConfig()
|
||||
if (result.success && result.settings) {
|
||||
onUpdateSetting("apiKey", result.settings.apiKey || "")
|
||||
onUpdateSetting("exportPath", result.settings.exportPath || "/home/deck/Downloads")
|
||||
onUpdateSetting("baseUrl", result.settings.baseUrl || "https://deckyvault.xyz")
|
||||
onUpdateSetting("hardwareSlug", result.settings.hardwareSlug || null)
|
||||
setConfigStatus({ message: "Config imported from Downloads", isError: false })
|
||||
} else {
|
||||
setConfigStatus({ message: result.error || "No config file found in Downloads", isError: true })
|
||||
}
|
||||
}
|
||||
|
||||
// ── Stopped state: show the session form ──────────────────────
|
||||
if (recordingState === "stopped") {
|
||||
return (
|
||||
@@ -79,88 +210,272 @@ export default function MainPanel({
|
||||
)
|
||||
}
|
||||
|
||||
// ── Idle or Recording state ───────────────────────────────────
|
||||
return (
|
||||
<PanelSection title="Recording">
|
||||
{error && (
|
||||
<>
|
||||
{/* ── Status ──────────────────────────────────────────────── */}
|
||||
<PanelSection title="Status">
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ color: "#e74c3c", padding: "8px" }}>
|
||||
{error}
|
||||
<ButtonItem layout="below" onClick={handleCheckMangohud}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FaCog />
|
||||
Check MangoHud Status
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
{mangohudStatus.checked && (
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "13px", padding: "4px 0" }}>
|
||||
{mangohudStatus.installed ? (
|
||||
<><FaCheck style={{ color: "#2ecc71" }} /> MangoHud {mangohudStatus.version}</>
|
||||
) : (
|
||||
<><FaTimes style={{ color: "#e74c3c" }} /> MangoHud not found</>
|
||||
)}
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
)}
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={handleTestKey} disabled={keyTestStatus === "testing"}>
|
||||
{keyTestStatus === "testing" ? "Testing..." : "Test API Key"}
|
||||
{keyTestStatus === "valid" && <FaCheck style={{ color: "#2ecc71", marginLeft: "8px" }} />}
|
||||
{keyTestStatus === "invalid" && <FaTimes style={{ color: "#e74c3c", marginLeft: "8px" }} />}
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
{keyTestMessage && (
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "12px", color: keyTestStatus === "valid" ? "#2ecc71" : "#e74c3c", padding: "4px 0" }}>
|
||||
{keyTestMessage}
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
)}
|
||||
</PanelSection>
|
||||
|
||||
{/* ── Usage Instructions ──────────────────────────────────── */}
|
||||
<PanelSection title="Usage Instructions">
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "12px", padding: "4px 0", lineHeight: "1.5" }}>
|
||||
Add this to your game's Steam launch options, then press Start Recording before launching.
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
)}
|
||||
<PanelSectionRow>
|
||||
<div style={{
|
||||
background: "rgba(255,255,255,0.1)",
|
||||
borderRadius: "8px",
|
||||
padding: "10px 14px",
|
||||
fontFamily: "monospace",
|
||||
fontSize: "14px",
|
||||
textAlign: "center",
|
||||
}}>
|
||||
mangohud %command%
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={handleCopyLaunchOption}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px", justifyContent: "center" }}>
|
||||
<FaCopy />
|
||||
{copiedLaunchOpt ? "Copied to clipboard" : "Copy Launch Option"}
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "11px", opacity: 0.6, padding: "4px 0" }}>
|
||||
The MangoHud config is stored in ~/.config/MangoHud/MangoHud.conf
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSectionRow>
|
||||
{recordingState === "idle" ? (
|
||||
<ButtonItem layout="below" onClick={onStart}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FaPlay />
|
||||
Start Recording
|
||||
</div>
|
||||
</ButtonItem>
|
||||
) : (
|
||||
<ButtonItem layout="below" onClick={onStop} disabled={false}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FaStop />
|
||||
Stop Recording
|
||||
</div>
|
||||
</ButtonItem>
|
||||
{/* ── Recording ──────────────────────────────────────────── */}
|
||||
<PanelSection title="Recording">
|
||||
{error && (
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ color: "#e74c3c", padding: "8px" }}>{error}</div>
|
||||
</PanelSectionRow>
|
||||
)}
|
||||
</PanelSectionRow>
|
||||
|
||||
{recordingState === "recording" && (
|
||||
<>
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ padding: "8px 0" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "6px", marginBottom: "4px" }}>
|
||||
<FaClock />
|
||||
<strong>{formatTime(elapsed)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
{session.gameName
|
||||
? `Recording: ${session.gameName}`
|
||||
: "No game detected — recording anyway"}
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
</>
|
||||
)}
|
||||
{recordingState === "idle" && (
|
||||
<>
|
||||
<PanelSectionRow>
|
||||
<TextField
|
||||
label="Game Name"
|
||||
value={session.gameName}
|
||||
onChange={(e) => setGameName(e.target.value)}
|
||||
placeholder="e.g. Cyberpunk 2077"
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={onStart}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px", justifyContent: "center" }}>
|
||||
<FaPlay />
|
||||
Start Recording
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
</>
|
||||
)}
|
||||
|
||||
{recordingState === "idle" && (
|
||||
<>
|
||||
<PanelSectionRow>
|
||||
<TextField
|
||||
label="Game Name"
|
||||
value={session.gameName}
|
||||
onChange={(e) => setGameName(e.target.value)}
|
||||
placeholder="e.g. Cyberpunk 2077"
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ padding: "8px 0", fontSize: "12px", opacity: 0.7 }}>
|
||||
Enable MangoHud for your game, then press Start Recording before launching.
|
||||
Configure MangoHud in the Settings tab.
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
</>
|
||||
)}
|
||||
|
||||
{recentSessions.length > 0 && recordingState === "idle" && (
|
||||
<PanelSection title="Recent Recordings">
|
||||
{recentSessions.map((rs, i) => (
|
||||
<PanelSectionRow key={i}>
|
||||
<div className={staticClasses.Text} style={{ padding: "4px 0", fontSize: "13px" }}>
|
||||
<strong>{rs.gameName || "Unknown game"}</strong>
|
||||
<br />
|
||||
<span style={{ opacity: 0.6 }}>
|
||||
{rs.fpsAvg ? `${rs.fpsAvg} FPS avg` : "No data"} ·{" "}
|
||||
{new Date(rs.date).toLocaleDateString()}
|
||||
</span>
|
||||
{recordingState === "recording" && (
|
||||
<>
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ padding: "8px 0", textAlign: "center" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: "6px", marginBottom: "4px" }}>
|
||||
<FaClock />
|
||||
<strong>{formatTime(elapsed)}</strong>
|
||||
</div>
|
||||
<div style={{ fontSize: "13px", opacity: 0.7 }}>
|
||||
{session.gameName ? `Recording: ${session.gameName}` : "Recording..."}
|
||||
</div>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
))}
|
||||
</PanelSection>
|
||||
)}
|
||||
</PanelSection>
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={onStop}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px", justifyContent: "center" }}>
|
||||
<FaStop />
|
||||
Stop Recording
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
</>
|
||||
)}
|
||||
|
||||
{recentSessions.length > 0 && recordingState === "idle" && (
|
||||
<PanelSection title="Recent Recordings">
|
||||
{recentSessions.map((rs, i) => (
|
||||
<PanelSectionRow key={i}>
|
||||
<div className={staticClasses.Text} style={{ padding: "4px 0", fontSize: "13px" }}>
|
||||
<strong>{rs.gameName || "Unknown game"}</strong>
|
||||
<br />
|
||||
<span style={{ opacity: 0.6 }}>
|
||||
{rs.fpsAvg ? `${rs.fpsAvg} FPS avg` : "No data"} · {new Date(rs.date).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
))}
|
||||
</PanelSection>
|
||||
)}
|
||||
</PanelSection>
|
||||
|
||||
{/* ── MangoHud Config ────────────────────────────────────── */}
|
||||
<PanelSection title="MangoHud Config">
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={handleWriteConfig}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FaDownload />
|
||||
Write Config
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
{configWritten && (
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "12px", color: "#2ecc71", padding: "4px 0" }}>
|
||||
<FaCheck /> Config written
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
)}
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={handleVerifyConfig}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FaSearch />
|
||||
Verify Config
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
{configVerified.checked && (
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "12px", padding: "4px 0", color: configVerified.valid ? "#2ecc71" : "#e74c3c" }}>
|
||||
{configVerified.valid ? <FaCheck /> : <FaTimes />} {configVerified.message}
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
)}
|
||||
</PanelSection>
|
||||
|
||||
{/* ── Configuration ────────────────────────────────────────── */}
|
||||
<PanelSection title="Configuration">
|
||||
<PanelSectionRow>
|
||||
<TextField
|
||||
label="API Key"
|
||||
value={settings.apiKey}
|
||||
onChange={(e) => onUpdateSetting("apiKey", e.target.value)}
|
||||
placeholder="dv_..."
|
||||
bIsPassword
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<TextField
|
||||
label="Export Path"
|
||||
value={settings.exportPath}
|
||||
onChange={(e) => onUpdateSetting("exportPath", e.target.value)}
|
||||
placeholder="/home/deck/Downloads"
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<TextField
|
||||
label="Server URL"
|
||||
value={settings.baseUrl}
|
||||
onChange={(e) => onUpdateSetting("baseUrl", e.target.value)}
|
||||
placeholder="https://deckyvault.xyz"
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<DropdownItem
|
||||
label="Default Hardware"
|
||||
rgOptions={HARDWARE_OPTIONS}
|
||||
selectedOption={settings.hardwareSlug || ""}
|
||||
onChange={(opt) => onUpdateSetting("hardwareSlug", opt.data as string || null)}
|
||||
/>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={handleExportConfig}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FaFileExport />
|
||||
Export Config to Downloads
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
<PanelSectionRow>
|
||||
<ButtonItem layout="below" onClick={handleImportConfig}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<FaFileImport />
|
||||
Import Config from Downloads
|
||||
</div>
|
||||
</ButtonItem>
|
||||
</PanelSectionRow>
|
||||
{configStatus && (
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "12px", padding: "4px 0", color: configStatus.isError ? "#e74c3c" : "#2ecc71" }}>
|
||||
{configStatus.isError ? <FaTimes /> : <FaCheck />} {configStatus.message}
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
)}
|
||||
</PanelSection>
|
||||
|
||||
{/* ── MangoHud Setup Guide ────────────────────────────────── */}
|
||||
<PanelSection title="MangoHud Setup Guide">
|
||||
<PanelSectionRow>
|
||||
<div className={staticClasses.Text} style={{ fontSize: "12px", padding: "8px", lineHeight: "1.6" }}>
|
||||
<strong>Steam Deck (SteamOS):</strong><br />
|
||||
MangoHud is pre-installed. Enable it per-game by adding<br />
|
||||
<code style={{ display: "block", margin: "4px 0", padding: "4px", background: "rgba(255,255,255,0.1)" }}>
|
||||
mangohud %command%
|
||||
</code>
|
||||
to the game's Steam launch options (right-click game → Properties → Launch Options).
|
||||
|
||||
<br /><br />
|
||||
<strong>Other Linux handhelds</strong> (ROG Ally, Legion Go):<br />
|
||||
Install via package manager:
|
||||
<code style={{ display: "block", margin: "4px 0", padding: "4px", background: "rgba(255,255,255,0.1)" }}>
|
||||
sudo apt install mangohud
|
||||
</code>
|
||||
or Flatpak:
|
||||
<code style={{ display: "block", margin: "4px 0", padding: "4px", background: "rgba(255,255,255,0.1)" }}>
|
||||
flatpak install flathub org.freedesktop.Platform.VulkanLayer.MangoHud
|
||||
</code>
|
||||
|
||||
<br /><br />
|
||||
<strong>Troubleshooting:</strong><br />
|
||||
• Log file empty? Check MangoHud is enabled for the game and the config was written.<br />
|
||||
• Not attaching? Try adding <code>mangohud %command%</code> to Steam launch options explicitly.
|
||||
</div>
|
||||
</PanelSectionRow>
|
||||
</PanelSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -9,12 +9,13 @@ import {
|
||||
} from "@decky/api"
|
||||
import { FaChartLine } from "react-icons/fa"
|
||||
import MainPanel from "./components/main-panel"
|
||||
import SettingsPanel from "./components/settings-panel"
|
||||
import { useSettings, useSession } from "./lib/store"
|
||||
import {
|
||||
readAndParseMangohudLog,
|
||||
clearMangohudLog,
|
||||
writeMangohudConfig,
|
||||
startMangohudLogging,
|
||||
stopMangohudLogging,
|
||||
getHardwareInfo,
|
||||
getOsVersion,
|
||||
getProtonVersion,
|
||||
@@ -71,16 +72,20 @@ function Content() {
|
||||
|
||||
// ── Handle start recording ────────────────────────────────────
|
||||
async function handleStart() {
|
||||
// Write MangoHud config with autostart_log so logging begins immediately
|
||||
// Write MangoHud config with logging settings
|
||||
await writeMangohudConfig()
|
||||
// Clear any previous log file
|
||||
await clearMangohudLog()
|
||||
// Start MangoHud logging via mangohudctl
|
||||
await startMangohudLogging()
|
||||
startRecording()
|
||||
}
|
||||
|
||||
// ── Handle stop recording: parse log + read system info ────────
|
||||
async function handleStop() {
|
||||
try {
|
||||
// Stop MangoHud logging via mangohudctl
|
||||
await stopMangohudLogging()
|
||||
stopRecording()
|
||||
|
||||
// Parse the MangoHud log
|
||||
@@ -161,9 +166,6 @@ function Content() {
|
||||
onReset={reset}
|
||||
setError={setError}
|
||||
setGameName={setGameName}
|
||||
/>
|
||||
<SettingsPanel
|
||||
settings={settings}
|
||||
onUpdateSetting={updateSetting}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -33,11 +33,21 @@ export const readAndParseMangohudLog = callable<[logPath?: string], {
|
||||
error?: string
|
||||
}>("read_and_parse_mangohud_log")
|
||||
|
||||
export const clearMangohudLog = callable<[logPath?: string], {
|
||||
export const clearMangohudLog = callable<[], {
|
||||
success: boolean
|
||||
error?: string
|
||||
}>("clear_mangohud_log")
|
||||
|
||||
export const startMangohudLogging = callable<[], {
|
||||
success: boolean
|
||||
error?: string
|
||||
}>("start_mangohud_logging")
|
||||
|
||||
export const stopMangohudLogging = callable<[], {
|
||||
success: boolean
|
||||
error?: string
|
||||
}>("stop_mangohud_logging")
|
||||
|
||||
// ── System Info ─────────────────────────────────────────────────
|
||||
export const getHardwareInfo = callable<[], {
|
||||
slug: string
|
||||
|
||||
Reference in New Issue
Block a user