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:
vicrodh committed 2026-01-30 22:32:33 -06:00
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 {
-1
View File
@@ -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>
-1
View File
@@ -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