feat: add Docker support and texture deletion functionality

Add Docker configuration with standalone output mode for containerized
deployment. Implement texture deletion API with proper error handling
and user feedback. Fix skin viewer sizing issues by using explicit
dimensions and removing conflicting layout properties. Add captcha
ID parameter to registration flow. Improve profile page UX including
Yggdrasil password reset display and character card editing controls.
This commit is contained in:
2026-02-24 11:09:37 +08:00
parent f5455afaf2
commit 2124790c8d
13 changed files with 264 additions and 81 deletions

View File

@@ -42,19 +42,32 @@ export default function CharacterCard({
transition={{ duration: 0.2 }}
>
<div className="flex items-center justify-between mb-4">
{isEditing ? (
<input
type="text"
value={editName}
onChange={(e) => onEditNameChange(e.target.value)}
className="text-lg font-semibold bg-transparent border-b border-orange-500 focus:outline-none text-gray-900 dark:text-white flex-1 mr-2"
onBlur={() => onSave(profile.uuid)}
onKeyPress={(e) => e.key === 'Enter' && onSave(profile.uuid)}
autoFocus
/>
) : (
<h3 className="text-lg font-semibold text-gray-900 dark:text-white truncate flex-1">{profile.name}</h3>
)}
<div className="flex items-center gap-2 flex-1">
{isEditing ? (
<input
type="text"
value={editName}
onChange={(e) => onEditNameChange(e.target.value)}
className="text-lg font-semibold bg-transparent border-b border-orange-500 focus:outline-none text-gray-900 dark:text-white flex-1"
onBlur={() => onSave(profile.uuid)}
onKeyPress={(e) => e.key === 'Enter' && onSave(profile.uuid)}
autoFocus
/>
) : (
<>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white truncate">{profile.name}</h3>
<motion.button
onClick={() => onEdit(profile.uuid, profile.name)}
className="text-gray-500 hover:text-orange-500 transition-colors"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
title="改名"
>
<PencilIcon className="w-4 h-4" />
</motion.button>
</>
)}
</div>
{profile.is_active && (
<span className="px-2 py-1 bg-gradient-to-r from-green-500 to-emerald-500 text-white text-xs rounded-full flex items-center space-x-1">
<CheckIcon className="w-3 h-3" />
@@ -68,9 +81,8 @@ export default function CharacterCard({
<SkinViewer
skinUrl={skinUrl}
isSlim={isSlim}
width={200}
height={200}
className="w-full h-full"
width={180}
height={180}
autoRotate={false}
/>
) : (
@@ -82,32 +94,10 @@ export default function CharacterCard({
<UserIcon className="w-10 h-10 text-white" />
</motion.div>
)}
{/* 皮肤选择按钮 */}
<motion.button
onClick={() => onSelectSkin(profile.uuid)}
className="absolute bottom-2 right-2 bg-gradient-to-r from-orange-500 to-amber-500 text-white p-2 rounded-full shadow-lg"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
title="选择皮肤"
>
<PencilIcon className="w-4 h-4" />
</motion.button>
</div>
{/* 操作按钮 */}
<div className="flex gap-2">
{!profile.is_active && (
<motion.button
onClick={() => onSetActive(profile.uuid)}
className="flex-1 bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white text-sm py-2 px-3 rounded-lg transition-all duration-200"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
使
</motion.button>
)}
{isEditing ? (
<>
<motion.button
@@ -130,13 +120,12 @@ export default function CharacterCard({
) : (
<>
<motion.button
onClick={() => onEdit(profile.uuid, profile.name)}
className="flex-1 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 text-sm py-2 px-3 rounded-lg transition-all duration-200"
onClick={() => onSelectSkin(profile.uuid)}
className="flex-1 bg-gradient-to-r from-orange-500 to-amber-500 hover:from-orange-600 hover:to-amber-600 text-white text-sm py-2 px-3 rounded-lg transition-all duration-200"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<PencilIcon className="w-4 h-4 inline mr-1" />
</motion.button>
<motion.button
onClick={() => onDelete(profile.uuid)}