Initial scaffold: FastAPI backend + React frontend + Docker Compose

- Backend: FastAPI with SQLite, clip CRUD, health check
- Frontend: React + Vite + TypeScript + Tailwind, dark theme
- Components: Generator, Gallery, Player
- Docker: Compose setup for backend + frontend
- Architecture docs in Palace KB
This commit is contained in:
rin.kagamine committed 2026-05-26 18:18:55 -04:00
commit a2bc785b14
23 files changed
+819

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
import { useRef, useEffect } from 'react'
interface Clip {
id: string
prompt: string
duration_seconds: number
}
interface Props {
clip: Clip
isPlaying: boolean
onTogglePlay: () => void
onClose: () => void
}
export default function Player({ clip, isPlaying, onTogglePlay, onClose }: Props) {
const audioRef = useRef<HTMLAudioElement>(null)
useEffect(() => {
if (audioRef.current) {
if (isPlaying) {
audioRef.current.play()
} else {
audioRef.current.pause()
}
}
}, [isPlaying, clip.id])
return (
<div className="fixed bottom-0 left-0 right-0 bg-lyria-panel border-t border-white/10 px-6 py-3">
<div className="max-w-6xl mx-auto flex items-center gap-4">
<button
onClick={onTogglePlay}
className="w-10 h-10 rounded-full bg-lyria-accent flex items-center justify-center text-white hover:bg-lyria-accent/90 transition-colors shrink-0"
>
{isPlaying ? (
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z" />
</svg>
) : (
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
)}
</button>
<div className="flex-1 min-w-0">
<p className="text-sm text-white truncate">{clip.prompt}</p>
<p className="text-xs text-lyria-muted">{clip.duration_seconds}s</p>
</div>
<audio
ref={audioRef}
src={`/api/clips/${clip.id}/audio`}
onEnded={() => onTogglePlay()}
className="hidden"
/>
<button
onClick={onClose}
className="text-lyria-muted hover:text-white transition-colors shrink-0"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
)
}