From 7021f4da071e79dc38766d6c0a1017fe67eba6b2 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Mon, 13 Jul 2026 06:22:13 +0800 Subject: [PATCH 1/4] fix(plugin): single row stats + dropdown + button, no filler text --- .../src/components/LibraryAppPanel.tsx | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/plugins/decky-vault/src/components/LibraryAppPanel.tsx b/plugins/decky-vault/src/components/LibraryAppPanel.tsx index 020efda..27403bd 100644 --- a/plugins/decky-vault/src/components/LibraryAppPanel.tsx +++ b/plugins/decky-vault/src/components/LibraryAppPanel.tsx @@ -94,43 +94,31 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: return ( <> - {/* Quick stats row */} + {/* Quick stats + device dropdown — single row */} -
- {device && data.estFps ? ( - - {data.estFps.avg} avg FPS +
+ {data.estFps ? ( + + {data.estFps.avg} avg {data.estFps.onePct != null && {data.estFps.onePct} 1% low} - {data.estFps.low != null && {data.estFps.low} min} + {data.estFps.low != null && {data.estFps.low} min {data.estFps.high != null && {data.estFps.high} max} {data.estFps.tdpAvg != null && {data.estFps.tdpAvg}W TDP} - {data.estFps.count} entries + ({data.estFps.count}) ) : ( - - {device - ? "No entries for this device yet" - : "Select a device to see estimated FPS"} - + No data yet )} + setDevice(opt.data as string)} + /> + Router.NavigateToExternalWeb(gameUrl)}> + ↗ +
- - {/* Device dropdown */} - - setDevice(opt.data as string)} - /> - - - {/* Open on DeckyVault button */} - - Router.NavigateToExternalWeb(gameUrl)}> - View on DeckyVault - - ) } \ No newline at end of file From c80c1d57750eb8e53aef7b1bdf4c1d26f6a84920 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Mon, 13 Jul 2026 06:25:55 +0800 Subject: [PATCH 2/4] fix(plugin): centered stats, View Details button with fallback, separate rows --- .../src/components/LibraryAppPanel.tsx | 54 +++++++++++++------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/plugins/decky-vault/src/components/LibraryAppPanel.tsx b/plugins/decky-vault/src/components/LibraryAppPanel.tsx index 27403bd..3da1375 100644 --- a/plugins/decky-vault/src/components/LibraryAppPanel.tsx +++ b/plugins/decky-vault/src/components/LibraryAppPanel.tsx @@ -16,6 +16,22 @@ interface Props { baseUrl: string } +function openExternalUrl(url: string) { + try { + if (Router?.NavigateToExternalWeb) { + Router.NavigateToExternalWeb(url) + return + } + } catch (e) { + console.error("[DeckyVault] NavigateToExternalWeb failed:", e) + } + try { + window.open(url, "_blank") + } catch (e) { + console.error("[DeckyVault] window.open failed:", e) + } +} + export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: Props) { const [data, setData] = useState(null) const [devices, setDevices] = useState([]) @@ -65,7 +81,7 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: if (loading) { return ( -
+
DeckyVault loading…
@@ -75,7 +91,7 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: if (fetchError) { return ( -
+
DeckyVault: {fetchError}
@@ -85,7 +101,7 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: if (!data || !data.game) { return ( -
+
Not on DeckyVault — add it
@@ -94,14 +110,14 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: return ( <> - {/* Quick stats + device dropdown — single row */} + {/* Centered stats row */} -
+
{data.estFps ? ( - + {data.estFps.avg} avg {data.estFps.onePct != null && {data.estFps.onePct} 1% low} - {data.estFps.low != null && {data.estFps.low} min + {data.estFps.low != null && {data.estFps.low} min} {data.estFps.high != null && {data.estFps.high} max} {data.estFps.tdpAvg != null && {data.estFps.tdpAvg}W TDP} ({data.estFps.count}) @@ -109,16 +125,24 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: ) : ( No data yet )} - setDevice(opt.data as string)} - /> - Router.NavigateToExternalWeb(gameUrl)}> - ↗ -
+ + {/* Device dropdown */} + + setDevice(opt.data as string)} + /> + + + {/* View Details button */} + + openExternalUrl(gameUrl)}> + View Details + + ) } \ No newline at end of file From d409e27a95064fd4a47c7fe726ad74eec058cf68 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Mon, 13 Jul 2026 06:28:41 +0800 Subject: [PATCH 3/4] fix(plugin): game URL by steamAppId, inline dropdown to save vertical space --- .../src/components/LibraryAppPanel.tsx | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/plugins/decky-vault/src/components/LibraryAppPanel.tsx b/plugins/decky-vault/src/components/LibraryAppPanel.tsx index 3da1375..601a3a4 100644 --- a/plugins/decky-vault/src/components/LibraryAppPanel.tsx +++ b/plugins/decky-vault/src/components/LibraryAppPanel.tsx @@ -74,8 +74,8 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: .map((d) => ({ label: `${d.name} (${d.count})`, data: d.slug })), ] - const gameUrl = data?.game?.slug - ? `${baseUrl}/games/${data.game.slug}` + const gameUrl = data?.game?.steamAppId + ? `${baseUrl}/game/${data.game.steamAppId}` : `${baseUrl}/games` if (loading) { @@ -110,33 +110,31 @@ export default function LibraryAppPanel({ appId, title, hardwareSlug, baseUrl }: return ( <> - {/* Centered stats row */} + {/* Stats + device dropdown inline */} -
- {data.estFps ? ( - - {data.estFps.avg} avg - {data.estFps.onePct != null && {data.estFps.onePct} 1% low} - {data.estFps.low != null && {data.estFps.low} min} - {data.estFps.high != null && {data.estFps.high} max} - {data.estFps.tdpAvg != null && {data.estFps.tdpAvg}W TDP} - ({data.estFps.count}) - - ) : ( - No data yet - )} +
+ + {data.estFps ? ( + + {data.estFps.avg} avg + {data.estFps.onePct != null && {data.estFps.onePct} 1% low} + {data.estFps.low != null && {data.estFps.low} min} + {data.estFps.high != null && {data.estFps.high} max} + {data.estFps.tdpAvg != null && {data.estFps.tdpAvg}W TDP} + ({data.estFps.count}) + + ) : ( + No data yet + )} + + setDevice(opt.data as string)} + />
- {/* Device dropdown */} - - setDevice(opt.data as string)} - /> - - {/* View Details button */} openExternalUrl(gameUrl)}> From c5ac3ba5677f2a8c41a69a23f621cc88b18db120 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Mon, 13 Jul 2026 06:30:00 +0800 Subject: [PATCH 4/4] fix(api): add tdpAvg to estFps type in buildPluginGameResponse args --- apps/web/lib/api/__tests__/plugin-public.test.ts | 2 +- apps/web/lib/api/plugin-public.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/lib/api/__tests__/plugin-public.test.ts b/apps/web/lib/api/__tests__/plugin-public.test.ts index afd9415..c7012e2 100644 --- a/apps/web/lib/api/__tests__/plugin-public.test.ts +++ b/apps/web/lib/api/__tests__/plugin-public.test.ts @@ -93,7 +93,7 @@ describe("buildPluginGameResponse — shape contract", () => { const r = await buildPluginGameResponse({ game: { id: "g1", steamAppId: 123, title: "X", slug: "x" }, entries, recent: entries, - estFps: { avg: 72.3, low: 35, onePct: 38, high: 140, count: 25 }, + estFps: { avg: 72.3, low: 35, onePct: 38, high: 140, count: 25, tdpAvg: 13.5 }, }) expect(r.estFps).not.toBeNull() expect(r.estFps!.avg).toBe(72.3) diff --git a/apps/web/lib/api/plugin-public.ts b/apps/web/lib/api/plugin-public.ts index cb601a2..f977023 100644 --- a/apps/web/lib/api/plugin-public.ts +++ b/apps/web/lib/api/plugin-public.ts @@ -71,7 +71,7 @@ export async function buildPluginGameResponse(args: { game: PluginGameRow | null entries?: PluginEntryRow[] recent?: PluginEntryRow[] - estFps?: { avg: number; low: number | null; onePct: number | null; high: number | null; count: number } | null + estFps?: { avg: number; low: number | null; onePct: number | null; high: number | null; count: number; tdpAvg: number | null } | null }): Promise { if (!args.game) { return { game: null, estFps: null, topEntries: [], recentEntries: [] }