Add always-visible player, play button on sidebar thumbnail, and GitHub link

- Player: Show minimal placeholder when no song is playing instead of hiding
- RightSidebar: Add play button overlay on cover art thumbnail
  - Hover to reveal play/pause button
  - Click anywhere on cover to play
  - Shows pause icon when song is currently playing
- SettingsModal: Add GitHub repo link for issues and feature requests
This commit is contained in:
fspecii
2026-02-04 13:43:25 +02:00
parent e616713ec1
commit 02fa32bc53
4 changed files with 72 additions and 17 deletions
+6
View File
@@ -959,6 +959,9 @@ export default function App() {
onNavigateToSong={handleNavigateToSong} onNavigateToSong={handleNavigateToSong}
isLiked={selectedSong ? likedSongIds.has(selectedSong.id) : false} isLiked={selectedSong ? likedSongIds.has(selectedSong.id) : false}
onToggleLike={toggleLike} onToggleLike={toggleLike}
onPlay={playSong}
isPlaying={isPlaying}
currentSong={currentSong}
/> />
</div> </div>
)} )}
@@ -1086,6 +1089,9 @@ export default function App() {
onNavigateToSong={handleNavigateToSong} onNavigateToSong={handleNavigateToSong}
isLiked={selectedSong ? likedSongIds.has(selectedSong.id) : false} isLiked={selectedSong ? likedSongIds.has(selectedSong.id) : false}
onToggleLike={toggleLike} onToggleLike={toggleLike}
onPlay={playSong}
isPlaying={isPlaying}
currentSong={currentSong}
/> />
</div> </div>
</div> </div>
+13 -1
View File
@@ -75,7 +75,19 @@ export const Player: React.FC<PlayerProps> = ({
return () => window.removeEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown);
}, [isFullscreen]); }, [isFullscreen]);
if (!currentSong) return null; // Show minimal player when no song is playing
if (!currentSong) {
return (
<div className="h-20 lg:h-24 bg-white dark:bg-black/95 backdrop-blur border-t border-zinc-200 dark:border-white/10 flex items-center justify-center z-50 transition-colors duration-300 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)] dark:shadow-none">
<div className="flex items-center gap-3 text-zinc-400 dark:text-zinc-600">
<div className="w-10 h-10 lg:w-12 lg:h-12 rounded bg-zinc-200 dark:bg-zinc-800 flex items-center justify-center">
<Play size={20} className="text-zinc-400 dark:text-zinc-600" />
</div>
<span className="text-sm font-medium">Select a song to play</span>
</div>
</div>
);
}
const formatTime = (time: number) => { const formatTime = (time: number) => {
if (isNaN(time)) return "0:00"; if (isNaN(time)) return "0:00";
+26 -3
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Song } from '../types'; import { Song } from '../types';
import { Heart, Share2, Play, MoreHorizontal, X, Copy, Wand2, MoreVertical, Download, Repeat, Video, Music, Link as LinkIcon, Sparkles, Globe, Lock, Trash2, Edit3, Layers } from 'lucide-react'; import { Heart, Share2, Play, Pause, MoreHorizontal, X, Copy, Wand2, MoreVertical, Download, Repeat, Video, Music, Link as LinkIcon, Sparkles, Globe, Lock, Trash2, Edit3, Layers } from 'lucide-react';
import { songsApi } from '../services/api'; import { songsApi } from '../services/api';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { SongDropdownMenu } from './SongDropdownMenu'; import { SongDropdownMenu } from './SongDropdownMenu';
@@ -19,9 +19,12 @@ interface RightSidebarProps {
onToggleLike?: (song: Song) => void; onToggleLike?: (song: Song) => void;
onDelete?: (song: Song) => void; onDelete?: (song: Song) => void;
onAddToPlaylist?: (song: Song) => void; onAddToPlaylist?: (song: Song) => void;
onPlay?: (song: Song) => void;
isPlaying?: boolean;
currentSong?: Song | null;
} }
export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpenVideo, onReuse, onSongUpdate, onNavigateToProfile, onNavigateToSong, isLiked, onToggleLike, onDelete, onAddToPlaylist }) => { export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpenVideo, onReuse, onSongUpdate, onNavigateToProfile, onNavigateToSong, isLiked, onToggleLike, onDelete, onAddToPlaylist, onPlay, isPlaying, currentSong }) => {
const { token, user } = useAuth(); const { token, user } = useAuth();
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
const [isOwner, setIsOwner] = useState(false); const [isOwner, setIsOwner] = useState(false);
@@ -64,7 +67,10 @@ export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpe
<div className="p-5 space-y-6"> <div className="p-5 space-y-6">
{/* Cover Art */} {/* Cover Art */}
<div className="group relative aspect-square w-full rounded-xl overflow-hidden shadow-2xl bg-zinc-200 dark:bg-zinc-800 ring-1 ring-black/5 dark:ring-white/10"> <div
className="group relative aspect-square w-full rounded-xl overflow-hidden shadow-2xl bg-zinc-200 dark:bg-zinc-800 ring-1 ring-black/5 dark:ring-white/10 cursor-pointer"
onClick={() => onPlay?.(song)}
>
{song.coverUrl ? ( {song.coverUrl ? (
<img src={song.coverUrl} alt={song.title} className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105" onError={(e) => { e.currentTarget.style.display = 'none'; }} /> <img src={song.coverUrl} alt={song.title} className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105" onError={(e) => { e.currentTarget.style.display = 'none'; }} />
) : null} ) : null}
@@ -73,6 +79,23 @@ export const RightSidebar: React.FC<RightSidebarProps> = ({ song, onClose, onOpe
{/* Overlay Gradient */} {/* Overlay Gradient */}
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-60"></div> <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-60"></div>
{/* Play Button Overlay */}
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
<button
onClick={(e) => {
e.stopPropagation();
onPlay?.(song);
}}
className="w-16 h-16 rounded-full bg-white/95 dark:bg-white text-black flex items-center justify-center shadow-2xl hover:scale-110 transition-transform"
>
{isPlaying && currentSong?.id === song.id ? (
<Pause size={28} fill="currentColor" />
) : (
<Play size={28} fill="currentColor" className="ml-1" />
)}
</button>
</div>
<div className="absolute bottom-4 left-4 right-4 flex items-center justify-between"> <div className="absolute bottom-4 left-4 right-4 flex items-center justify-between">
<div className="flex items-center gap-2 text-white"> <div className="flex items-center gap-2 text-white">
<Play size={16} fill="currentColor" /> <Play size={16} fill="currentColor" />
+16 -2
View File
@@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { X, User as UserIcon, Palette, Info, Edit3, ExternalLink } from 'lucide-react'; import { X, User as UserIcon, Palette, Info, Edit3, ExternalLink, Github } from 'lucide-react';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { EditProfileModal } from './EditProfileModal'; import { EditProfileModal } from './EditProfileModal';
@@ -144,7 +144,8 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({ isOpen, onClose, t
Powered by ACE-Step 1.5. Open source and free to use. Powered by ACE-Step 1.5. Open source and free to use.
</p> </p>
<div className="pt-3 border-t border-zinc-200 dark:border-zinc-700/50 mt-4"> <div className="pt-3 border-t border-zinc-200 dark:border-zinc-700/50 mt-4">
<p className="text-zinc-900 dark:text-white font-medium mb-2">Created by Ambsd</p> <p className="text-zinc-900 dark:text-white font-medium mb-3">Created by Ambsd</p>
<div className="flex flex-wrap gap-2">
<a <a
href="https://x.com/AmbsdOP" href="https://x.com/AmbsdOP"
target="_blank" target="_blank"
@@ -156,6 +157,19 @@ export const SettingsModal: React.FC<SettingsModalProps> = ({ isOpen, onClose, t
</svg> </svg>
Follow @AmbsdOP Follow @AmbsdOP
</a> </a>
<a
href="https://github.com/fspecii/ace-step-ui"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-4 py-2 bg-zinc-800 dark:bg-zinc-700 text-white rounded-lg text-sm font-medium hover:bg-zinc-700 dark:hover:bg-zinc-600 transition-colors"
>
<Github size={16} />
GitHub Repo
</a>
</div>
<p className="text-xs text-zinc-400 dark:text-zinc-500 mt-3">
Report issues or request features on GitHub
</p>
</div> </div>
</div> </div>
</div> </div>