feat(plugin): add main panel component (record/stop + recent sessions)

This commit is contained in:
2026-06-28 07:57:34 +08:00
parent ad816d1ae5
commit 87eeb3fb39
@@ -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<SessionData>) => 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 (
<SessionForm
session={session}
error={error}
settings={settings}
onUpdateSession={onUpdateSession}
onAddToRecent={onAddToRecent}
onReset={onReset}
setError={setError}
/>
)
}
// ── Idle or Recording state ───────────────────────────────────
return (
<PanelSection title="Recording">
{error && (
<PanelSectionRow>
<div className={staticClasses.Text} style={{ color: "#e74c3c", padding: "8px" }}>
{error}
</div>
</PanelSectionRow>
)}
<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>
)}
</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>
<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>
</div>
</PanelSectionRow>
))}
</PanelSection>
)}
</PanelSection>
)
}