refactor(immersive): remove visualizer feature entirely
- Remove VisualizerPanel component - Remove visualizer-focus display mode - Remove visualizer tab from split view - Remove enableVisualizer prop - Remove keyboard shortcut 5 and v for visualizer - Clean up unused imports and CSS
This commit is contained in:
1 parent
14ed60bb94
commit
0fc0723804
5 files changed
+5
-200
No files matched your search
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { X, Disc3, LayoutGrid, MessageSquareText, ListMusic, BarChart3, Music2, Info, Radio } from 'lucide-svelte';
|
||||
import { X, Disc3, LayoutGrid, MessageSquareText, ListMusic, Music2, Info, Radio } from 'lucide-svelte';
|
||||
import { t } from '$lib/i18n';
|
||||
|
||||
export type ImmersiveTab = 'lyrics' | 'credits' | 'suggestions' | 'visualizer' | 'queue';
|
||||
export type DisplayMode = 'coverflow' | 'split' | 'lyrics-focus' | 'queue-focus' | 'visualizer-focus';
|
||||
export type ImmersiveTab = 'lyrics' | 'credits' | 'suggestions' | 'queue';
|
||||
export type DisplayMode = 'coverflow' | 'split' | 'lyrics-focus' | 'queue-focus';
|
||||
|
||||
interface Props {
|
||||
activeTab: ImmersiveTab;
|
||||
@@ -15,7 +15,6 @@
|
||||
hasLyrics?: boolean;
|
||||
hasCredits?: boolean;
|
||||
hasSuggestions?: boolean;
|
||||
hasVisualizer?: boolean;
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -27,15 +26,13 @@
|
||||
visible = true,
|
||||
hasLyrics = true,
|
||||
hasCredits = true,
|
||||
hasSuggestions = true,
|
||||
hasVisualizer = false
|
||||
hasSuggestions = true
|
||||
}: Props = $props();
|
||||
|
||||
const tabs = $derived([
|
||||
{ id: 'lyrics' as const, label: $t('player.lyrics'), icon: Music2, enabled: hasLyrics },
|
||||
{ id: 'credits' as const, label: $t('player.credits') || 'Credits', icon: Info, enabled: hasCredits },
|
||||
{ id: 'suggestions' as const, label: $t('player.suggestions') || 'Suggestions', icon: Radio, enabled: hasSuggestions },
|
||||
{ id: 'visualizer' as const, label: $t('player.visualizer') || 'Visualizer', icon: BarChart3, enabled: hasVisualizer },
|
||||
{ id: 'queue' as const, label: $t('player.queue') || 'Queue', icon: ListMusic, enabled: true },
|
||||
].filter(tab => tab.enabled));
|
||||
|
||||
@@ -44,7 +41,6 @@
|
||||
{ id: 'split', icon: LayoutGrid, title: 'Split View (2)' },
|
||||
{ id: 'lyrics-focus', icon: MessageSquareText, title: 'Lyrics Focus (3)' },
|
||||
{ id: 'queue-focus', icon: ListMusic, title: 'Queue Focus (4)' },
|
||||
{ id: 'visualizer-focus', icon: BarChart3, title: 'Visualizer (5)' },
|
||||
];
|
||||
|
||||
// Only show tabs in split mode
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import LyricsPanel from './panels/LyricsPanel.svelte';
|
||||
import CreditsPanel from './panels/CreditsPanel.svelte';
|
||||
import SuggestionsPanel from './panels/SuggestionsPanel.svelte';
|
||||
import VisualizerPanel from './panels/VisualizerPanel.svelte';
|
||||
import QueuePanel from './panels/QueuePanel.svelte';
|
||||
import CoverflowPanel from './panels/CoverflowPanel.svelte';
|
||||
import LyricsFocusPanel from './panels/LyricsFocusPanel.svelte';
|
||||
@@ -65,7 +64,6 @@
|
||||
// Feature flags
|
||||
enableCredits?: boolean;
|
||||
enableSuggestions?: boolean;
|
||||
enableVisualizer?: boolean;
|
||||
// Queue
|
||||
queueTracks?: QueueTrack[];
|
||||
queueCurrentIndex?: number;
|
||||
@@ -108,7 +106,6 @@
|
||||
lyricsError = null,
|
||||
enableCredits = true,
|
||||
enableSuggestions = true,
|
||||
enableVisualizer = false,
|
||||
queueTracks = [],
|
||||
queueCurrentIndex = 0,
|
||||
onQueuePlayTrack,
|
||||
@@ -175,9 +172,6 @@
|
||||
case '4':
|
||||
displayMode = 'queue-focus';
|
||||
break;
|
||||
case '5':
|
||||
displayMode = 'visualizer-focus';
|
||||
break;
|
||||
// Tab shortcuts (only in split mode)
|
||||
case 'l':
|
||||
case 'L':
|
||||
@@ -191,10 +185,6 @@
|
||||
case 'S':
|
||||
if (displayMode === 'split' && enableSuggestions) activeTab = 'suggestions';
|
||||
break;
|
||||
case 'v':
|
||||
case 'V':
|
||||
if (displayMode === 'split' && enableVisualizer) activeTab = 'visualizer';
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
if (displayMode === 'split') activeTab = 'queue';
|
||||
@@ -247,7 +237,6 @@
|
||||
hasLyrics={true}
|
||||
hasCredits={enableCredits}
|
||||
hasSuggestions={enableSuggestions}
|
||||
hasVisualizer={enableVisualizer}
|
||||
/>
|
||||
|
||||
<!-- Content based on display mode -->
|
||||
@@ -280,13 +269,6 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if displayMode === 'visualizer-focus'}
|
||||
<!-- Visualizer Focus: Full screen visualizer -->
|
||||
<div class="focus-panel">
|
||||
<div class="focus-panel-content visualizer-content">
|
||||
<VisualizerPanel {isPlaying} />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Split: Artwork + Panel side by side -->
|
||||
<div class="main-content">
|
||||
@@ -310,8 +292,6 @@
|
||||
<CreditsPanel {trackId} />
|
||||
{:else if activeTab === 'suggestions'}
|
||||
<SuggestionsPanel {trackId} {artistId} />
|
||||
{:else if activeTab === 'visualizer'}
|
||||
<VisualizerPanel {isPlaying} />
|
||||
{:else if activeTab === 'queue'}
|
||||
<QueuePanel
|
||||
tracks={queueTracks}
|
||||
@@ -407,7 +387,7 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Focus mode panels (queue, visualizer) */
|
||||
/* Focus mode panels (queue) */
|
||||
.focus-panel {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -430,10 +410,6 @@
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.focus-panel-content.visualizer-content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1200px) {
|
||||
.main-content {
|
||||
|
||||
@@ -9,7 +9,6 @@ export { default as ImmersiveControls } from './ImmersiveControls.svelte';
|
||||
export { default as LyricsPanel } from './panels/LyricsPanel.svelte';
|
||||
export { default as CreditsPanel } from './panels/CreditsPanel.svelte';
|
||||
export { default as SuggestionsPanel } from './panels/SuggestionsPanel.svelte';
|
||||
export { default as VisualizerPanel } from './panels/VisualizerPanel.svelte';
|
||||
|
||||
// Shared
|
||||
export { default as ProgressBar } from './shared/ProgressBar.svelte';
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
isPlaying?: boolean;
|
||||
}
|
||||
|
||||
let { isPlaying = false }: Props = $props();
|
||||
|
||||
let canvas: HTMLCanvasElement | null = $state(null);
|
||||
let animationId: number | null = null;
|
||||
let bars: number[] = [];
|
||||
const BAR_COUNT = 64;
|
||||
const BAR_WIDTH = 4;
|
||||
const BAR_GAP = 2;
|
||||
|
||||
// Initialize bars with random heights
|
||||
function initBars() {
|
||||
bars = Array(BAR_COUNT).fill(0).map(() => Math.random() * 0.3 + 0.1);
|
||||
}
|
||||
|
||||
// Animate bars
|
||||
function animate() {
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
const centerX = width / 2;
|
||||
const totalWidth = BAR_COUNT * (BAR_WIDTH + BAR_GAP) - BAR_GAP;
|
||||
const startX = centerX - totalWidth / 2;
|
||||
|
||||
// Clear canvas
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
// Update bar heights with smooth animation
|
||||
bars = bars.map((bar, i) => {
|
||||
if (isPlaying) {
|
||||
// Random movement when playing
|
||||
const target = Math.random() * 0.8 + 0.2;
|
||||
return bar + (target - bar) * 0.15;
|
||||
} else {
|
||||
// Slowly decay to idle state
|
||||
const idle = Math.sin(Date.now() / 1000 + i * 0.2) * 0.1 + 0.15;
|
||||
return bar + (idle - bar) * 0.05;
|
||||
}
|
||||
});
|
||||
|
||||
// Draw bars
|
||||
const gradient = ctx.createLinearGradient(0, height, 0, 0);
|
||||
gradient.addColorStop(0, 'rgba(124, 58, 237, 0.8)'); // Purple
|
||||
gradient.addColorStop(0.5, 'rgba(139, 92, 246, 0.6)');
|
||||
gradient.addColorStop(1, 'rgba(167, 139, 250, 0.4)');
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
|
||||
bars.forEach((bar, i) => {
|
||||
const x = startX + i * (BAR_WIDTH + BAR_GAP);
|
||||
const barHeight = bar * height * 0.7;
|
||||
const y = (height - barHeight) / 2;
|
||||
|
||||
// Rounded rectangle
|
||||
const radius = BAR_WIDTH / 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + radius, y);
|
||||
ctx.lineTo(x + BAR_WIDTH - radius, y);
|
||||
ctx.quadraticCurveTo(x + BAR_WIDTH, y, x + BAR_WIDTH, y + radius);
|
||||
ctx.lineTo(x + BAR_WIDTH, y + barHeight - radius);
|
||||
ctx.quadraticCurveTo(x + BAR_WIDTH, y + barHeight, x + BAR_WIDTH - radius, y + barHeight);
|
||||
ctx.lineTo(x + radius, y + barHeight);
|
||||
ctx.quadraticCurveTo(x, y + barHeight, x, y + barHeight - radius);
|
||||
ctx.lineTo(x, y + radius);
|
||||
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
});
|
||||
|
||||
animationId = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// Handle canvas resize
|
||||
function resizeCanvas() {
|
||||
if (!canvas) return;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
canvas.width = rect.width * window.devicePixelRatio;
|
||||
canvas.height = rect.height * window.devicePixelRatio;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
initBars();
|
||||
resizeCanvas();
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', resizeCanvas);
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="visualizer-panel">
|
||||
<canvas bind:this={canvas} class="visualizer-canvas"></canvas>
|
||||
<div class="visualizer-label">
|
||||
<span class="label-text">Audio Visualizer</span>
|
||||
{#if !isPlaying}
|
||||
<span class="label-hint">Play a track to see the visualization</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.visualizer-panel {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.visualizer-canvas {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.visualizer-label {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
opacity: 0.5;
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
|
||||
.visualizer-panel:hover .visualizer-label {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.label-text {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--alpha-40, rgba(255, 255, 255, 0.4));
|
||||
}
|
||||
|
||||
.label-hint {
|
||||
font-size: 11px;
|
||||
color: var(--alpha-30, rgba(255, 255, 255, 0.3));
|
||||
}
|
||||
</style>
|
||||
@@ -3112,7 +3112,6 @@
|
||||
lyricsError={lyricsStatus === 'error' ? lyricsError : (lyricsStatus === 'not_found' ? 'No lyrics found' : null)}
|
||||
enableCredits={true}
|
||||
enableSuggestions={true}
|
||||
enableVisualizer={false}
|
||||
queueTracks={[
|
||||
...(currentQueueTrack ? [currentQueueTrack] : []),
|
||||
...queue
|
||||
|
||||
Reference in new issue
Block a user