From b6b10159adb5a0ac264d954b9a0573839ec33d54 Mon Sep 17 00:00:00 2001 From: hurkicorgi Date: Mon, 13 Apr 2026 00:51:35 -0600 Subject: [PATCH] Polish remaining UX items: avatar fallback, chat truncation, delete confirmations - PlayerAvatar component with onError fallback to initial badge; used in PlayerManager and ChatBridge. - ChatBridge: truncate long usernames to 16 chars with tooltip, add avatar column, bump tinted text colors to -300 for better dark-mode contrast. - BackupManager: confirm step for Delete (was one-click). - ModManager: confirm step for snapshot Delete, clearer "Confirm Restore" / "Confirm Delete" labels, unify outer spacing with the rest of admin. Co-Authored-By: Claude Opus 4.6 (1M context) --- components/BackupManager.tsx | 26 ++++++++++++- components/ChatBridge.tsx | 75 +++++++++++++++++++++--------------- components/ModManager.tsx | 30 +++++++++++++-- components/PlayerAvatar.tsx | 40 +++++++++++++++++++ components/PlayerManager.tsx | 19 ++------- 5 files changed, 140 insertions(+), 50 deletions(-) create mode 100644 components/PlayerAvatar.tsx diff --git a/components/BackupManager.tsx b/components/BackupManager.tsx index 630b25a..8adf675 100644 --- a/components/BackupManager.tsx +++ b/components/BackupManager.tsx @@ -21,6 +21,7 @@ type Backup = { export function BackupManager() { const queryClient = useQueryClient(); const [confirmRestore, setConfirmRestore] = useState(null); + const [confirmDelete, setConfirmDelete] = useState(null); const [result, setResult] = useState<{ ok: boolean; message: string } | null>(null); const { data: backups = [] } = useQuery({ @@ -151,6 +152,29 @@ export function BackupManager() { Cancel + ) : confirmDelete === b.name ? ( + <> + + + ) : ( <> deleteBackup.mutate(b.name)} + onClick={() => setConfirmDelete(b.name)} disabled={isBusy} className="text-xs h-9 text-muted-foreground sm:opacity-0 sm:group-hover:opacity-100 transition" > diff --git a/components/ChatBridge.tsx b/components/ChatBridge.tsx index 3b73af6..d57e8bc 100644 --- a/components/ChatBridge.tsx +++ b/components/ChatBridge.tsx @@ -4,7 +4,7 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useState, useRef, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Badge } from "@/components/ui/badge"; +import { PlayerAvatar } from "@/components/PlayerAvatar"; import { Card, CardContent, @@ -69,12 +69,16 @@ export function ChatBridge() { const typeColors: Record = { chat: "text-foreground", - join: "text-emerald-400", - leave: "text-amber-400", - death: "text-red-400", - server: "text-blue-400", + join: "text-emerald-300", + leave: "text-amber-300", + death: "text-red-300", + server: "text-blue-300", }; + const MAX_NAME = 16; + const truncateName = (n: string) => + n.length > MAX_NAME ? `${n.slice(0, MAX_NAME - 1)}…` : n; + return ( @@ -95,34 +99,43 @@ export function ChatBridge() { No chat messages yet

) : ( - messages.map((msg, i) => ( -
- - {msg.time} - - {msg.type === "chat" ? ( - - <{msg.player}> {msg.message} + messages.map((msg, i) => { + const hasPlayer = msg.type !== "server"; + const shortName = truncateName(msg.player); + return ( +
+ + {msg.time} - ) : msg.type === "join" ? ( - - {msg.player} joined the game + {hasPlayer ? ( + + ) : ( +
+ S +
+ )} + + {msg.type === "chat" ? ( + <>{shortName} {msg.message} + ) : msg.type === "join" ? ( + <>{shortName} joined the game + ) : msg.type === "leave" ? ( + <>{shortName} left the game + ) : msg.type === "death" ? ( + <>{shortName} {msg.message} + ) : ( + <>[Server] {msg.message} + )} - ) : msg.type === "leave" ? ( - - {msg.player} left the game - - ) : msg.type === "death" ? ( - - {msg.player} {msg.message} - - ) : ( - - [Server] {msg.message} - - )} -
- )) +
+ ); + }) )} diff --git a/components/ModManager.tsx b/components/ModManager.tsx index 260727a..e934e66 100644 --- a/components/ModManager.tsx +++ b/components/ModManager.tsx @@ -121,6 +121,7 @@ export function ModManager() { } | null>(null); const [confirmRemove, setConfirmRemove] = useState(null); const [confirmRestore, setConfirmRestore] = useState(null); + const [confirmDeleteSnap, setConfirmDeleteSnap] = useState(null); const debounceRef = useRef>(null); // Installed mods @@ -340,7 +341,7 @@ export function ModManager() { }; return ( -
+
{/* ── Mod Manager Card ──────────────────────────────── */} @@ -799,7 +800,7 @@ export function ModManager() { disabled={isBusy} className="text-xs h-9" > - Confirm + Confirm Restore + + ) : ( <>