From 87eeb3fb392a1f1c0db3758887ca7e1664b810e2 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Sun, 28 Jun 2026 07:57:34 +0800 Subject: [PATCH] feat(plugin): add main panel component (record/stop + recent sessions) --- .../decky-vault/src/components/main-panel.tsx | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 plugins/decky-vault/src/components/main-panel.tsx diff --git a/plugins/decky-vault/src/components/main-panel.tsx b/plugins/decky-vault/src/components/main-panel.tsx new file mode 100644 index 0000000..3636445 --- /dev/null +++ b/plugins/decky-vault/src/components/main-panel.tsx @@ -0,0 +1,152 @@ +import { useEffect, useState } from "react" +import { + ButtonItem, + PanelSection, + PanelSectionRow, + staticClasses, +} from "@decky/ui" +import { + FaPlay, + FaStop, + FaClock, +} from "react-icons/fa" +import type { RecordingState, SessionData, RecentSession, PluginSettings } from "../lib/store" +import SessionForm from "./session-form" + +interface MainPanelProps { + recordingState: RecordingState + session: SessionData + recentSessions: RecentSession[] + error: string + settings: PluginSettings + onStart: () => void + onStop: () => void + onUpdateSession: (updates: Partial) => void + onAddToRecent: (sess: SessionData) => void + onReset: () => void + setError: (msg: string) => void +} + +export default function MainPanel({ + recordingState, + session, + recentSessions, + error, + settings, + onStart, + onStop, + onUpdateSession, + onAddToRecent, + onReset, + setError, +}: MainPanelProps) { + const [elapsed, setElapsed] = useState(0) + + // Timer for recording state + useEffect(() => { + if (recordingState !== "recording") { + setElapsed(0) + return + } + const interval = setInterval(() => { + setElapsed(Math.floor((Date.now() - session.startedAt) / 1000)) + }, 1000) + return () => clearInterval(interval) + }, [recordingState, session.startedAt]) + + function formatTime(seconds: number): string { + const m = Math.floor(seconds / 60) + const s = seconds % 60 + return `${m}:${s.toString().padStart(2, "0")}` + } + + // ── Stopped state: show the session form ────────────────────── + if (recordingState === "stopped") { + return ( + + ) + } + + // ── Idle or Recording state ─────────────────────────────────── + return ( + + {error && ( + +
+ {error} +
+
+ )} + + + {recordingState === "idle" ? ( + +
+ + Start Recording +
+
+ ) : ( + +
+ + Stop Recording +
+
+ )} +
+ + {recordingState === "recording" && ( + <> + +
+
+ + {formatTime(elapsed)} +
+
+ {session.gameName + ? `Recording: ${session.gameName}` + : "No game detected — recording anyway"} +
+
+
+ + )} + + {recordingState === "idle" && ( + +
+ Enable MangoHud for your game, then press Start Recording before launching. + Configure MangoHud in the Settings tab. +
+
+ )} + + {recentSessions.length > 0 && recordingState === "idle" && ( + + {recentSessions.map((rs, i) => ( + +
+ {rs.gameName || "Unknown game"} +
+ + {rs.fpsAvg ? `${rs.fpsAvg} FPS avg` : "No data"} ·{" "} + {new Date(rs.date).toLocaleDateString()} + +
+
+ ))} +
+ )} +
+ ) +} \ No newline at end of file