From 1bad1f4e33706e01c9173c4df7249e682686623d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Mon, 31 Aug 2026 17:31:57 +0200 Subject: [PATCH] Streamline the Replay view with the Control view Page title is now "{session name} - Replay" (matching generateMetadata) instead of a bare "Replay" heading with the name duplicated inside a Card title. Dropped that redundant outer Card - the pre-connection scan/match buttons now sit in a flat row below the header, same as Control's scan/record row, with the same "no devices connected" hint card shown underneath when nothing's connected yet. Playback controls once a replay starts still live in a card, matching DeviceCard's style. Also gave "Match & replay" a Play icon. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01WzyfRyA7h5rs5SCAahLAWd --- app/(app)/sessions/[id]/replay/page.tsx | 11 +- components/sessions/ReplayPlayer.tsx | 196 ++++++++++++------------ 2 files changed, 110 insertions(+), 97 deletions(-) diff --git a/app/(app)/sessions/[id]/replay/page.tsx b/app/(app)/sessions/[id]/replay/page.tsx index 46eca40..fa8b231 100644 --- a/app/(app)/sessions/[id]/replay/page.tsx +++ b/app/(app)/sessions/[id]/replay/page.tsx @@ -5,14 +5,21 @@ import { getSessionName } from "@/lib/db/queries/sessions"; export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise { const { id } = await params; const name = await getSessionName(Number(id)); - return { title: name ? `Replay ${name}` : "Replay" }; + return { title: name ? `${name} - Replay` : "Replay" }; } export default async function ReplayPage({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; + const name = await getSessionName(Number(id)); return (
-

Replay

+
+

{name ?? `Session #${id}`} - Replay

+

+ Connect the devices you want to replay onto, then match them to this session's original device + slots and play its recorded intensity back live over Web Bluetooth. +

+
); diff --git a/components/sessions/ReplayPlayer.tsx b/components/sessions/ReplayPlayer.tsx index 23cee3f..d1cc0cf 100644 --- a/components/sessions/ReplayPlayer.tsx +++ b/components/sessions/ReplayPlayer.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Slider } from "@/components/ui/slider"; import { DeviceScanPanel } from "@/components/control/DeviceScanPanel"; @@ -137,109 +137,115 @@ export function ReplayPlayer({ sessionId }: { sessionId: number }) { return (
- - - {data.session.name ?? `Session #${data.session.id}`} - - - {!player ? ( - <> -

- Connect the devices you want to replay onto, then match them to the session's + {!player ? ( + <> +

+ void startScanning()} onStopScan={() => void stopScanning()} /> + +
+ + {connectedDevices.length === 0 && ( + + + No devices connected yet. Scan to discover nearby toys, then match them to this session's original device slots. -

-
- void startScanning()} onStopScan={() => void stopScanning()} /> + + + )} + + ) : ( + + + {replayTargets.length > 0 && ( +
+ {replayTargets.map((t) => ( + + {t.slotLabel} → {t.deviceName} + + ))} +
+ )} + player.seek(v)} + /> +
+ + {formatTime(elapsedMs)} / {formatTime(data.session.durationMs)} + +
+
- - ) : ( -
- {replayTargets.length > 0 && ( -
- {replayTargets.map((t) => ( - - {t.slotLabel} → {t.deviceName} - - ))} -
- )} - player.seek(v)} - /> -
- - {formatTime(elapsedMs)} / {formatTime(data.session.durationMs)} - -
- - -
-
- )} - - + + + )}