/** * @file Music Player Node - Interactive music player with visualizer */ import * as React from "react"; import type { NodeDefinition, NodeRendererProps, InspectorRenderProps, ExternalDataReference, } from "../../../../../types/NodeDefinition"; import { getTextColor } from "./colorUtils"; import classes from "./MusicPlayerNode.module.css"; export type MusicData = { id: string; title: string; artist: string; duration: number; // in seconds currentTime: number; isPlaying: boolean; volume: number; // 0-100 visualizerData: number[]; // amplitude data for visualizer }; export const MusicPlayerRenderer = ({ node, isSelected, isDragging, externalData }: NodeRendererProps) => { const musicData = externalData as MusicData | undefined; const [localTime, setLocalTime] = React.useState(musicData?.currentTime || 0); React.useEffect(() => { setLocalTime(musicData?.currentTime || 0); }, [musicData?.currentTime]); const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, "0")}`; }; const progress = musicData ? (localTime / musicData.duration) * 100 : 0; const playerColor = "#ec4899"; return (
🎵 {musicData?.title || "No Track"}
{musicData?.artist || "Unknown Artist"}
{musicData?.isPlaying ? "▶ PLAYING" : "⏸ PAUSED"}
{musicData?.visualizerData?.map((amplitude, index) => (
))}
{formatTime(localTime)} {formatTime(musicData?.duration || 0)}
🔊
); }; export const MusicPlayerInspectorRenderer = ({ externalData, onUpdateExternalData }: InspectorRenderProps) => { const musicData = externalData as MusicData | undefined; const [editedData, setEditedData] = React.useState({ id: musicData?.id || "", title: musicData?.title || "New Track", artist: musicData?.artist || "Unknown Artist", duration: musicData?.duration || 180, currentTime: musicData?.currentTime || 0, isPlaying: musicData?.isPlaying || false, volume: musicData?.volume || 75, visualizerData: musicData?.visualizerData || Array.from({ length: 20 }, () => Math.random() * 100), }); const handleSave = async () => { if (onUpdateExternalData) { await onUpdateExternalData(editedData); } }; const togglePlayback = () => { setEditedData((prev) => ({ ...prev, isPlaying: !prev.isPlaying })); }; const randomizeVisualizer = () => { setEditedData((prev) => ({ ...prev, visualizerData: Array.from({ length: 20 }, () => Math.random() * 100), })); }; return (

Music Player

setEditedData({ ...editedData, title: e.target.value })} className={classes.input} />
setEditedData({ ...editedData, artist: e.target.value })} className={classes.input} />
setEditedData({ ...editedData, duration: Number(e.target.value) })} className={classes.slider} />
setEditedData({ ...editedData, currentTime: Number(e.target.value) })} className={classes.slider} />
setEditedData({ ...editedData, volume: Number(e.target.value) })} className={classes.slider} />
); }; export const MusicPlayerNodeDefinition: NodeDefinition = { type: "music-player", displayName: "Music Player", description: "Interactive music player with visualizer", category: "Media", defaultData: { title: "Music Player", }, defaultSize: { width: 300, height: 180 }, ports: [ { id: "audio-input", type: "input", label: "Audio Source", position: "left", }, { id: "audio-output", type: "output", label: "Audio Stream", position: "right", }, { id: "visualization", type: "output", label: "Visual Data", position: "bottom", }, ], renderNode: MusicPlayerRenderer, renderInspector: MusicPlayerInspectorRenderer, loadExternalData: async (ref: ExternalDataReference) => { await new Promise((resolve) => setTimeout(resolve, 200)); return { id: ref.id, title: "Epic Journey", artist: "Digital Dreams", duration: 210, currentTime: 45, isPlaying: true, volume: 75, visualizerData: Array.from({ length: 20 }, () => Math.random() * 100), }; }, updateExternalData: async (_ref: ExternalDataReference, data: unknown) => { await new Promise((resolve) => setTimeout(resolve, 200)); console.log("Updated music player data:", data); }, };