feat: migrate and redesign dashboard to modern React

- Full rewrite of legacy vanilla JS UI into React SPA
- Implement modern design system using Tailwind CSS and shadcn/ui primitives
- Create typed API modules and hooks for voice, media, and moderation
- Add new features: separated Music and Screen Share panels, Image Grid
- Implement unified WebSocket hook for real-time state and PCM audio
- Improve visualizer with smooth CSS transitions and live state sync
- Add __dirname polyfill for ES module compatibility
- Ensure responsive layout for mobile and desktop

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-05-16 19:17:34 +07:00
parent 3c7d722973
commit 82025a19b2
51 changed files with 2865 additions and 1204 deletions

View File

@@ -0,0 +1,63 @@
import { useCallback, useEffect, useState } from "react";
import { getMediaStatus, queueMedia, skipMedia, stopMedia } from "../api/media";
import type { MediaMode, MediaState } from "../types/media";
const emptyMediaState: MediaState = { playing: false, current: null, queue: [] };
export function useMediaControl() {
const [mediaState, setMediaState] = useState<MediaState>(emptyMediaState);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const refreshMedia = useCallback(async () => {
const state = await getMediaStatus();
setMediaState(state);
return state;
}, []);
const enqueue = useCallback(async (source: string, mode: MediaMode) => {
setLoading(true);
setError(null);
try {
const state = await queueMedia(source, mode);
setMediaState(state);
return state;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
setError(message);
throw err;
} finally {
setLoading(false);
}
}, []);
const skip = useCallback(async () => {
setLoading(true);
setError(null);
try {
const state = await skipMedia();
setMediaState(state);
return state;
} finally {
setLoading(false);
}
}, []);
const stop = useCallback(async () => {
setLoading(true);
setError(null);
try {
const state = await stopMedia();
setMediaState(state);
return state;
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
refreshMedia().catch((err) => setError(err instanceof Error ? err.message : String(err)));
}, [refreshMedia]);
return { mediaState, setMediaState, loading, error, refreshMedia, enqueue, skip, stop };
}