react-youtube-jukebox 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # react-youtube-jukebox
2
+
3
+ A floating YouTube jukebox and playlist player component for React.
4
+
5
+ It wraps the YouTube IFrame Player API with a dock-style player UI, queue rotation, portal rendering, and a customizable expanded panel.
6
+
7
+ For full documentation and live demos, visit **[react-youtube-jukebox.com](https://react-youtube-jukebox.com/)**.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add react-youtube-jukebox
13
+ ```
14
+
15
+ ```bash
16
+ npm i react-youtube-jukebox
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import { Jukebox } from "react-youtube-jukebox";
23
+
24
+ const tracks = [
25
+ { videoId: "yTg4v2Cnfyo", title: "Soul Below", artist: "Ljones" },
26
+ { videoId: "s4MQku9Mkwc", title: "Something About Us", artist: "Daft Punk" },
27
+ ];
28
+
29
+ export function Page() {
30
+ return <Jukebox tracks={tracks} position="bottom-center" offset={20} />;
31
+ }
32
+ ```
33
+
34
+ 기본 스타일은 컴포넌트 import 시 자동으로 주입됩니다.
35
+ 기본 테마는 `glass`이며, 필요하면 `theme="simple"`, `theme="sunset"`, `theme="ride"`를 전달할 수 있습니다.
36
+ 쉘 형태는 `chrome` prop으로 제어하며 기본값은 `classic`입니다. `wallet`과 `ride` 프리셋으로 같은 로직 위에 다른 UI chrome을 적용할 수 있습니다.
37
+
38
+ ## Custom Expanded Panel
39
+
40
+ ```tsx
41
+ import {
42
+ Jukebox,
43
+ type JukeboxExpandedRenderProps,
44
+ } from "react-youtube-jukebox";
45
+
46
+ function CustomExpandedPanel({
47
+ currentTrack,
48
+ isExpanded,
49
+ playerMountRef,
50
+ togglePlay,
51
+ }: JukeboxExpandedRenderProps) {
52
+ return (
53
+ <section data-open={isExpanded}>
54
+ <div ref={playerMountRef} style={{ aspectRatio: "16 / 9" }} />
55
+ <strong>{currentTrack.title}</strong>
56
+ <button onClick={togglePlay}>Toggle</button>
57
+ </section>
58
+ );
59
+ }
60
+
61
+ export function Page() {
62
+ return (
63
+ <Jukebox
64
+ tracks={tracks}
65
+ renderExpandedContent={(props) => <CustomExpandedPanel {...props} />}
66
+ />
67
+ );
68
+ }
69
+ ```
70
+
71
+ `renderExpandedContent`를 전달하면 기본 expanded 디자인 대신 사용자 렌더를 사용합니다. 토글 애니메이션과 컨테이너 visibility는 라이브러리가 계속 관리하고, 소비자는 내부 레이아웃과 컨트롤만 정의하면 됩니다.
72
+
73
+ ## Props
74
+
75
+ ```ts
76
+ type JukeboxTrack = {
77
+ videoId: string;
78
+ title: string;
79
+ artist?: string;
80
+ };
81
+
82
+ type JukeboxTheme = "glass" | "simple" | "sunset" | "ride";
83
+ type JukeboxChrome = "classic" | "wallet" | "ride";
84
+ type JukeboxExpandedRenderProps = {
85
+ currentIndex: number;
86
+ currentTrack: JukeboxTrack;
87
+ isExpanded: boolean;
88
+ isMuted: boolean;
89
+ isPlaying: boolean;
90
+ nextTrack: JukeboxTrack | undefined;
91
+ playerMountRef: (node: HTMLDivElement | null) => void;
92
+ totalTracks: number;
93
+ volume: number;
94
+ setVolume: (nextVolume: number) => void;
95
+ toggleMute: () => void;
96
+ togglePlay: () => void;
97
+ playNext: () => void;
98
+ playPrev: () => void;
99
+ };
100
+
101
+ type JukeboxProps = {
102
+ tracks: JukeboxTrack[];
103
+ autoplay?: boolean;
104
+ position?:
105
+ | "bottom-right"
106
+ | "bottom-left"
107
+ | "bottom-center"
108
+ | "top-right"
109
+ | "top-left"
110
+ | "top-center";
111
+ theme?: JukeboxTheme;
112
+ chrome?: JukeboxChrome;
113
+ offset?: number | { x: number; y: number };
114
+ portal?: boolean;
115
+ className?: string;
116
+ renderExpandedContent?: (
117
+ props: JukeboxExpandedRenderProps,
118
+ ) => React.ReactNode;
119
+ };
120
+ ```
121
+
122
+ 기본 사용은 viewport 기준 포털 렌더링입니다. 레이아웃 안에서 직접 배치가 필요할 때만 `portal={false}`를 사용합니다.
123
+ `position="bottom-center"`와 `position="top-center"`를 사용하면 x축은 가운데 정렬되고, `offset`은 top/bottom 여백에 그대로 적용됩니다.
124
+ `autoplay`는 기본값이 `true`이며, 첫 진입 시 무음 상태로 자동 재생합니다. 자동 재생을 끄려면 `autoplay={false}`를 전달합니다.
@@ -0,0 +1,48 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ type JukeboxTrack = {
5
+ videoId: string;
6
+ title: string;
7
+ artist?: string;
8
+ };
9
+ type JukeboxPosition = "bottom-right" | "bottom-left" | "bottom-center" | "top-right" | "top-left" | "top-center";
10
+ type JukeboxTheme = "glass" | "simple" | "sunset" | "ride";
11
+ type JukeboxChrome = "classic" | "wallet" | "ride";
12
+ type JukeboxOffset = number | {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ type JukeboxProps = {
17
+ tracks: JukeboxTrack[];
18
+ autoplay?: boolean;
19
+ position?: JukeboxPosition;
20
+ theme?: JukeboxTheme;
21
+ chrome?: JukeboxChrome;
22
+ offset?: JukeboxOffset;
23
+ portal?: boolean;
24
+ className?: string;
25
+ renderExpandedContent?: (props: JukeboxExpandedRenderProps) => ReactNode;
26
+ };
27
+ type JukeboxPlayerState = {
28
+ currentIndex: number;
29
+ isMuted: boolean;
30
+ isPlaying: boolean;
31
+ playerMountRef: (node: HTMLDivElement | null) => void;
32
+ volume: number;
33
+ setVolume: (nextVolume: number) => void;
34
+ toggleMute: () => void;
35
+ togglePlay: () => void;
36
+ playNext: () => void;
37
+ playPrev: () => void;
38
+ };
39
+ type JukeboxExpandedRenderProps = JukeboxPlayerState & {
40
+ currentTrack: JukeboxTrack;
41
+ isExpanded: boolean;
42
+ nextTrack: JukeboxTrack | undefined;
43
+ totalTracks: number;
44
+ };
45
+
46
+ declare function Jukebox({ tracks, autoplay, position, theme, chrome, offset, portal, className, renderExpandedContent, }: JukeboxProps): react_jsx_runtime.JSX.Element | null;
47
+
48
+ export { Jukebox, type JukeboxChrome, type JukeboxExpandedRenderProps, type JukeboxOffset, type JukeboxPosition, type JukeboxProps, type JukeboxTheme, type JukeboxTrack };
package/dist/index.js ADDED
@@ -0,0 +1,683 @@
1
+ import { useState, useSyncExternalStore, useRef, useCallback, useEffect } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import clsx from 'clsx';
4
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
+
6
+ // src/components/Jukebox.tsx
7
+
8
+ // src/lib/shared.ts
9
+ var DEFAULT_POSITION = "bottom-right";
10
+ var DEFAULT_THEME = "glass";
11
+ var DEFAULT_CHROME = "classic";
12
+ var DEFAULT_OFFSET_PX = 20;
13
+ var DEFAULT_VOLUME = 100;
14
+ var LEVEL_BAR_HEIGHTS = [12, 18, 14];
15
+ var LEVEL_BAR_REST_HEIGHT = 8;
16
+ var LEVEL_BAR_ANIMATION_DELAY_MS = 120;
17
+ function getEffectiveChrome(chrome) {
18
+ const isTemporarilyDisabledChrome = chrome === "wallet" || chrome === "ride";
19
+ if (isTemporarilyDisabledChrome) {
20
+ return DEFAULT_CHROME;
21
+ }
22
+ return chrome;
23
+ }
24
+ function getNextTrackIndex(index, step, totalTracks) {
25
+ if (totalTracks <= 0) {
26
+ return 0;
27
+ }
28
+ return (index + step + totalTracks) % totalTracks;
29
+ }
30
+ function clampVolume(value) {
31
+ return Math.min(Math.max(Math.round(value), 0), 100);
32
+ }
33
+ function normalizeOffset(offset) {
34
+ if (typeof offset === "number") {
35
+ return { x: offset, y: offset };
36
+ }
37
+ if (offset) {
38
+ return { x: offset.x, y: offset.y };
39
+ }
40
+ return { x: DEFAULT_OFFSET_PX, y: DEFAULT_OFFSET_PX };
41
+ }
42
+ function getPositionStyle(position, offset, isPortal) {
43
+ const normalizedOffset = normalizeOffset(offset);
44
+ const isTopPosition = position.startsWith("top");
45
+ const isCenterPosition = position.endsWith("center");
46
+ const isLeftPosition = position.endsWith("left");
47
+ const style = {
48
+ position: isPortal ? "fixed" : "absolute"
49
+ };
50
+ if (isTopPosition) {
51
+ style.top = normalizedOffset.y;
52
+ } else {
53
+ style.bottom = normalizedOffset.y;
54
+ }
55
+ if (isCenterPosition) {
56
+ style.left = "50%";
57
+ style.transform = "translateX(-50%)";
58
+ return style;
59
+ }
60
+ if (isLeftPosition) {
61
+ style.left = normalizedOffset.x;
62
+ } else {
63
+ style.right = normalizedOffset.x;
64
+ }
65
+ return style;
66
+ }
67
+
68
+ // src/lib/youtube.ts
69
+ var PLAYER_STATE_ENDED = 0;
70
+ var PLAYER_STATE_PLAYING = 1;
71
+ var PLAYER_STATE_PAUSED = 2;
72
+ var youtubeIframeApiPromise = null;
73
+ function canControlPlayer(player) {
74
+ return player !== null && typeof player.pauseVideo === "function" && typeof player.playVideo === "function";
75
+ }
76
+ function loadYouTubeIframeApi() {
77
+ if (typeof window === "undefined") {
78
+ return Promise.reject(new Error("YouTube iframe API requires a browser."));
79
+ }
80
+ if (window.YT?.Player) {
81
+ return Promise.resolve(window.YT);
82
+ }
83
+ if (youtubeIframeApiPromise) {
84
+ return youtubeIframeApiPromise;
85
+ }
86
+ youtubeIframeApiPromise = new Promise((resolve, reject) => {
87
+ const resetPromise = () => {
88
+ youtubeIframeApiPromise = null;
89
+ };
90
+ const existingScript = document.querySelector(
91
+ 'script[src="https://www.youtube.com/iframe_api"]'
92
+ );
93
+ const script = existingScript ?? document.createElement("script");
94
+ if (!existingScript) {
95
+ script.src = "https://www.youtube.com/iframe_api";
96
+ script.async = true;
97
+ document.head.append(script);
98
+ }
99
+ const handleError = () => {
100
+ resetPromise();
101
+ reject(new Error("Failed to load the YouTube iframe API."));
102
+ };
103
+ script.addEventListener("error", handleError, { once: true });
104
+ const previousReadyHandler = window.onYouTubeIframeAPIReady;
105
+ window.onYouTubeIframeAPIReady = () => {
106
+ previousReadyHandler?.();
107
+ script.removeEventListener("error", handleError);
108
+ if (!window.YT?.Player) {
109
+ resetPromise();
110
+ reject(new Error("YouTube iframe API loaded without a player."));
111
+ return;
112
+ }
113
+ resolve(window.YT);
114
+ };
115
+ });
116
+ return youtubeIframeApiPromise;
117
+ }
118
+
119
+ // src/hooks/useJukeboxPlayer.ts
120
+ function useJukeboxPlayer({
121
+ autoplay,
122
+ tracks
123
+ }) {
124
+ const playerRef = useRef(null);
125
+ const currentIndexRef = useRef(0);
126
+ const isPlayingRef = useRef(false);
127
+ const mutedPreferenceRef = useRef(true);
128
+ const shouldResumePlaybackRef = useRef(autoplay);
129
+ const tracksRef = useRef(tracks);
130
+ const volumeRef = useRef(DEFAULT_VOLUME);
131
+ const [playerMountNode, setPlayerMountNode] = useState(
132
+ null
133
+ );
134
+ const [currentIndex, setCurrentIndex] = useState(0);
135
+ const [isReady, setIsReady] = useState(false);
136
+ const [isPlaying, setIsPlaying] = useState(false);
137
+ const [isMuted, setIsMuted] = useState(true);
138
+ const [volume, setVolumeState] = useState(DEFAULT_VOLUME);
139
+ const trackCount = tracks.length;
140
+ const hasTracks = trackCount > 0;
141
+ const hasMultipleTracks = trackCount > 1;
142
+ const safeCurrentIndex = hasTracks ? Math.min(currentIndex, trackCount - 1) : 0;
143
+ const currentTrack = tracks[safeCurrentIndex];
144
+ const currentVideoId = currentTrack?.videoId;
145
+ const moveTrack = useCallback(
146
+ (step) => {
147
+ if (!hasMultipleTracks) {
148
+ return;
149
+ }
150
+ shouldResumePlaybackRef.current = isPlayingRef.current;
151
+ setCurrentIndex((index) => getNextTrackIndex(index, step, trackCount));
152
+ },
153
+ [hasMultipleTracks, trackCount]
154
+ );
155
+ const pausePlayback = useCallback(() => {
156
+ const player = playerRef.current;
157
+ shouldResumePlaybackRef.current = false;
158
+ if (!canControlPlayer(player)) {
159
+ setIsPlaying(false);
160
+ return;
161
+ }
162
+ player.pauseVideo();
163
+ setIsPlaying(false);
164
+ }, []);
165
+ useEffect(() => {
166
+ currentIndexRef.current = safeCurrentIndex;
167
+ }, [safeCurrentIndex]);
168
+ useEffect(() => {
169
+ tracksRef.current = tracks;
170
+ }, [tracks]);
171
+ useEffect(() => {
172
+ isPlayingRef.current = isPlaying;
173
+ }, [isPlaying]);
174
+ useEffect(() => {
175
+ volumeRef.current = volume;
176
+ }, [volume]);
177
+ useEffect(() => {
178
+ if (!isReady && !isPlayingRef.current) {
179
+ shouldResumePlaybackRef.current = autoplay;
180
+ }
181
+ }, [autoplay, isReady]);
182
+ useEffect(() => {
183
+ if (!playerMountNode || !hasTracks) {
184
+ return;
185
+ }
186
+ let isCancelled = false;
187
+ void loadYouTubeIframeApi().then((YT) => {
188
+ if (isCancelled) {
189
+ return;
190
+ }
191
+ playerRef.current = new YT.Player(playerMountNode, {
192
+ width: "100%",
193
+ height: "100%",
194
+ videoId: tracksRef.current[currentIndexRef.current]?.videoId ?? "",
195
+ playerVars: {
196
+ controls: 1,
197
+ origin: window.location.origin,
198
+ playsinline: 1,
199
+ rel: 0
200
+ },
201
+ events: {
202
+ onReady: () => {
203
+ const player = playerRef.current;
204
+ if (!player) {
205
+ return;
206
+ }
207
+ player.setVolume(volumeRef.current);
208
+ if (mutedPreferenceRef.current) {
209
+ player.mute();
210
+ } else {
211
+ player.unMute();
212
+ }
213
+ setIsMuted(mutedPreferenceRef.current);
214
+ setIsReady(true);
215
+ },
216
+ onStateChange: (event) => {
217
+ if (event.data === PLAYER_STATE_ENDED) {
218
+ const nextTrackCount = tracksRef.current.length;
219
+ if (nextTrackCount <= 1) {
220
+ shouldResumePlaybackRef.current = false;
221
+ setIsPlaying(false);
222
+ return;
223
+ }
224
+ shouldResumePlaybackRef.current = true;
225
+ setCurrentIndex(
226
+ (index) => getNextTrackIndex(index, 1, nextTrackCount)
227
+ );
228
+ return;
229
+ }
230
+ if (event.data === PLAYER_STATE_PLAYING) {
231
+ setIsPlaying(true);
232
+ return;
233
+ }
234
+ if (event.data === PLAYER_STATE_PAUSED) {
235
+ setIsPlaying(false);
236
+ }
237
+ },
238
+ onError: () => {
239
+ shouldResumePlaybackRef.current = false;
240
+ setIsPlaying(false);
241
+ }
242
+ }
243
+ });
244
+ }).catch(() => {
245
+ setIsReady(false);
246
+ setIsPlaying(false);
247
+ });
248
+ return () => {
249
+ isCancelled = true;
250
+ setIsReady(false);
251
+ setIsPlaying(false);
252
+ playerRef.current?.destroy();
253
+ playerRef.current = null;
254
+ };
255
+ }, [hasTracks, playerMountNode]);
256
+ useEffect(() => {
257
+ const player = playerRef.current;
258
+ if (!isReady || !player || !currentVideoId) {
259
+ return;
260
+ }
261
+ if (shouldResumePlaybackRef.current) {
262
+ player.loadVideoById(currentVideoId);
263
+ return;
264
+ }
265
+ player.cueVideoById(currentVideoId);
266
+ }, [currentVideoId, isReady]);
267
+ return {
268
+ playerMountRef: setPlayerMountNode,
269
+ currentIndex: safeCurrentIndex,
270
+ isMuted,
271
+ isPlaying,
272
+ volume,
273
+ setVolume: useCallback((nextVolume) => {
274
+ const clampedVolume = clampVolume(nextVolume);
275
+ const player = playerRef.current;
276
+ setVolumeState(clampedVolume);
277
+ volumeRef.current = clampedVolume;
278
+ if (clampedVolume === 0) {
279
+ mutedPreferenceRef.current = true;
280
+ setIsMuted(true);
281
+ } else {
282
+ mutedPreferenceRef.current = false;
283
+ setIsMuted(false);
284
+ }
285
+ if (!player) {
286
+ return;
287
+ }
288
+ player.setVolume(clampedVolume);
289
+ if (clampedVolume === 0) {
290
+ player.mute();
291
+ return;
292
+ }
293
+ player.unMute();
294
+ }, []),
295
+ toggleMute: useCallback(() => {
296
+ const player = playerRef.current;
297
+ const nextMuted = !mutedPreferenceRef.current;
298
+ mutedPreferenceRef.current = nextMuted;
299
+ setIsMuted(nextMuted);
300
+ if (!player) {
301
+ return;
302
+ }
303
+ if (nextMuted) {
304
+ player.mute();
305
+ return;
306
+ }
307
+ player.unMute();
308
+ }, []),
309
+ togglePlay: useCallback(() => {
310
+ const player = playerRef.current;
311
+ if (!canControlPlayer(player) || !isReady || !hasTracks) {
312
+ return;
313
+ }
314
+ if (isPlayingRef.current) {
315
+ pausePlayback();
316
+ return;
317
+ }
318
+ shouldResumePlaybackRef.current = true;
319
+ player.playVideo();
320
+ }, [hasTracks, isReady, pausePlayback]),
321
+ playNext: useCallback(() => {
322
+ moveTrack(1);
323
+ }, [moveTrack]),
324
+ playPrev: useCallback(() => {
325
+ moveTrack(-1);
326
+ }, [moveTrack])
327
+ };
328
+ }
329
+ function SpeakerIcon({ isMuted }) {
330
+ if (isMuted) {
331
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: [
332
+ /* @__PURE__ */ jsx("path", { d: "M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z" }),
333
+ /* @__PURE__ */ jsx("path", { d: "M10.28 5.22a.75.75 0 0 1 1.06 0L12 5.88l.66-.66a.75.75 0 1 1 1.06 1.06l-.66.66.66.66a.75.75 0 1 1-1.06 1.06L12 7.94l-.66.66a.75.75 0 0 1-1.06-1.06l.66-.66-.66-.66a.75.75 0 0 1 0-1.06Z" })
334
+ ] });
335
+ }
336
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: [
337
+ /* @__PURE__ */ jsx("path", { d: "M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z" }),
338
+ /* @__PURE__ */ jsx("path", { d: "M10.5 5.02a.75.75 0 0 1 1.06 0 3.86 3.86 0 0 1 0 5.46.75.75 0 1 1-1.06-1.06 2.36 2.36 0 0 0 0-3.34.75.75 0 0 1 0-1.06Z" })
339
+ ] });
340
+ }
341
+ function JukeboxExpandedPlayer({
342
+ currentIndex,
343
+ isMuted,
344
+ isPlaying,
345
+ nextTrack,
346
+ playerMountRef,
347
+ totalTracks,
348
+ volume,
349
+ playNext,
350
+ playPrev,
351
+ togglePlay,
352
+ toggleMute,
353
+ setVolume
354
+ }) {
355
+ const hasMultipleTracks = totalTracks > 1;
356
+ const hasNextTrack = nextTrack !== void 0;
357
+ const handleVolumeInput = (event) => {
358
+ setVolume(Number(event.target.value));
359
+ };
360
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
361
+ /* @__PURE__ */ jsxs("div", { className: "rj-expanded__shell", children: [
362
+ /* @__PURE__ */ jsx("div", { className: "rj-expanded__screen-frame", children: /* @__PURE__ */ jsx("div", { className: "rj-expanded__screen", children: /* @__PURE__ */ jsx("div", { ref: playerMountRef, className: "rj-expanded__player" }) }) }),
363
+ /* @__PURE__ */ jsx("div", { className: "rj-expanded__meta", children: /* @__PURE__ */ jsxs("div", { className: "rj-expanded__controls", children: [
364
+ /* @__PURE__ */ jsxs("div", { className: "rj-expanded__transport", children: [
365
+ /* @__PURE__ */ jsx(
366
+ "button",
367
+ {
368
+ type: "button",
369
+ onClick: playPrev,
370
+ disabled: !hasMultipleTracks,
371
+ className: "rj-chip-button",
372
+ children: "\u25C0"
373
+ }
374
+ ),
375
+ /* @__PURE__ */ jsx(
376
+ "button",
377
+ {
378
+ type: "button",
379
+ onClick: togglePlay,
380
+ className: "rj-chip-button rj-chip-button--primary",
381
+ children: isPlaying ? "Pause" : "Play"
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsx(
385
+ "button",
386
+ {
387
+ type: "button",
388
+ onClick: playNext,
389
+ disabled: !hasMultipleTracks,
390
+ className: "rj-chip-button",
391
+ children: "\u25B6"
392
+ }
393
+ )
394
+ ] }),
395
+ /* @__PURE__ */ jsxs("div", { className: "rj-expanded__utility", children: [
396
+ /* @__PURE__ */ jsx(
397
+ "button",
398
+ {
399
+ type: "button",
400
+ onClick: toggleMute,
401
+ "aria-label": isMuted ? "Unmute" : "Mute",
402
+ className: "rj-icon-button",
403
+ children: /* @__PURE__ */ jsx("span", { className: "rj-icon-button__icon", children: /* @__PURE__ */ jsx(SpeakerIcon, { isMuted }) })
404
+ }
405
+ ),
406
+ /* @__PURE__ */ jsx(
407
+ "input",
408
+ {
409
+ type: "range",
410
+ min: 0,
411
+ max: 100,
412
+ step: 1,
413
+ value: volume,
414
+ onChange: handleVolumeInput,
415
+ "aria-label": "Volume",
416
+ className: "rj-volume"
417
+ }
418
+ ),
419
+ hasMultipleTracks ? /* @__PURE__ */ jsxs("span", { className: "rj-expanded__counter", children: [
420
+ currentIndex + 1,
421
+ " / ",
422
+ totalTracks
423
+ ] }) : null
424
+ ] })
425
+ ] }) })
426
+ ] }),
427
+ hasNextTrack ? /* @__PURE__ */ jsxs("div", { className: "rj-next-track", children: [
428
+ /* @__PURE__ */ jsx("span", { className: "rj-next-track__label", children: "Next" }),
429
+ /* @__PURE__ */ jsx(
430
+ "button",
431
+ {
432
+ type: "button",
433
+ onClick: playNext,
434
+ disabled: !hasMultipleTracks,
435
+ className: "rj-next-track__button",
436
+ children: nextTrack.title
437
+ }
438
+ )
439
+ ] }) : null
440
+ ] });
441
+ }
442
+
443
+ // #style-inject:#style-inject
444
+ function styleInject(css, { insertAt } = {}) {
445
+ if (typeof document === "undefined") return;
446
+ const head = document.head || document.getElementsByTagName("head")[0];
447
+ const style = document.createElement("style");
448
+ style.type = "text/css";
449
+ if (insertAt === "top") {
450
+ if (head.firstChild) {
451
+ head.insertBefore(style, head.firstChild);
452
+ } else {
453
+ head.appendChild(style);
454
+ }
455
+ } else {
456
+ head.appendChild(style);
457
+ }
458
+ if (style.styleSheet) {
459
+ style.styleSheet.cssText = css;
460
+ } else {
461
+ style.appendChild(document.createTextNode(css));
462
+ }
463
+ }
464
+
465
+ // src/styles/jukebox.css
466
+ styleInject('.rj-root {\n --rj-text: #111827;\n --rj-text-muted: #667085;\n --rj-border: rgba(255, 255, 255, 0.55);\n --rj-panel: rgba(255, 255, 255, 0.84);\n --rj-shadow: 0 18px 44px -22px rgba(15, 23, 42, 0.28);\n --rj-gradient:\n linear-gradient(\n 180deg,\n #ffffff 0%,\n #e3eaf2 100%);\n --rj-gradient-soft:\n linear-gradient(\n 180deg,\n #f8fafc 0%,\n #e7edf4 100%);\n --rj-button-text: #334155;\n --rj-dock-bg: rgba(255, 255, 255, 0.82);\n --rj-dock-shadow: 0 -8px 22px -16px rgba(15, 23, 42, 0.24), 0 10px 28px -20px rgba(15, 23, 42, 0.2);\n --rj-dock-inner-bg:\n linear-gradient(\n \n 180deg,\n rgba(255, 255, 255, 0.84) 0%,\n rgba(241, 245, 249, 0.94) 100% );\n --rj-track-hover-bg: rgba(255, 255, 255, 0.55);\n --rj-level-gradient:\n linear-gradient(\n \n 180deg,\n #f8fafc 0%,\n #cbd5e1 45%,\n #64748b 100% );\n --rj-level-shadow-color: rgba(148, 163, 184, 0.14);\n --rj-icon-hover-bg: rgba(15, 23, 42, 0.04);\n --rj-shell-border: rgba(0, 0, 0, 0.06);\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82);\n --rj-screen-frame-border: #c7d0da;\n --rj-screen-frame-bg:\n linear-gradient(\n 180deg,\n #fbfdff 0%,\n #e9eef4 100%);\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.84);\n --rj-screen-border: #9aa7b8;\n --rj-screen-bg: #0f172a;\n --rj-player-bg:\n radial-gradient(\n circle at top,\n rgba(45, 212, 191, 0.15),\n transparent 45%),\n linear-gradient(\n 180deg,\n rgba(15, 23, 42, 0.96) 0%,\n rgba(2, 6, 23, 1) 100%);\n --rj-chip-border: #b4bfcc;\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\n --rj-chip-primary-border: #a7b3c2;\n --rj-chip-primary-text: #1e293b;\n --rj-volume-track: #cbd5e1;\n --rj-volume-thumb: #334155;\n --rj-volume-thumb-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);\n --rj-expanded-icon: #475569;\n --rj-expanded-icon-hover: #1e293b;\n --rj-next-border: #c3cad5;\n --rj-next-bg:\n linear-gradient(\n 180deg,\n #f8fafc 0%,\n #e8edf3 100%);\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\n --rj-font-family:\n Inter,\n "Segoe UI",\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n sans-serif;\n --rj-root-width: min(296px, calc(100vw - 16px));\n --rj-root-expanded-width: min(296px, calc(100vw - 16px));\n --rj-dock-radius: 22px;\n --rj-dock-padding: 6px;\n --rj-dock-backdrop: blur(18px) saturate(1.5);\n --rj-dock-inner-radius: 18px;\n --rj-dock-inner-gap: 6px;\n --rj-dock-inner-padding-right: 6px;\n --rj-track-summary-radius: 18px;\n --rj-track-summary-padding: 10px 12px;\n --rj-expanded-radius: 22px;\n --rj-expanded-padding: 8px;\n --rj-expanded-backdrop: blur(18px) saturate(1.5);\n --rj-shell-radius: 18px;\n --rj-shell-padding: 12px;\n --rj-screen-frame-radius: 15px;\n --rj-screen-frame-padding: 8px;\n --rj-screen-radius: 12px;\n --rj-screen-padding: 8px;\n --rj-player-radius: 10px;\n --rj-chip-radius: 9999px;\n --rj-chip-padding: 7px 10px;\n --rj-volume-width: 72px;\n --rj-expanded-control-gap: 8px;\n --rj-next-radius: 16px;\n --rj-next-padding: 10px 12px;\n color: var(--rj-text);\n z-index: 50;\n width: var(--rj-root-width);\n overflow: visible;\n font-family: var(--rj-font-family);\n}\n.rj-root[data-theme=simple] {\n --rj-text: #171717;\n --rj-text-muted: #737373;\n --rj-border: rgba(23, 23, 23, 0.1);\n --rj-panel: #ffffff;\n --rj-shadow: 0 16px 40px -28px rgba(23, 23, 23, 0.24);\n --rj-gradient:\n linear-gradient(\n 180deg,\n #ffffff 0%,\n #f5f5f5 100%);\n --rj-gradient-soft:\n linear-gradient(\n 180deg,\n #fafafa 0%,\n #f4f4f5 100%);\n --rj-button-text: #262626;\n --rj-dock-bg: #ffffff;\n --rj-dock-shadow: 0 14px 32px -28px rgba(23, 23, 23, 0.2);\n --rj-dock-inner-bg: #ffffff;\n --rj-track-hover-bg: #f5f5f5;\n --rj-level-gradient:\n linear-gradient(\n \n 180deg,\n #fafafa 0%,\n #d4d4d8 52%,\n #52525b 100% );\n --rj-level-shadow-color: rgba(82, 82, 91, 0.12);\n --rj-icon-hover-bg: rgba(23, 23, 23, 0.05);\n --rj-shell-border: rgba(23, 23, 23, 0.08);\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\n --rj-screen-frame-border: #d4d4d8;\n --rj-screen-frame-bg:\n linear-gradient(\n 180deg,\n #ffffff 0%,\n #f4f4f5 100%);\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\n --rj-screen-border: #a1a1aa;\n --rj-screen-bg: #18181b;\n --rj-player-bg:\n radial-gradient(\n circle at top,\n rgba(255, 255, 255, 0.08),\n transparent 40%),\n linear-gradient(\n 180deg,\n #27272a 0%,\n #09090b 100%);\n --rj-chip-border: #d4d4d8;\n --rj-chip-shadow: none;\n --rj-chip-primary-border: #171717;\n --rj-chip-primary-text: #171717;\n --rj-volume-track: #d4d4d8;\n --rj-volume-thumb: #171717;\n --rj-volume-thumb-shadow: none;\n --rj-expanded-icon: #52525b;\n --rj-expanded-icon-hover: #171717;\n --rj-next-border: #e4e4e7;\n --rj-next-bg:\n linear-gradient(\n 180deg,\n #fafafa 0%,\n #f4f4f5 100%);\n --rj-next-shadow: none;\n}\n.rj-root[data-theme=simple][data-chrome=ride] .rj-expanded .rj-icon-button {\n width: 24px;\n height: 24px;\n border-radius: 9999px;\n background: transparent;\n}\n.rj-root[data-theme=simple][data-chrome=ride] .rj-expanded .rj-icon-button__icon {\n width: 12px;\n height: 12px;\n}\n.rj-root[data-theme=simple][data-chrome=wallet] .rj-expanded__utility {\n gap: 8px;\n}\n.rj-root[data-theme=simple][data-chrome=wallet] .rj-expanded .rj-icon-button {\n margin-left: 6px;\n}\n.rj-root[data-theme=sunset] {\n --rj-text: #4a1635;\n --rj-text-muted: #8b5b6d;\n --rj-border: rgba(255, 255, 255, 0.3);\n --rj-panel: rgba(255, 244, 237, 0.86);\n --rj-shadow: 0 22px 48px -24px rgba(159, 18, 57, 0.28);\n --rj-gradient:\n linear-gradient(\n 135deg,\n #fff0d8 0%,\n #ffc7a1 48%,\n #ff9bb2 100%);\n --rj-gradient-soft:\n linear-gradient(\n \n 180deg,\n rgba(255, 248, 240, 0.96) 0%,\n rgba(255, 225, 213, 0.96) 100% );\n --rj-button-text: #7a284f;\n --rj-dock-bg: rgba(255, 241, 232, 0.82);\n --rj-dock-shadow: 0 -8px 22px -16px rgba(190, 24, 93, 0.22), 0 12px 30px -18px rgba(249, 115, 22, 0.24);\n --rj-dock-inner-bg:\n linear-gradient(\n \n 135deg,\n rgba(255, 249, 240, 0.96) 0%,\n rgba(255, 228, 212, 0.96) 100% );\n --rj-track-hover-bg: rgba(255, 255, 255, 0.45);\n --rj-level-gradient:\n linear-gradient(\n \n 180deg,\n #fff7ed 0%,\n #fb7185 48%,\n #7c3aed 100% );\n --rj-level-shadow-color: rgba(251, 113, 133, 0.2);\n --rj-icon-hover-bg: rgba(190, 24, 93, 0.08);\n --rj-shell-border: rgba(244, 114, 182, 0.15);\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72);\n --rj-screen-frame-border: #f9a8d4;\n --rj-screen-frame-bg:\n linear-gradient(\n 180deg,\n #fff1f2 0%,\n #ffe4e6 100%);\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.84);\n --rj-screen-border: #be185d;\n --rj-screen-bg: #431407;\n --rj-player-bg:\n radial-gradient(\n circle at top,\n rgba(253, 224, 71, 0.18),\n transparent 40%),\n radial-gradient(\n \n circle at 70% 30%,\n rgba(244, 114, 182, 0.18),\n transparent 35% ),\n linear-gradient(\n 180deg,\n #7c2d12 0%,\n #4c0519 100%);\n --rj-chip-border: rgba(244, 114, 182, 0.28);\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72);\n --rj-chip-primary-border: rgba(190, 24, 93, 0.16);\n --rj-chip-primary-text: #5b1032;\n --rj-volume-track: #fda4af;\n --rj-volume-thumb: #9f1239;\n --rj-volume-thumb-shadow: 0 1px 6px rgba(159, 18, 57, 0.24);\n --rj-expanded-icon: #9f1239;\n --rj-expanded-icon-hover: #831843;\n --rj-next-border: rgba(244, 114, 182, 0.2);\n --rj-next-bg:\n linear-gradient(\n 180deg,\n #fff1f2 0%,\n #ffe4e6 100%);\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82);\n}\n.rj-root[data-theme=ride] {\n --rj-text: #f8fafc;\n --rj-text-muted: rgba(226, 232, 240, 0.7);\n --rj-border: rgba(255, 255, 255, 0.1);\n --rj-panel: rgba(3, 7, 18, 0.96);\n --rj-shadow: 0 24px 54px -34px rgba(2, 6, 23, 0.72);\n --rj-gradient:\n linear-gradient(\n 180deg,\n #f1f5f9 0%,\n #d8e1ea 100%);\n --rj-gradient-soft:\n linear-gradient(\n \n 180deg,\n rgba(15, 23, 42, 0.96) 0%,\n rgba(2, 6, 23, 0.98) 100% );\n --rj-button-text: #f8fafc;\n --rj-dock-bg: #050816;\n --rj-dock-shadow: 0 24px 54px -34px rgba(2, 6, 23, 0.72);\n --rj-dock-inner-bg:\n linear-gradient(\n \n 180deg,\n rgba(255, 255, 255, 0.03) 0%,\n rgba(255, 255, 255, 0) 100% );\n --rj-track-hover-bg: rgba(255, 255, 255, 0.06);\n --rj-level-gradient:\n linear-gradient(\n \n 180deg,\n #f8fafc 0%,\n #cbd5e1 45%,\n #64748b 100% );\n --rj-level-shadow-color: rgba(148, 163, 184, 0.14);\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.08);\n --rj-shell-border: rgba(255, 255, 255, 0.08);\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);\n --rj-screen-frame-border: rgba(148, 163, 184, 0.24);\n --rj-screen-frame-bg:\n linear-gradient(\n \n 180deg,\n rgba(30, 41, 59, 0.92) 0%,\n rgba(15, 23, 42, 0.98) 100% );\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);\n --rj-screen-border: rgba(100, 116, 139, 0.4);\n --rj-screen-bg: #050816;\n --rj-player-bg:\n radial-gradient(\n circle at top,\n rgba(255, 255, 255, 0.08),\n transparent 40%),\n linear-gradient(\n 180deg,\n rgba(15, 23, 42, 0.96) 0%,\n rgba(2, 6, 23, 1) 100%);\n --rj-chip-border: rgba(191, 219, 254, 0.58);\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\n --rj-chip-primary-border: rgba(255, 255, 255, 0.38);\n --rj-chip-primary-text: #0f172a;\n --rj-volume-track: rgba(148, 163, 184, 0.38);\n --rj-volume-thumb: #ffffff;\n --rj-volume-thumb-shadow: none;\n --rj-expanded-icon: rgba(226, 232, 240, 0.82);\n --rj-expanded-icon-hover: #ffffff;\n --rj-next-border: rgba(255, 255, 255, 0.1);\n --rj-next-bg:\n linear-gradient(\n \n 180deg,\n rgba(15, 23, 42, 0.96) 0%,\n rgba(2, 6, 23, 0.98) 100% );\n --rj-next-shadow: none;\n}\n.rj-root[data-chrome=wallet] {\n --rj-dock-radius: 26px;\n --rj-dock-padding: 6px;\n --rj-dock-inner-radius: 20px;\n --rj-dock-inner-gap: 8px;\n --rj-dock-inner-padding-right: 0;\n --rj-track-summary-radius: 20px;\n --rj-track-summary-padding: 12px;\n --rj-expanded-radius: 26px;\n --rj-expanded-padding: 8px;\n --rj-shell-radius: 20px;\n --rj-shell-padding: 12px;\n --rj-screen-frame-radius: 18px;\n --rj-screen-frame-padding: 7px;\n --rj-screen-radius: 14px;\n --rj-screen-padding: 7px;\n --rj-player-radius: 12px;\n --rj-chip-radius: 14px;\n --rj-chip-padding: 7px 10px;\n --rj-volume-width: 68px;\n --rj-expanded-control-gap: 8px;\n --rj-next-radius: 16px;\n --rj-next-padding: 9px 12px;\n}\n.rj-root[data-chrome=ride] {\n --rj-font-family:\n "IBM Plex Sans",\n "Segoe UI",\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n sans-serif;\n --rj-dock-radius: 18px;\n --rj-dock-padding: 4px;\n --rj-dock-backdrop: none;\n --rj-dock-inner-radius: 14px;\n --rj-dock-inner-gap: 4px;\n --rj-dock-inner-padding-right: 4px;\n --rj-track-summary-radius: 12px;\n --rj-track-summary-padding: 12px;\n --rj-expanded-radius: 18px;\n --rj-expanded-padding: 6px;\n --rj-expanded-backdrop: none;\n --rj-shell-radius: 14px;\n --rj-shell-padding: 12px;\n --rj-screen-frame-radius: 12px;\n --rj-screen-frame-padding: 6px;\n --rj-screen-radius: 10px;\n --rj-screen-padding: 6px;\n --rj-player-radius: 8px;\n --rj-chip-radius: 12px;\n --rj-chip-padding: 10px 12px;\n --rj-volume-width: 88px;\n --rj-expanded-control-gap: 6px;\n --rj-next-radius: 14px;\n --rj-next-padding: 10px 12px;\n}\n.rj-root[data-theme=glass][data-chrome=wallet] {\n --rj-text: #0f172a;\n --rj-text-muted: #64748b;\n --rj-border: rgba(191, 219, 254, 0.72);\n --rj-panel: rgba(245, 249, 255, 0.96);\n --rj-shadow: 0 22px 46px -28px rgba(37, 99, 235, 0.22);\n --rj-gradient:\n linear-gradient(\n 135deg,\n #eff6ff 0%,\n #dbeafe 100%);\n --rj-gradient-soft:\n linear-gradient(\n 180deg,\n #ffffff 0%,\n #eef4ff 100%);\n --rj-button-text: #1e3a8a;\n --rj-dock-bg: rgba(231, 240, 255, 0.86);\n --rj-dock-shadow: 0 16px 36px -28px rgba(59, 130, 246, 0.26), inset 0 1px 0 rgba(255, 255, 255, 0.92);\n --rj-dock-inner-bg: transparent;\n --rj-track-hover-bg: rgba(219, 234, 254, 0.58);\n --rj-level-gradient:\n linear-gradient(\n \n 180deg,\n #60a5fa 0%,\n #2563eb 60%,\n #1d4ed8 100% );\n --rj-level-shadow-color: rgba(96, 165, 250, 0.2);\n --rj-icon-hover-bg: rgba(59, 130, 246, 0.1);\n --rj-shell-border: rgba(191, 219, 254, 0.88);\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96);\n --rj-screen-frame-border: #bfdbfe;\n --rj-screen-frame-bg:\n linear-gradient(\n 180deg,\n #ffffff 0%,\n #eff6ff 100%);\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96);\n --rj-screen-border: #93c5fd;\n --rj-screen-bg: #dbeafe;\n --rj-player-bg:\n radial-gradient(\n circle at top,\n rgba(255, 255, 255, 0.48),\n transparent 48%),\n linear-gradient(\n 180deg,\n #bfdbfe 0%,\n #60a5fa 100%);\n --rj-chip-border: rgba(147, 197, 253, 0.9);\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.92);\n --rj-chip-primary-border: rgba(59, 130, 246, 0.28);\n --rj-chip-primary-text: #1e3a8a;\n --rj-volume-track: #bfdbfe;\n --rj-volume-thumb: #2563eb;\n --rj-volume-thumb-shadow: 0 2px 8px rgba(37, 99, 235, 0.18);\n --rj-expanded-icon: #2563eb;\n --rj-expanded-icon-hover: #1d4ed8;\n --rj-next-border: rgba(147, 197, 253, 0.72);\n --rj-next-bg:\n linear-gradient(\n 180deg,\n #ffffff 0%,\n #eff6ff 100%);\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96);\n}\n.rj-root--inline {\n z-index: 20;\n width: min(100%, var(--rj-root-width));\n max-width: 100%;\n}\n.rj-dock {\n border: 1px solid var(--rj-border);\n border-radius: var(--rj-dock-radius);\n background: var(--rj-dock-bg);\n padding: var(--rj-dock-padding);\n box-shadow: var(--rj-dock-shadow);\n backdrop-filter: var(--rj-dock-backdrop);\n}\n.rj-root--expanded .rj-dock {\n width: var(--rj-root-expanded-width);\n max-width: calc(100vw - 16px);\n}\n.rj-root--inline.rj-root--expanded .rj-dock {\n width: min(100%, var(--rj-root-expanded-width));\n max-width: 100%;\n}\n.rj-root[data-position$=right].rj-root--expanded .rj-dock {\n margin-left: calc(var(--rj-root-width) - var(--rj-root-expanded-width));\n}\n.rj-dock__inner {\n display: flex;\n align-items: center;\n gap: var(--rj-dock-inner-gap);\n border-radius: var(--rj-dock-inner-radius);\n background: var(--rj-dock-inner-bg);\n padding-right: var(--rj-dock-inner-padding-right);\n}\n.rj-track-summary {\n display: flex;\n min-width: 0;\n flex: 1;\n align-items: center;\n gap: 10px;\n border: 0;\n background: transparent;\n border-radius: var(--rj-track-summary-radius);\n padding: var(--rj-track-summary-padding);\n text-align: left;\n color: inherit;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.rj-track-summary:hover {\n background: var(--rj-track-hover-bg);\n}\n.rj-track-summary--empty {\n cursor: default;\n}\n.rj-track-summary--empty:hover {\n background: transparent;\n}\n.rj-level-meter {\n display: flex;\n width: 28px;\n height: 28px;\n flex-shrink: 0;\n align-items: flex-end;\n justify-content: center;\n gap: 4px;\n}\n.rj-level-bar {\n width: 4px;\n border-radius: 9999px;\n background: var(--rj-level-gradient);\n box-shadow: 0 0 0 1px var(--rj-level-shadow-color);\n opacity: 0.55;\n transform-origin: bottom center;\n}\n.rj-level-bar--playing {\n opacity: 1;\n animation: rj-jukebox-bounce 0.9s ease-in-out infinite;\n}\n.rj-track-summary__copy {\n min-width: 0;\n flex: 1;\n}\n.rj-track-summary__title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: -0.01em;\n}\n.rj-track-summary__artist {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n color: var(--rj-text-muted);\n}\n.rj-chevron {\n display: inline-flex;\n height: 16px;\n width: 16px;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n color: var(--rj-text-muted);\n}\n.rj-chevron__icon {\n display: block;\n height: 100%;\n width: 100%;\n transform: rotate(0deg);\n transition: transform 0.2s ease;\n}\n.rj-chevron__icon--expanded {\n transform: rotate(180deg);\n}\n.rj-icon-button {\n display: inline-flex;\n height: 32px;\n width: 32px;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border: 0;\n border-radius: 9999px;\n background: transparent;\n color: var(--rj-text-muted);\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n color 0.2s ease,\n opacity 0.2s ease;\n}\n.rj-icon-button:hover {\n background: var(--rj-icon-hover-bg);\n color: var(--rj-text);\n}\n.rj-icon-button:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n}\n.rj-icon-button__icon {\n display: inline-flex;\n height: 16px;\n width: 16px;\n}\n.rj-icon-button__icon svg {\n display: block;\n height: 100%;\n width: 100%;\n}\n.rj-expanded {\n position: absolute;\n width: var(--rj-root-expanded-width);\n max-width: calc(100vw - 16px);\n right: 0;\n bottom: calc(100% + 10px);\n left: auto;\n z-index: 2;\n border: 1px solid var(--rj-border);\n border-radius: var(--rj-expanded-radius);\n background: var(--rj-panel);\n padding: var(--rj-expanded-padding);\n box-shadow: var(--rj-shadow);\n backdrop-filter: var(--rj-expanded-backdrop);\n}\n.rj-root--inline .rj-expanded {\n width: min(100%, var(--rj-root-expanded-width));\n max-width: 100%;\n}\n.rj-root[data-position$=left] .rj-expanded {\n right: auto;\n left: 0;\n}\n.rj-expanded--hidden {\n visibility: hidden;\n pointer-events: none;\n opacity: 0;\n}\n.rj-root[data-position^=top] .rj-expanded {\n top: calc(100% + 10px);\n bottom: auto;\n}\n.rj-expanded__shell {\n border-radius: var(--rj-shell-radius);\n border: 1px solid var(--rj-shell-border);\n background: var(--rj-gradient-soft);\n padding: var(--rj-shell-padding);\n box-shadow: var(--rj-shell-inset-shadow);\n}\n.rj-expanded__screen-frame {\n border-radius: var(--rj-screen-frame-radius);\n border: 1px solid var(--rj-screen-frame-border);\n background: var(--rj-screen-frame-bg);\n padding: var(--rj-screen-frame-padding);\n box-shadow: var(--rj-screen-frame-shadow);\n}\n.rj-expanded__screen {\n border-radius: var(--rj-screen-radius);\n border: 1px solid var(--rj-screen-border);\n background: var(--rj-screen-bg);\n padding: var(--rj-screen-padding);\n}\n.rj-expanded__player {\n overflow: hidden;\n width: 100%;\n aspect-ratio: 16 / 9;\n border-radius: var(--rj-player-radius);\n background: var(--rj-player-bg);\n}\n.rj-expanded__meta {\n margin-top: 12px;\n}\n.rj-expanded__titles {\n margin-bottom: 10px;\n}\n.rj-expanded__title {\n font-size: 13px;\n font-weight: 700;\n}\n.rj-expanded__artist {\n margin-top: 2px;\n font-size: 11px;\n color: var(--rj-text-muted);\n}\n.rj-expanded__controls {\n display: flex;\n flex-wrap: nowrap;\n min-width: 0;\n align-items: center;\n gap: var(--rj-expanded-control-gap);\n}\n.rj-expanded__transport,\n.rj-expanded__utility {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: var(--rj-expanded-control-gap);\n}\n.rj-expanded__transport {\n flex: 0 0 auto;\n}\n.rj-expanded__utility {\n flex: 1 1 auto;\n margin-left: auto;\n justify-content: flex-end;\n}\n.rj-chip-button {\n border: 1px solid var(--rj-chip-border);\n border-radius: var(--rj-chip-radius);\n background: var(--rj-gradient-soft);\n padding: var(--rj-chip-padding);\n color: var(--rj-button-text);\n font-size: 10px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: var(--rj-chip-shadow);\n transition: transform 0.2s ease, opacity 0.2s ease;\n}\n.rj-chip-button:hover {\n transform: translateY(-1px);\n}\n.rj-chip-button:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n transform: none;\n}\n.rj-chip-button--primary {\n border-color: var(--rj-chip-primary-border);\n background: var(--rj-gradient);\n color: var(--rj-chip-primary-text);\n font-weight: 700;\n}\n.rj-volume {\n min-width: 0;\n width: var(--rj-volume-width);\n flex: 1 1 var(--rj-volume-width);\n height: 4px;\n cursor: pointer;\n appearance: none;\n border-radius: 9999px;\n background: var(--rj-volume-track);\n outline: none;\n}\n.rj-volume::-webkit-slider-runnable-track {\n height: 4px;\n border-radius: 9999px;\n background: var(--rj-volume-track);\n}\n.rj-volume::-webkit-slider-thumb {\n width: 12px;\n height: 12px;\n margin-top: -4px;\n appearance: none;\n border: 0;\n border-radius: 9999px;\n background: var(--rj-volume-thumb);\n box-shadow: var(--rj-volume-thumb-shadow);\n}\n.rj-volume::-moz-range-track {\n height: 4px;\n border-radius: 9999px;\n background: var(--rj-volume-track);\n}\n.rj-volume::-moz-range-thumb {\n width: 12px;\n height: 12px;\n border: 0;\n border-radius: 9999px;\n background: var(--rj-volume-thumb);\n box-shadow: var(--rj-volume-thumb-shadow);\n}\n.rj-expanded .rj-icon-button {\n margin-left: 4px;\n height: 24px;\n width: 24px;\n color: var(--rj-expanded-icon);\n}\n.rj-expanded .rj-icon-button:hover {\n background: transparent;\n color: var(--rj-expanded-icon-hover);\n}\n.rj-expanded .rj-icon-button__icon {\n height: 12px;\n width: 12px;\n}\n.rj-expanded__counter {\n margin-left: auto;\n flex-shrink: 0;\n font-size: 10px;\n color: var(--rj-text-muted);\n white-space: nowrap;\n font-family:\n "SF Mono",\n SFMono-Regular,\n ui-monospace,\n Menlo,\n Consolas,\n monospace;\n}\n.rj-next-track {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 10px;\n border-radius: var(--rj-next-radius);\n border: 1px solid var(--rj-next-border);\n background: var(--rj-next-bg);\n padding: var(--rj-next-padding);\n font-size: 10px;\n color: var(--rj-text-muted);\n box-shadow: var(--rj-next-shadow);\n}\n.rj-next-track__label {\n font-weight: 700;\n}\n.rj-next-track__button {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n border: 0;\n background: transparent;\n color: var(--rj-button-text);\n cursor: pointer;\n}\n.rj-next-track__button:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n}\n.rj-root[data-chrome=wallet] .rj-dock {\n border-color: var(--rj-border);\n}\n.rj-root[data-chrome=wallet] .rj-dock__inner {\n align-items: center;\n}\n.rj-root[data-chrome=wallet] .rj-track-summary {\n border: 1px solid var(--rj-border);\n background: var(--rj-gradient-soft);\n box-shadow: var(--rj-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.92);\n}\n.rj-root[data-chrome=wallet] .rj-level-meter {\n width: 32px;\n height: 32px;\n border-radius: 14px;\n background: var(--rj-gradient);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.94);\n}\n.rj-root[data-chrome=wallet] .rj-chevron {\n width: 22px;\n height: 22px;\n border-radius: 9999px;\n background: var(--rj-track-hover-bg);\n}\n.rj-root[data-chrome=wallet] .rj-track-summary__title {\n font-size: 13px;\n letter-spacing: -0.02em;\n}\n.rj-root[data-chrome=wallet] .rj-track-summary__artist {\n margin-top: 3px;\n font-size: 10px;\n}\n.rj-root[data-chrome=wallet] .rj-dock .rj-icon-button {\n width: 34px;\n height: 34px;\n border: 1px solid var(--rj-border);\n border-radius: 14px;\n background: var(--rj-gradient-soft);\n box-shadow: var(--rj-chip-shadow);\n}\n.rj-root[data-chrome=wallet] .rj-expanded__meta {\n margin-top: 10px;\n}\n.rj-root[data-chrome=wallet] .rj-expanded__controls {\n align-items: center;\n gap: 8px;\n}\n.rj-root[data-chrome=wallet] .rj-expanded__transport,\n.rj-root[data-chrome=wallet] .rj-expanded__utility {\n flex: 0 1 auto;\n margin-left: 0;\n min-width: 0;\n border: 0;\n background: transparent;\n padding: 0;\n box-shadow: none;\n}\n.rj-root[data-chrome=wallet] .rj-expanded__transport {\n display: grid;\n grid-template-columns: 30px auto 30px;\n gap: 6px;\n align-items: center;\n}\n.rj-root[data-chrome=wallet] .rj-expanded__utility {\n margin-left: auto;\n flex: 1 1 auto;\n justify-content: flex-end;\n gap: 6px;\n}\n.rj-root[data-chrome=wallet] .rj-chip-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 11px;\n min-height: 32px;\n padding-inline: 10px;\n border-radius: 9999px;\n text-align: center;\n}\n.rj-root[data-chrome=wallet] .rj-expanded__transport .rj-chip-button:not(.rj-chip-button--primary) {\n width: 30px;\n min-width: 30px;\n padding-inline: 0;\n}\n.rj-root[data-chrome=wallet] .rj-chip-button--primary {\n justify-self: center;\n min-width: 48px;\n}\n.rj-root[data-chrome=wallet] .rj-expanded .rj-icon-button {\n margin-left: 0;\n width: 18px;\n height: 18px;\n border-radius: 9999px;\n background: transparent;\n}\n.rj-root[data-chrome=wallet] .rj-expanded .rj-icon-button__icon {\n width: 14px;\n height: 14px;\n}\n.rj-root[data-chrome=wallet] .rj-volume {\n width: auto;\n min-width: 40px;\n}\n.rj-root[data-chrome=wallet] .rj-expanded__counter {\n margin-left: 0;\n flex: 0 0 auto;\n padding: 0;\n border: 0;\n background: transparent;\n}\n.rj-root[data-chrome=wallet] .rj-next-track {\n gap: 10px;\n}\n.rj-root[data-chrome=wallet] .rj-next-track__label {\n border-radius: 10px;\n background: var(--rj-track-hover-bg);\n padding: 4px 8px;\n}\n.rj-root[data-chrome=ride] .rj-dock {\n border-color: var(--rj-border);\n}\n.rj-root[data-chrome=ride] .rj-track-summary__title,\n.rj-root[data-chrome=ride] .rj-expanded__title,\n.rj-root[data-chrome=ride] .rj-next-track__label {\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.rj-root[data-chrome=ride] .rj-track-summary__title {\n font-size: 11px;\n}\n.rj-root[data-chrome=ride] .rj-track-summary__artist {\n font-size: 10px;\n}\n.rj-root[data-chrome=ride] .rj-dock .rj-icon-button {\n border-radius: 12px;\n background: var(--rj-track-hover-bg);\n}\n.rj-root[data-chrome=ride] .rj-expanded {\n overflow: hidden;\n border-color: var(--rj-border);\n background:\n radial-gradient(\n circle at top left,\n var(--rj-track-hover-bg),\n transparent 32%),\n var(--rj-next-bg);\n box-shadow: var(--rj-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.04);\n}\n.rj-root[data-chrome=ride] .rj-expanded__shell {\n position: relative;\n background:\n linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0.04),\n rgba(255, 255, 255, 0)),\n var(--rj-panel);\n}\n.rj-root[data-chrome=ride] .rj-expanded__shell::before {\n content: "";\n position: absolute;\n top: 12px;\n right: 12px;\n width: 58px;\n height: 58px;\n border-radius: 50%;\n background:\n radial-gradient(\n circle,\n rgba(255, 255, 255, 0.12),\n transparent 62%);\n opacity: 0.75;\n pointer-events: none;\n}\n.rj-root[data-chrome=ride] .rj-expanded__screen-frame {\n border-color: var(--rj-screen-frame-border);\n background: var(--rj-screen-frame-bg);\n box-shadow: var(--rj-screen-frame-shadow), 0 16px 24px -24px rgba(0, 0, 0, 0.28);\n}\n.rj-root[data-chrome=ride] .rj-expanded__screen {\n border-color: var(--rj-screen-border);\n}\n.rj-root[data-chrome=ride] .rj-expanded__meta {\n margin-top: 14px;\n}\n.rj-root[data-chrome=ride] .rj-expanded__titles {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.rj-root[data-chrome=ride] .rj-expanded__artist {\n max-width: 96px;\n margin-top: 0;\n text-align: right;\n}\n.rj-root[data-chrome=ride] .rj-expanded__controls {\n display: flex;\n flex-wrap: nowrap;\n align-items: center;\n gap: 8px;\n}\n.rj-root[data-chrome=ride] .rj-expanded__transport,\n.rj-root[data-chrome=ride] .rj-expanded__utility {\n margin-left: 0;\n min-width: 0;\n}\n.rj-root[data-chrome=ride] .rj-expanded__transport {\n display: grid;\n grid-template-columns: 30px auto 30px;\n gap: 4px;\n align-items: center;\n justify-content: flex-start;\n width: auto;\n max-width: none;\n border: 1px solid var(--rj-border);\n border-radius: 14px;\n background: var(--rj-panel);\n padding: 6px;\n}\n.rj-root[data-chrome=ride] .rj-expanded__utility {\n display: flex;\n flex: 1 1 auto;\n margin-left: auto;\n justify-content: flex-end;\n gap: 8px;\n align-items: center;\n}\n.rj-root[data-chrome=ride] .rj-chip-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n min-height: 30px;\n padding-inline: 0;\n box-shadow: none;\n text-align: center;\n}\n.rj-root[data-chrome=ride] .rj-chip-button--primary {\n justify-self: center;\n width: auto;\n min-width: 46px;\n padding-inline: 8px;\n border-color: var(--rj-chip-primary-border);\n border-radius: 13px;\n background: var(--rj-gradient-soft);\n color: var(--rj-chip-primary-text);\n font-size: 9px;\n line-height: 1;\n letter-spacing: 0;\n text-transform: none;\n}\n.rj-root[data-chrome=ride] .rj-expanded__transport .rj-chip-button:not(.rj-chip-button--primary) {\n width: 30px;\n min-width: 30px;\n padding-inline: 0;\n border-color: var(--rj-chip-border);\n border-radius: 12px;\n background: var(--rj-gradient-soft);\n color: var(--rj-button-text);\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.92), 0 8px 16px -14px rgba(15, 23, 42, 0.22);\n}\n.rj-root[data-chrome=ride] .rj-expanded__transport .rj-chip-button:not(.rj-chip-button--primary):hover {\n transform: translateY(0);\n filter: brightness(1.02);\n}\n.rj-root[data-chrome=ride] .rj-expanded .rj-icon-button {\n margin-left: 0;\n width: 36px;\n height: 36px;\n border-radius: 12px;\n background: var(--rj-track-hover-bg);\n}\n.rj-root[data-chrome=ride] .rj-volume {\n width: auto;\n min-width: 44px;\n}\n.rj-root[data-chrome=ride] .rj-expanded__counter {\n margin-left: 0;\n padding: 0;\n border-top: 0;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.rj-root[data-chrome=ride] .rj-next-track {\n align-items: flex-start;\n gap: 12px;\n background: var(--rj-next-bg);\n}\n.rj-root[data-chrome=ride] .rj-next-track__button {\n text-align: right;\n}\n@media (prefers-color-scheme: dark) {\n .rj-root[data-theme=glass] {\n --rj-text: #f8fafc;\n --rj-text-muted: #b8c2cf;\n --rj-border: rgba(255, 255, 255, 0.1);\n --rj-panel: rgba(0, 0, 0, 0.48);\n --rj-shadow: 0 22px 60px -30px rgba(15, 23, 42, 0.42);\n --rj-gradient:\n linear-gradient(\n 180deg,\n #f1f5f9 0%,\n #d8e1ea 100%);\n --rj-gradient-soft:\n linear-gradient(\n 180deg,\n #2b3440 0%,\n #1c232d 100%);\n --rj-button-text: #e2e8f0;\n --rj-dock-bg: rgba(0, 0, 0, 0.52);\n --rj-dock-inner-bg:\n linear-gradient(\n \n 180deg,\n rgba(37, 42, 49, 0.94) 0%,\n rgba(22, 27, 34, 0.98) 100% );\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.08);\n --rj-shell-border: rgba(255, 255, 255, 0.06);\n --rj-next-border: rgba(255, 255, 255, 0.08);\n --rj-screen-frame-border: rgba(255, 255, 255, 0.08);\n --rj-expanded-icon: #94a3b8;\n --rj-expanded-icon-hover: #ffffff;\n --rj-volume-track: #475569;\n --rj-volume-thumb: #e2e8f0;\n }\n .rj-root[data-theme=simple] {\n --rj-text: #fafafa;\n --rj-text-muted: #a1a1aa;\n --rj-border: rgba(255, 255, 255, 0.08);\n --rj-panel: rgba(10, 10, 11, 0.94);\n --rj-shadow: 0 24px 54px -30px rgba(0, 0, 0, 0.58);\n --rj-gradient:\n linear-gradient(\n 180deg,\n #fafafa 0%,\n #d4d4d8 100%);\n --rj-gradient-soft:\n linear-gradient(\n 180deg,\n #27272a 0%,\n #18181b 100%);\n --rj-button-text: #f4f4f5;\n --rj-dock-bg: rgba(10, 10, 11, 0.92);\n --rj-dock-shadow: 0 18px 40px -26px rgba(0, 0, 0, 0.5);\n --rj-dock-inner-bg: #111113;\n --rj-track-hover-bg: rgba(255, 255, 255, 0.04);\n --rj-level-gradient:\n linear-gradient(\n \n 180deg,\n #fafafa 0%,\n #a1a1aa 52%,\n #3f3f46 100% );\n --rj-level-shadow-color: rgba(255, 255, 255, 0.08);\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.07);\n --rj-shell-border: rgba(255, 255, 255, 0.08);\n --rj-shell-inset-shadow: none;\n --rj-screen-frame-border: #3f3f46;\n --rj-screen-frame-bg:\n linear-gradient(\n 180deg,\n #27272a 0%,\n #18181b 100%);\n --rj-screen-frame-shadow: none;\n --rj-screen-border: #52525b;\n --rj-screen-bg: #09090b;\n --rj-player-bg:\n radial-gradient(\n \n circle at top,\n rgba(255, 255, 255, 0.06),\n transparent 38% ),\n linear-gradient(\n 180deg,\n #18181b 0%,\n #020617 100%);\n --rj-chip-border: #3f3f46;\n --rj-chip-primary-border: #fafafa;\n --rj-chip-primary-text: #171717;\n --rj-volume-track: #3f3f46;\n --rj-volume-thumb: #fafafa;\n --rj-expanded-icon: #d4d4d8;\n --rj-expanded-icon-hover: #ffffff;\n --rj-next-border: #3f3f46;\n --rj-next-bg:\n linear-gradient(\n 180deg,\n #27272a 0%,\n #18181b 100%);\n }\n .rj-root[data-theme=sunset] {\n --rj-text: #fff1f2;\n --rj-text-muted: #fecdd3;\n --rj-border: rgba(255, 255, 255, 0.1);\n --rj-panel: rgba(76, 5, 25, 0.78);\n --rj-shadow: 0 24px 56px -28px rgba(76, 5, 25, 0.5);\n --rj-gradient:\n linear-gradient(\n \n 135deg,\n #fde68a 0%,\n #fb7185 54%,\n #7c3aed 100% );\n --rj-gradient-soft:\n linear-gradient(\n \n 180deg,\n rgba(136, 19, 55, 0.92) 0%,\n rgba(76, 5, 25, 0.96) 100% );\n --rj-button-text: #fff7ed;\n --rj-dock-bg: rgba(76, 5, 25, 0.74);\n --rj-dock-shadow: 0 -8px 24px -18px rgba(244, 114, 182, 0.24), 0 14px 34px -18px rgba(126, 34, 206, 0.3);\n --rj-dock-inner-bg:\n linear-gradient(\n \n 135deg,\n rgba(136, 19, 55, 0.92) 0%,\n rgba(76, 5, 25, 0.96) 100% );\n --rj-track-hover-bg: rgba(255, 255, 255, 0.06);\n --rj-level-gradient:\n linear-gradient(\n \n 180deg,\n #fde68a 0%,\n #fb7185 52%,\n #7c3aed 100% );\n --rj-level-shadow-color: rgba(253, 224, 71, 0.16);\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.08);\n --rj-shell-border: rgba(255, 255, 255, 0.08);\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);\n --rj-screen-frame-border: rgba(251, 113, 133, 0.4);\n --rj-screen-frame-bg:\n linear-gradient(\n 180deg,\n #881337 0%,\n #4c0519 100%);\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\n --rj-screen-border: rgba(253, 186, 116, 0.48);\n --rj-screen-bg: #19030d;\n --rj-player-bg:\n radial-gradient(\n circle at top,\n rgba(253, 224, 71, 0.2),\n transparent 40%),\n radial-gradient(\n \n circle at 70% 30%,\n rgba(192, 132, 252, 0.18),\n transparent 36% ),\n linear-gradient(\n 180deg,\n #4c0519 0%,\n #1e1b4b 100%);\n --rj-chip-border: rgba(253, 186, 116, 0.2);\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\n --rj-chip-primary-border: rgba(253, 224, 71, 0.18);\n --rj-chip-primary-text: #4c0519;\n --rj-volume-track: rgba(251, 113, 133, 0.48);\n --rj-volume-thumb: #fde68a;\n --rj-volume-thumb-shadow: 0 1px 8px rgba(253, 224, 71, 0.18);\n --rj-expanded-icon: #fda4af;\n --rj-expanded-icon-hover: #fff1f2;\n --rj-next-border: rgba(251, 113, 133, 0.22);\n --rj-next-bg:\n linear-gradient(\n \n 180deg,\n rgba(136, 19, 55, 0.96) 0%,\n rgba(76, 5, 25, 0.96) 100% );\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\n }\n .rj-root[data-chrome=ride] {\n --rj-volume-thumb-shadow: none;\n }\n}\n@media (max-width: 640px) {\n .rj-root {\n --rj-root-width: min(100vw - 16px, 320px);\n --rj-root-expanded-width: min(100vw - 16px, 320px);\n }\n .rj-root--inline {\n --rj-root-width: 100%;\n --rj-root-expanded-width: 100%;\n }\n .rj-root--inline .rj-expanded {\n padding: 6px;\n }\n .rj-root--inline .rj-expanded__shell {\n padding: 10px;\n }\n .rj-root--inline .rj-next-track {\n flex-wrap: wrap;\n align-items: flex-start;\n }\n .rj-root--inline .rj-next-track__button {\n width: 100%;\n text-align: left;\n }\n .rj-root--inline[data-chrome=wallet] .rj-expanded__utility {\n gap: 8px;\n }\n .rj-root--inline[data-chrome=wallet] .rj-expanded__counter {\n margin-left: auto;\n }\n .rj-root--inline[data-chrome=ride] .rj-expanded__titles {\n flex-direction: column;\n gap: 4px;\n }\n .rj-root--inline[data-chrome=ride] .rj-expanded__artist {\n max-width: none;\n text-align: left;\n }\n .rj-root--inline[data-chrome=ride] .rj-expanded__transport {\n width: auto;\n }\n .rj-root--inline[data-chrome=ride] .rj-expanded__counter {\n justify-self: auto;\n }\n}\n@keyframes rj-jukebox-bounce {\n 0%, 100% {\n transform: scaleY(0.35);\n }\n 50% {\n transform: scaleY(1);\n }\n}\n');
467
+ function subscribeToClientRender() {
468
+ return () => void 0;
469
+ }
470
+ function getClientRenderSnapshot() {
471
+ return true;
472
+ }
473
+ function getServerRenderSnapshot() {
474
+ return false;
475
+ }
476
+ function VolumeIcon({ isMuted }) {
477
+ if (isMuted) {
478
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: [
479
+ /* @__PURE__ */ jsx("path", { d: "M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z" }),
480
+ /* @__PURE__ */ jsx("path", { d: "M10.28 5.22a.75.75 0 0 1 1.06 0L12 5.88l.66-.66a.75.75 0 1 1 1.06 1.06l-.66.66.66.66a.75.75 0 1 1-1.06 1.06L12 7.94l-.66.66a.75.75 0 0 1-1.06-1.06l.66-.66-.66-.66a.75.75 0 0 1 0-1.06Z" })
481
+ ] });
482
+ }
483
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: [
484
+ /* @__PURE__ */ jsx("path", { d: "M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z" }),
485
+ /* @__PURE__ */ jsx("path", { d: "M10.5 5.02a.75.75 0 0 1 1.06 0 3.86 3.86 0 0 1 0 5.46.75.75 0 1 1-1.06-1.06 2.36 2.36 0 0 0 0-3.34.75.75 0 0 1 0-1.06Z" }),
486
+ /* @__PURE__ */ jsx("path", { d: "M11.9 3.62a.75.75 0 0 1 1.06 0 5.84 5.84 0 0 1 0 8.76.75.75 0 1 1-1.06-1.06 4.34 4.34 0 0 0 0-6.64.75.75 0 0 1 0-1.06Z" })
487
+ ] });
488
+ }
489
+ function ChevronIcon({ isExpanded }) {
490
+ return /* @__PURE__ */ jsx(
491
+ "svg",
492
+ {
493
+ viewBox: "0 0 16 16",
494
+ fill: "none",
495
+ "aria-hidden": "true",
496
+ className: clsx("rj-chevron__icon", {
497
+ "rj-chevron__icon--expanded": isExpanded
498
+ }),
499
+ children: /* @__PURE__ */ jsx(
500
+ "path",
501
+ {
502
+ d: "M4 6.5 8 10l4-3.5",
503
+ stroke: "currentColor",
504
+ strokeWidth: "1.5",
505
+ strokeLinecap: "round",
506
+ strokeLinejoin: "round"
507
+ }
508
+ )
509
+ }
510
+ );
511
+ }
512
+ function TrackSummary({
513
+ currentTrack,
514
+ isExpanded,
515
+ isPlaying,
516
+ onToggleExpanded
517
+ }) {
518
+ if (!currentTrack) {
519
+ return /* @__PURE__ */ jsx("div", { className: "rj-track-summary rj-track-summary--empty", children: /* @__PURE__ */ jsxs("div", { className: "rj-track-summary__copy", children: [
520
+ /* @__PURE__ */ jsx("div", { className: "rj-track-summary__title", children: "No tracks" }),
521
+ /* @__PURE__ */ jsx("div", { className: "rj-track-summary__artist", children: "Pass at least one YouTube video to start playback." })
522
+ ] }) });
523
+ }
524
+ return /* @__PURE__ */ jsxs(
525
+ "button",
526
+ {
527
+ type: "button",
528
+ onClick: onToggleExpanded,
529
+ "aria-expanded": isExpanded,
530
+ className: "rj-track-summary",
531
+ children: [
532
+ /* @__PURE__ */ jsx("div", { className: "rj-level-meter", children: LEVEL_BAR_HEIGHTS.map((height, index) => {
533
+ const animationDelayMs = index * LEVEL_BAR_ANIMATION_DELAY_MS;
534
+ return /* @__PURE__ */ jsx(
535
+ "div",
536
+ {
537
+ "aria-hidden": "true",
538
+ className: clsx("rj-level-bar", {
539
+ "rj-level-bar--playing": isPlaying
540
+ }),
541
+ style: {
542
+ minHeight: `${LEVEL_BAR_REST_HEIGHT}px`,
543
+ height: `${isPlaying ? height : LEVEL_BAR_REST_HEIGHT}px`,
544
+ animationDelay: `${animationDelayMs}ms`
545
+ }
546
+ },
547
+ `${height}-${index}`
548
+ );
549
+ }) }),
550
+ /* @__PURE__ */ jsxs("div", { className: "rj-track-summary__copy", children: [
551
+ /* @__PURE__ */ jsx("div", { className: "rj-track-summary__title", children: currentTrack.title }),
552
+ /* @__PURE__ */ jsx("div", { className: "rj-track-summary__artist", children: currentTrack.artist ?? "Unknown artist" })
553
+ ] }),
554
+ /* @__PURE__ */ jsx("span", { className: "rj-chevron", children: /* @__PURE__ */ jsx(ChevronIcon, { isExpanded }) })
555
+ ]
556
+ }
557
+ );
558
+ }
559
+ function ExpandedPanel({
560
+ children,
561
+ isExpanded
562
+ }) {
563
+ return /* @__PURE__ */ jsx(
564
+ "div",
565
+ {
566
+ "aria-hidden": !isExpanded,
567
+ className: clsx("rj-expanded", {
568
+ "rj-expanded--hidden": !isExpanded
569
+ }),
570
+ children
571
+ }
572
+ );
573
+ }
574
+ function Jukebox({
575
+ tracks,
576
+ autoplay = true,
577
+ position = DEFAULT_POSITION,
578
+ theme = DEFAULT_THEME,
579
+ chrome = DEFAULT_CHROME,
580
+ offset,
581
+ portal = true,
582
+ className,
583
+ renderExpandedContent
584
+ }) {
585
+ const [isExpanded, setIsExpanded] = useState(false);
586
+ const isMounted = useSyncExternalStore(
587
+ subscribeToClientRender,
588
+ getClientRenderSnapshot,
589
+ getServerRenderSnapshot
590
+ );
591
+ const {
592
+ playerMountRef,
593
+ currentIndex,
594
+ isMuted,
595
+ isPlaying,
596
+ volume,
597
+ setVolume,
598
+ toggleMute,
599
+ togglePlay,
600
+ playNext,
601
+ playPrev
602
+ } = useJukeboxPlayer({ autoplay, tracks });
603
+ const effectiveChrome = getEffectiveChrome(chrome);
604
+ const currentTrack = tracks[currentIndex];
605
+ const nextTrack = tracks.length > 1 ? tracks[getNextTrackIndex(currentIndex, 1, tracks.length)] : void 0;
606
+ const effectiveIsExpanded = currentTrack ? isExpanded : false;
607
+ const expandedRenderProps = currentTrack ? {
608
+ currentIndex,
609
+ currentTrack,
610
+ isExpanded: effectiveIsExpanded,
611
+ isMuted,
612
+ isPlaying,
613
+ nextTrack,
614
+ playerMountRef,
615
+ totalTracks: tracks.length,
616
+ volume,
617
+ setVolume,
618
+ toggleMute,
619
+ togglePlay,
620
+ playNext,
621
+ playPrev
622
+ } : void 0;
623
+ const handleToggleExpanded = () => {
624
+ if (!currentTrack) {
625
+ return;
626
+ }
627
+ setIsExpanded((expanded) => !expanded);
628
+ };
629
+ const content = /* @__PURE__ */ jsxs(
630
+ "div",
631
+ {
632
+ className: clsx(
633
+ "rj-root",
634
+ {
635
+ "rj-root--expanded": effectiveIsExpanded,
636
+ "rj-root--portal": portal,
637
+ "rj-root--inline": !portal
638
+ },
639
+ className
640
+ ),
641
+ "data-position": position,
642
+ "data-theme": theme,
643
+ "data-chrome": effectiveChrome,
644
+ style: getPositionStyle(position, offset, portal),
645
+ children: [
646
+ expandedRenderProps ? /* @__PURE__ */ jsx(ExpandedPanel, { isExpanded: effectiveIsExpanded, children: renderExpandedContent ? renderExpandedContent(expandedRenderProps) : /* @__PURE__ */ jsx(JukeboxExpandedPlayer, { ...expandedRenderProps }) }) : null,
647
+ /* @__PURE__ */ jsx("div", { className: "rj-dock", children: /* @__PURE__ */ jsxs("div", { className: "rj-dock__inner", children: [
648
+ /* @__PURE__ */ jsx(
649
+ TrackSummary,
650
+ {
651
+ currentTrack,
652
+ isExpanded: effectiveIsExpanded,
653
+ isPlaying,
654
+ onToggleExpanded: handleToggleExpanded
655
+ }
656
+ ),
657
+ /* @__PURE__ */ jsx(
658
+ "button",
659
+ {
660
+ type: "button",
661
+ onClick: toggleMute,
662
+ disabled: !currentTrack,
663
+ "aria-label": isMuted ? "Unmute" : "Mute",
664
+ className: "rj-icon-button",
665
+ children: /* @__PURE__ */ jsx("span", { className: "rj-icon-button__icon", children: /* @__PURE__ */ jsx(VolumeIcon, { isMuted }) })
666
+ }
667
+ )
668
+ ] }) })
669
+ ]
670
+ }
671
+ );
672
+ if (!portal) {
673
+ return content;
674
+ }
675
+ if (!isMounted) {
676
+ return null;
677
+ }
678
+ return createPortal(content, document.body);
679
+ }
680
+
681
+ export { Jukebox };
682
+ //# sourceMappingURL=index.js.map
683
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/shared.ts","../src/lib/youtube.ts","../src/hooks/useJukeboxPlayer.ts","../src/components/JukeboxExpandedPlayer.tsx","#style-inject:#style-inject","../src/styles/jukebox.css","../src/components/Jukebox.tsx"],"names":["jsxs","jsx","useState"],"mappings":"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","file":"index.js","sourcesContent":["import type { CSSProperties, ReactNode } from \"react\";\n\nexport type JukeboxTrack = {\n videoId: string;\n title: string;\n artist?: string;\n};\n\nexport type JukeboxPosition =\n | \"bottom-right\"\n | \"bottom-left\"\n | \"bottom-center\"\n | \"top-right\"\n | \"top-left\"\n | \"top-center\";\n\nexport type JukeboxTheme = \"glass\" | \"simple\" | \"sunset\" | \"ride\";\nexport type JukeboxChrome = \"classic\" | \"wallet\" | \"ride\";\n\nexport type JukeboxOffset = number | { x: number; y: number };\n\nexport type JukeboxProps = {\n tracks: JukeboxTrack[];\n autoplay?: boolean;\n position?: JukeboxPosition;\n theme?: JukeboxTheme;\n chrome?: JukeboxChrome;\n offset?: JukeboxOffset;\n portal?: boolean;\n className?: string;\n renderExpandedContent?: (\n props: JukeboxExpandedRenderProps,\n ) => ReactNode;\n};\n\nexport type JukeboxPlayerState = {\n currentIndex: number;\n isMuted: boolean;\n isPlaying: boolean;\n playerMountRef: (node: HTMLDivElement | null) => void;\n volume: number;\n setVolume: (nextVolume: number) => void;\n toggleMute: () => void;\n togglePlay: () => void;\n playNext: () => void;\n playPrev: () => void;\n};\n\nexport type JukeboxExpandedRenderProps = JukeboxPlayerState & {\n currentTrack: JukeboxTrack;\n isExpanded: boolean;\n nextTrack: JukeboxTrack | undefined;\n totalTracks: number;\n};\n\nexport const DEFAULT_POSITION: JukeboxPosition = \"bottom-right\";\nexport const DEFAULT_THEME: JukeboxTheme = \"glass\";\nexport const DEFAULT_CHROME: JukeboxChrome = \"classic\";\nexport const TEMPORARILY_DISABLED_CHROMES = [\"wallet\", \"ride\"] as const;\nexport const DEFAULT_OFFSET_PX = 20;\nexport const DEFAULT_VOLUME = 100;\nexport const LEVEL_BAR_HEIGHTS = [12, 18, 14] as const;\nexport const LEVEL_BAR_REST_HEIGHT = 8;\nexport const LEVEL_BAR_ANIMATION_DELAY_MS = 120;\n\nexport function getEffectiveChrome(chrome: JukeboxChrome): JukeboxChrome {\n const isTemporarilyDisabledChrome =\n chrome === \"wallet\" || chrome === \"ride\";\n\n if (isTemporarilyDisabledChrome) {\n return DEFAULT_CHROME;\n }\n\n return chrome;\n}\n\nexport function getNextTrackIndex(\n index: number,\n step: number,\n totalTracks: number,\n) {\n if (totalTracks <= 0) {\n return 0;\n }\n\n return (index + step + totalTracks) % totalTracks;\n}\n\nexport function clampVolume(value: number) {\n return Math.min(Math.max(Math.round(value), 0), 100);\n}\n\nexport function normalizeOffset(offset: JukeboxOffset | undefined) {\n if (typeof offset === \"number\") {\n return { x: offset, y: offset };\n }\n\n if (offset) {\n return { x: offset.x, y: offset.y };\n }\n\n return { x: DEFAULT_OFFSET_PX, y: DEFAULT_OFFSET_PX };\n}\n\nexport function getPositionStyle(\n position: JukeboxPosition,\n offset: JukeboxOffset | undefined,\n isPortal: boolean,\n): CSSProperties {\n const normalizedOffset = normalizeOffset(offset);\n const isTopPosition = position.startsWith(\"top\");\n const isCenterPosition = position.endsWith(\"center\");\n const isLeftPosition = position.endsWith(\"left\");\n const style: CSSProperties = {\n position: isPortal ? \"fixed\" : \"absolute\",\n };\n\n if (isTopPosition) {\n style.top = normalizedOffset.y;\n } else {\n style.bottom = normalizedOffset.y;\n }\n\n if (isCenterPosition) {\n style.left = \"50%\";\n style.transform = \"translateX(-50%)\";\n return style;\n }\n\n if (isLeftPosition) {\n style.left = normalizedOffset.x;\n } else {\n style.right = normalizedOffset.x;\n }\n\n return style;\n}\n","export const PLAYER_STATE_ENDED = 0;\nexport const PLAYER_STATE_PLAYING = 1;\nexport const PLAYER_STATE_PAUSED = 2;\n\nexport type YouTubePlayerStateEvent = {\n data: number;\n};\n\nexport type YouTubePlayer = {\n cueVideoById(videoId: string): void;\n destroy(): void;\n loadVideoById(videoId: string): void;\n mute(): void;\n pauseVideo(): void;\n playVideo(): void;\n setVolume(volume: number): void;\n unMute(): void;\n};\n\nexport type YouTubePlayerOptions = {\n events: {\n onError: () => void;\n onReady: () => void;\n onStateChange: (event: YouTubePlayerStateEvent) => void;\n };\n height: string;\n playerVars: {\n controls: 0 | 1;\n origin?: string;\n playsinline: 0 | 1;\n rel: 0 | 1;\n };\n videoId: string;\n width: string;\n};\n\nexport type YouTubeNamespace = {\n Player: new (\n element: HTMLDivElement,\n options: YouTubePlayerOptions,\n ) => YouTubePlayer;\n};\n\ndeclare global {\n interface Window {\n YT?: YouTubeNamespace;\n onYouTubeIframeAPIReady?: () => void;\n }\n}\n\nlet youtubeIframeApiPromise: Promise<YouTubeNamespace> | null = null;\n\nexport function canControlPlayer(\n player: YouTubePlayer | null,\n): player is YouTubePlayer {\n return (\n player !== null &&\n typeof player.pauseVideo === \"function\" &&\n typeof player.playVideo === \"function\"\n );\n}\n\nexport function loadYouTubeIframeApi() {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"YouTube iframe API requires a browser.\"));\n }\n\n if (window.YT?.Player) {\n return Promise.resolve(window.YT);\n }\n\n if (youtubeIframeApiPromise) {\n return youtubeIframeApiPromise;\n }\n\n youtubeIframeApiPromise = new Promise<YouTubeNamespace>((resolve, reject) => {\n const resetPromise = () => {\n youtubeIframeApiPromise = null;\n };\n const existingScript = document.querySelector<HTMLScriptElement>(\n 'script[src=\"https://www.youtube.com/iframe_api\"]',\n );\n\n const script = existingScript ?? document.createElement(\"script\");\n\n if (!existingScript) {\n script.src = \"https://www.youtube.com/iframe_api\";\n script.async = true;\n document.head.append(script);\n }\n\n const handleError = () => {\n resetPromise();\n reject(new Error(\"Failed to load the YouTube iframe API.\"));\n };\n\n script.addEventListener(\"error\", handleError, { once: true });\n\n const previousReadyHandler = window.onYouTubeIframeAPIReady;\n\n window.onYouTubeIframeAPIReady = () => {\n previousReadyHandler?.();\n script.removeEventListener(\"error\", handleError);\n\n if (!window.YT?.Player) {\n resetPromise();\n reject(new Error(\"YouTube iframe API loaded without a player.\"));\n return;\n }\n\n resolve(window.YT);\n };\n });\n\n return youtubeIframeApiPromise;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport {\n clampVolume,\n DEFAULT_VOLUME,\n getNextTrackIndex,\n type JukeboxPlayerState,\n type JukeboxTrack,\n} from \"../lib/shared\";\nimport {\n canControlPlayer,\n loadYouTubeIframeApi,\n PLAYER_STATE_ENDED,\n PLAYER_STATE_PAUSED,\n PLAYER_STATE_PLAYING,\n type YouTubePlayer,\n} from \"../lib/youtube\";\n\ntype UseJukeboxPlayerOptions = {\n autoplay: boolean;\n tracks: JukeboxTrack[];\n};\n\nexport function useJukeboxPlayer({\n autoplay,\n tracks,\n}: UseJukeboxPlayerOptions): JukeboxPlayerState {\n const playerRef = useRef<YouTubePlayer | null>(null);\n const currentIndexRef = useRef(0);\n const isPlayingRef = useRef(false);\n const mutedPreferenceRef = useRef(true);\n const shouldResumePlaybackRef = useRef(autoplay);\n const tracksRef = useRef(tracks);\n const volumeRef = useRef(DEFAULT_VOLUME);\n const [playerMountNode, setPlayerMountNode] = useState<HTMLDivElement | null>(\n null,\n );\n const [currentIndex, setCurrentIndex] = useState(0);\n const [isReady, setIsReady] = useState(false);\n const [isPlaying, setIsPlaying] = useState(false);\n const [isMuted, setIsMuted] = useState(true);\n const [volume, setVolumeState] = useState(DEFAULT_VOLUME);\n\n const trackCount = tracks.length;\n const hasTracks = trackCount > 0;\n const hasMultipleTracks = trackCount > 1;\n const safeCurrentIndex = hasTracks ? Math.min(currentIndex, trackCount - 1) : 0;\n const currentTrack = tracks[safeCurrentIndex];\n const currentVideoId = currentTrack?.videoId;\n\n const moveTrack = useCallback(\n (step: number) => {\n if (!hasMultipleTracks) {\n return;\n }\n\n shouldResumePlaybackRef.current = isPlayingRef.current;\n setCurrentIndex((index) => getNextTrackIndex(index, step, trackCount));\n },\n [hasMultipleTracks, trackCount],\n );\n\n const pausePlayback = useCallback(() => {\n const player = playerRef.current;\n\n shouldResumePlaybackRef.current = false;\n\n if (!canControlPlayer(player)) {\n setIsPlaying(false);\n return;\n }\n\n player.pauseVideo();\n setIsPlaying(false);\n }, []);\n\n useEffect(() => {\n currentIndexRef.current = safeCurrentIndex;\n }, [safeCurrentIndex]);\n\n useEffect(() => {\n tracksRef.current = tracks;\n }, [tracks]);\n\n useEffect(() => {\n isPlayingRef.current = isPlaying;\n }, [isPlaying]);\n\n useEffect(() => {\n volumeRef.current = volume;\n }, [volume]);\n\n useEffect(() => {\n if (!isReady && !isPlayingRef.current) {\n shouldResumePlaybackRef.current = autoplay;\n }\n }, [autoplay, isReady]);\n\n useEffect(() => {\n if (!playerMountNode || !hasTracks) {\n return;\n }\n\n let isCancelled = false;\n\n void loadYouTubeIframeApi()\n .then((YT) => {\n if (isCancelled) {\n return;\n }\n\n playerRef.current = new YT.Player(playerMountNode, {\n width: \"100%\",\n height: \"100%\",\n videoId: tracksRef.current[currentIndexRef.current]?.videoId ?? \"\",\n playerVars: {\n controls: 1,\n origin: window.location.origin,\n playsinline: 1,\n rel: 0,\n },\n events: {\n onReady: () => {\n const player = playerRef.current;\n\n if (!player) {\n return;\n }\n\n player.setVolume(volumeRef.current);\n\n if (mutedPreferenceRef.current) {\n player.mute();\n } else {\n player.unMute();\n }\n\n setIsMuted(mutedPreferenceRef.current);\n setIsReady(true);\n },\n onStateChange: (event) => {\n if (event.data === PLAYER_STATE_ENDED) {\n const nextTrackCount = tracksRef.current.length;\n\n if (nextTrackCount <= 1) {\n shouldResumePlaybackRef.current = false;\n setIsPlaying(false);\n return;\n }\n\n shouldResumePlaybackRef.current = true;\n setCurrentIndex((index) =>\n getNextTrackIndex(index, 1, nextTrackCount),\n );\n return;\n }\n\n if (event.data === PLAYER_STATE_PLAYING) {\n setIsPlaying(true);\n return;\n }\n\n if (event.data === PLAYER_STATE_PAUSED) {\n setIsPlaying(false);\n }\n },\n onError: () => {\n shouldResumePlaybackRef.current = false;\n setIsPlaying(false);\n },\n },\n });\n })\n .catch(() => {\n setIsReady(false);\n setIsPlaying(false);\n });\n\n return () => {\n isCancelled = true;\n setIsReady(false);\n setIsPlaying(false);\n playerRef.current?.destroy();\n playerRef.current = null;\n };\n }, [hasTracks, playerMountNode]);\n\n useEffect(() => {\n const player = playerRef.current;\n\n if (!isReady || !player || !currentVideoId) {\n return;\n }\n\n if (shouldResumePlaybackRef.current) {\n player.loadVideoById(currentVideoId);\n return;\n }\n\n player.cueVideoById(currentVideoId);\n }, [currentVideoId, isReady]);\n\n return {\n playerMountRef: setPlayerMountNode,\n currentIndex: safeCurrentIndex,\n isMuted,\n isPlaying,\n volume,\n setVolume: useCallback((nextVolume: number) => {\n const clampedVolume = clampVolume(nextVolume);\n const player = playerRef.current;\n\n setVolumeState(clampedVolume);\n volumeRef.current = clampedVolume;\n\n if (clampedVolume === 0) {\n mutedPreferenceRef.current = true;\n setIsMuted(true);\n } else {\n mutedPreferenceRef.current = false;\n setIsMuted(false);\n }\n\n if (!player) {\n return;\n }\n\n player.setVolume(clampedVolume);\n\n if (clampedVolume === 0) {\n player.mute();\n return;\n }\n\n player.unMute();\n }, []),\n toggleMute: useCallback(() => {\n const player = playerRef.current;\n const nextMuted = !mutedPreferenceRef.current;\n\n mutedPreferenceRef.current = nextMuted;\n setIsMuted(nextMuted);\n\n if (!player) {\n return;\n }\n\n if (nextMuted) {\n player.mute();\n return;\n }\n\n player.unMute();\n }, []),\n togglePlay: useCallback(() => {\n const player = playerRef.current;\n\n if (!canControlPlayer(player) || !isReady || !hasTracks) {\n return;\n }\n\n if (isPlayingRef.current) {\n pausePlayback();\n return;\n }\n\n shouldResumePlaybackRef.current = true;\n player.playVideo();\n }, [hasTracks, isReady, pausePlayback]),\n playNext: useCallback(() => {\n moveTrack(1);\n }, [moveTrack]),\n playPrev: useCallback(() => {\n moveTrack(-1);\n }, [moveTrack]),\n };\n}\n","import type { ChangeEvent } from \"react\";\n\nimport type { JukeboxExpandedRenderProps } from \"../lib/shared\";\n\ntype JukeboxExpandedPlayerProps = JukeboxExpandedRenderProps;\n\nfunction SpeakerIcon({ isMuted }: { isMuted: boolean }) {\n if (isMuted) {\n return (\n <svg viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z\" />\n <path d=\"M10.28 5.22a.75.75 0 0 1 1.06 0L12 5.88l.66-.66a.75.75 0 1 1 1.06 1.06l-.66.66.66.66a.75.75 0 1 1-1.06 1.06L12 7.94l-.66.66a.75.75 0 0 1-1.06-1.06l.66-.66-.66-.66a.75.75 0 0 1 0-1.06Z\" />\n </svg>\n );\n }\n\n return (\n <svg viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z\" />\n <path d=\"M10.5 5.02a.75.75 0 0 1 1.06 0 3.86 3.86 0 0 1 0 5.46.75.75 0 1 1-1.06-1.06 2.36 2.36 0 0 0 0-3.34.75.75 0 0 1 0-1.06Z\" />\n </svg>\n );\n}\n\nexport function JukeboxExpandedPlayer({\n currentIndex,\n isMuted,\n isPlaying,\n nextTrack,\n playerMountRef,\n totalTracks,\n volume,\n playNext,\n playPrev,\n togglePlay,\n toggleMute,\n setVolume,\n}: JukeboxExpandedPlayerProps) {\n const hasMultipleTracks = totalTracks > 1;\n const hasNextTrack = nextTrack !== undefined;\n\n const handleVolumeInput = (event: ChangeEvent<HTMLInputElement>) => {\n setVolume(Number(event.target.value));\n };\n\n return (\n <>\n <div className=\"rj-expanded__shell\">\n <div className=\"rj-expanded__screen-frame\">\n <div className=\"rj-expanded__screen\">\n <div ref={playerMountRef} className=\"rj-expanded__player\" />\n </div>\n </div>\n\n <div className=\"rj-expanded__meta\">\n <div className=\"rj-expanded__controls\">\n <div className=\"rj-expanded__transport\">\n <button\n type=\"button\"\n onClick={playPrev}\n disabled={!hasMultipleTracks}\n className=\"rj-chip-button\">\n ◀\n </button>\n <button\n type=\"button\"\n onClick={togglePlay}\n className=\"rj-chip-button rj-chip-button--primary\">\n {isPlaying ? \"Pause\" : \"Play\"}\n </button>\n <button\n type=\"button\"\n onClick={playNext}\n disabled={!hasMultipleTracks}\n className=\"rj-chip-button\">\n ▶\n </button>\n </div>\n\n <div className=\"rj-expanded__utility\">\n <button\n type=\"button\"\n onClick={toggleMute}\n aria-label={isMuted ? \"Unmute\" : \"Mute\"}\n className=\"rj-icon-button\">\n <span className=\"rj-icon-button__icon\">\n <SpeakerIcon isMuted={isMuted} />\n </span>\n </button>\n <input\n type=\"range\"\n min={0}\n max={100}\n step={1}\n value={volume}\n onChange={handleVolumeInput}\n aria-label=\"Volume\"\n className=\"rj-volume\"\n />\n {hasMultipleTracks ? (\n <span className=\"rj-expanded__counter\">\n {currentIndex + 1} / {totalTracks}\n </span>\n ) : null}\n </div>\n </div>\n </div>\n </div>\n\n {hasNextTrack ? (\n <div className=\"rj-next-track\">\n <span className=\"rj-next-track__label\">Next</span>\n <button\n type=\"button\"\n onClick={playNext}\n disabled={!hasMultipleTracks}\n className=\"rj-next-track__button\">\n {nextTrack.title}\n </button>\n </div>\n ) : null}\n </>\n );\n}\n","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".rj-root {\\n --rj-text: #111827;\\n --rj-text-muted: #667085;\\n --rj-border: rgba(255, 255, 255, 0.55);\\n --rj-panel: rgba(255, 255, 255, 0.84);\\n --rj-shadow: 0 18px 44px -22px rgba(15, 23, 42, 0.28);\\n --rj-gradient:\\n linear-gradient(\\n 180deg,\\n #ffffff 0%,\\n #e3eaf2 100%);\\n --rj-gradient-soft:\\n linear-gradient(\\n 180deg,\\n #f8fafc 0%,\\n #e7edf4 100%);\\n --rj-button-text: #334155;\\n --rj-dock-bg: rgba(255, 255, 255, 0.82);\\n --rj-dock-shadow: 0 -8px 22px -16px rgba(15, 23, 42, 0.24), 0 10px 28px -20px rgba(15, 23, 42, 0.2);\\n --rj-dock-inner-bg:\\n linear-gradient(\\n \\n 180deg,\\n rgba(255, 255, 255, 0.84) 0%,\\n rgba(241, 245, 249, 0.94) 100% );\\n --rj-track-hover-bg: rgba(255, 255, 255, 0.55);\\n --rj-level-gradient:\\n linear-gradient(\\n \\n 180deg,\\n #f8fafc 0%,\\n #cbd5e1 45%,\\n #64748b 100% );\\n --rj-level-shadow-color: rgba(148, 163, 184, 0.14);\\n --rj-icon-hover-bg: rgba(15, 23, 42, 0.04);\\n --rj-shell-border: rgba(0, 0, 0, 0.06);\\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82);\\n --rj-screen-frame-border: #c7d0da;\\n --rj-screen-frame-bg:\\n linear-gradient(\\n 180deg,\\n #fbfdff 0%,\\n #e9eef4 100%);\\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.84);\\n --rj-screen-border: #9aa7b8;\\n --rj-screen-bg: #0f172a;\\n --rj-player-bg:\\n radial-gradient(\\n circle at top,\\n rgba(45, 212, 191, 0.15),\\n transparent 45%),\\n linear-gradient(\\n 180deg,\\n rgba(15, 23, 42, 0.96) 0%,\\n rgba(2, 6, 23, 1) 100%);\\n --rj-chip-border: #b4bfcc;\\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\\n --rj-chip-primary-border: #a7b3c2;\\n --rj-chip-primary-text: #1e293b;\\n --rj-volume-track: #cbd5e1;\\n --rj-volume-thumb: #334155;\\n --rj-volume-thumb-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);\\n --rj-expanded-icon: #475569;\\n --rj-expanded-icon-hover: #1e293b;\\n --rj-next-border: #c3cad5;\\n --rj-next-bg:\\n linear-gradient(\\n 180deg,\\n #f8fafc 0%,\\n #e8edf3 100%);\\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\\n --rj-font-family:\\n Inter,\\n \\\"Segoe UI\\\",\\n ui-sans-serif,\\n system-ui,\\n -apple-system,\\n BlinkMacSystemFont,\\n sans-serif;\\n --rj-root-width: min(296px, calc(100vw - 16px));\\n --rj-root-expanded-width: min(296px, calc(100vw - 16px));\\n --rj-dock-radius: 22px;\\n --rj-dock-padding: 6px;\\n --rj-dock-backdrop: blur(18px) saturate(1.5);\\n --rj-dock-inner-radius: 18px;\\n --rj-dock-inner-gap: 6px;\\n --rj-dock-inner-padding-right: 6px;\\n --rj-track-summary-radius: 18px;\\n --rj-track-summary-padding: 10px 12px;\\n --rj-expanded-radius: 22px;\\n --rj-expanded-padding: 8px;\\n --rj-expanded-backdrop: blur(18px) saturate(1.5);\\n --rj-shell-radius: 18px;\\n --rj-shell-padding: 12px;\\n --rj-screen-frame-radius: 15px;\\n --rj-screen-frame-padding: 8px;\\n --rj-screen-radius: 12px;\\n --rj-screen-padding: 8px;\\n --rj-player-radius: 10px;\\n --rj-chip-radius: 9999px;\\n --rj-chip-padding: 7px 10px;\\n --rj-volume-width: 72px;\\n --rj-expanded-control-gap: 8px;\\n --rj-next-radius: 16px;\\n --rj-next-padding: 10px 12px;\\n color: var(--rj-text);\\n z-index: 50;\\n width: var(--rj-root-width);\\n overflow: visible;\\n font-family: var(--rj-font-family);\\n}\\n.rj-root[data-theme=simple] {\\n --rj-text: #171717;\\n --rj-text-muted: #737373;\\n --rj-border: rgba(23, 23, 23, 0.1);\\n --rj-panel: #ffffff;\\n --rj-shadow: 0 16px 40px -28px rgba(23, 23, 23, 0.24);\\n --rj-gradient:\\n linear-gradient(\\n 180deg,\\n #ffffff 0%,\\n #f5f5f5 100%);\\n --rj-gradient-soft:\\n linear-gradient(\\n 180deg,\\n #fafafa 0%,\\n #f4f4f5 100%);\\n --rj-button-text: #262626;\\n --rj-dock-bg: #ffffff;\\n --rj-dock-shadow: 0 14px 32px -28px rgba(23, 23, 23, 0.2);\\n --rj-dock-inner-bg: #ffffff;\\n --rj-track-hover-bg: #f5f5f5;\\n --rj-level-gradient:\\n linear-gradient(\\n \\n 180deg,\\n #fafafa 0%,\\n #d4d4d8 52%,\\n #52525b 100% );\\n --rj-level-shadow-color: rgba(82, 82, 91, 0.12);\\n --rj-icon-hover-bg: rgba(23, 23, 23, 0.05);\\n --rj-shell-border: rgba(23, 23, 23, 0.08);\\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\\n --rj-screen-frame-border: #d4d4d8;\\n --rj-screen-frame-bg:\\n linear-gradient(\\n 180deg,\\n #ffffff 0%,\\n #f4f4f5 100%);\\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);\\n --rj-screen-border: #a1a1aa;\\n --rj-screen-bg: #18181b;\\n --rj-player-bg:\\n radial-gradient(\\n circle at top,\\n rgba(255, 255, 255, 0.08),\\n transparent 40%),\\n linear-gradient(\\n 180deg,\\n #27272a 0%,\\n #09090b 100%);\\n --rj-chip-border: #d4d4d8;\\n --rj-chip-shadow: none;\\n --rj-chip-primary-border: #171717;\\n --rj-chip-primary-text: #171717;\\n --rj-volume-track: #d4d4d8;\\n --rj-volume-thumb: #171717;\\n --rj-volume-thumb-shadow: none;\\n --rj-expanded-icon: #52525b;\\n --rj-expanded-icon-hover: #171717;\\n --rj-next-border: #e4e4e7;\\n --rj-next-bg:\\n linear-gradient(\\n 180deg,\\n #fafafa 0%,\\n #f4f4f5 100%);\\n --rj-next-shadow: none;\\n}\\n.rj-root[data-theme=simple][data-chrome=ride] .rj-expanded .rj-icon-button {\\n width: 24px;\\n height: 24px;\\n border-radius: 9999px;\\n background: transparent;\\n}\\n.rj-root[data-theme=simple][data-chrome=ride] .rj-expanded .rj-icon-button__icon {\\n width: 12px;\\n height: 12px;\\n}\\n.rj-root[data-theme=simple][data-chrome=wallet] .rj-expanded__utility {\\n gap: 8px;\\n}\\n.rj-root[data-theme=simple][data-chrome=wallet] .rj-expanded .rj-icon-button {\\n margin-left: 6px;\\n}\\n.rj-root[data-theme=sunset] {\\n --rj-text: #4a1635;\\n --rj-text-muted: #8b5b6d;\\n --rj-border: rgba(255, 255, 255, 0.3);\\n --rj-panel: rgba(255, 244, 237, 0.86);\\n --rj-shadow: 0 22px 48px -24px rgba(159, 18, 57, 0.28);\\n --rj-gradient:\\n linear-gradient(\\n 135deg,\\n #fff0d8 0%,\\n #ffc7a1 48%,\\n #ff9bb2 100%);\\n --rj-gradient-soft:\\n linear-gradient(\\n \\n 180deg,\\n rgba(255, 248, 240, 0.96) 0%,\\n rgba(255, 225, 213, 0.96) 100% );\\n --rj-button-text: #7a284f;\\n --rj-dock-bg: rgba(255, 241, 232, 0.82);\\n --rj-dock-shadow: 0 -8px 22px -16px rgba(190, 24, 93, 0.22), 0 12px 30px -18px rgba(249, 115, 22, 0.24);\\n --rj-dock-inner-bg:\\n linear-gradient(\\n \\n 135deg,\\n rgba(255, 249, 240, 0.96) 0%,\\n rgba(255, 228, 212, 0.96) 100% );\\n --rj-track-hover-bg: rgba(255, 255, 255, 0.45);\\n --rj-level-gradient:\\n linear-gradient(\\n \\n 180deg,\\n #fff7ed 0%,\\n #fb7185 48%,\\n #7c3aed 100% );\\n --rj-level-shadow-color: rgba(251, 113, 133, 0.2);\\n --rj-icon-hover-bg: rgba(190, 24, 93, 0.08);\\n --rj-shell-border: rgba(244, 114, 182, 0.15);\\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72);\\n --rj-screen-frame-border: #f9a8d4;\\n --rj-screen-frame-bg:\\n linear-gradient(\\n 180deg,\\n #fff1f2 0%,\\n #ffe4e6 100%);\\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.84);\\n --rj-screen-border: #be185d;\\n --rj-screen-bg: #431407;\\n --rj-player-bg:\\n radial-gradient(\\n circle at top,\\n rgba(253, 224, 71, 0.18),\\n transparent 40%),\\n radial-gradient(\\n \\n circle at 70% 30%,\\n rgba(244, 114, 182, 0.18),\\n transparent 35% ),\\n linear-gradient(\\n 180deg,\\n #7c2d12 0%,\\n #4c0519 100%);\\n --rj-chip-border: rgba(244, 114, 182, 0.28);\\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72);\\n --rj-chip-primary-border: rgba(190, 24, 93, 0.16);\\n --rj-chip-primary-text: #5b1032;\\n --rj-volume-track: #fda4af;\\n --rj-volume-thumb: #9f1239;\\n --rj-volume-thumb-shadow: 0 1px 6px rgba(159, 18, 57, 0.24);\\n --rj-expanded-icon: #9f1239;\\n --rj-expanded-icon-hover: #831843;\\n --rj-next-border: rgba(244, 114, 182, 0.2);\\n --rj-next-bg:\\n linear-gradient(\\n 180deg,\\n #fff1f2 0%,\\n #ffe4e6 100%);\\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82);\\n}\\n.rj-root[data-theme=ride] {\\n --rj-text: #f8fafc;\\n --rj-text-muted: rgba(226, 232, 240, 0.7);\\n --rj-border: rgba(255, 255, 255, 0.1);\\n --rj-panel: rgba(3, 7, 18, 0.96);\\n --rj-shadow: 0 24px 54px -34px rgba(2, 6, 23, 0.72);\\n --rj-gradient:\\n linear-gradient(\\n 180deg,\\n #f1f5f9 0%,\\n #d8e1ea 100%);\\n --rj-gradient-soft:\\n linear-gradient(\\n \\n 180deg,\\n rgba(15, 23, 42, 0.96) 0%,\\n rgba(2, 6, 23, 0.98) 100% );\\n --rj-button-text: #f8fafc;\\n --rj-dock-bg: #050816;\\n --rj-dock-shadow: 0 24px 54px -34px rgba(2, 6, 23, 0.72);\\n --rj-dock-inner-bg:\\n linear-gradient(\\n \\n 180deg,\\n rgba(255, 255, 255, 0.03) 0%,\\n rgba(255, 255, 255, 0) 100% );\\n --rj-track-hover-bg: rgba(255, 255, 255, 0.06);\\n --rj-level-gradient:\\n linear-gradient(\\n \\n 180deg,\\n #f8fafc 0%,\\n #cbd5e1 45%,\\n #64748b 100% );\\n --rj-level-shadow-color: rgba(148, 163, 184, 0.14);\\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.08);\\n --rj-shell-border: rgba(255, 255, 255, 0.08);\\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);\\n --rj-screen-frame-border: rgba(148, 163, 184, 0.24);\\n --rj-screen-frame-bg:\\n linear-gradient(\\n \\n 180deg,\\n rgba(30, 41, 59, 0.92) 0%,\\n rgba(15, 23, 42, 0.98) 100% );\\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);\\n --rj-screen-border: rgba(100, 116, 139, 0.4);\\n --rj-screen-bg: #050816;\\n --rj-player-bg:\\n radial-gradient(\\n circle at top,\\n rgba(255, 255, 255, 0.08),\\n transparent 40%),\\n linear-gradient(\\n 180deg,\\n rgba(15, 23, 42, 0.96) 0%,\\n rgba(2, 6, 23, 1) 100%);\\n --rj-chip-border: rgba(191, 219, 254, 0.58);\\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\\n --rj-chip-primary-border: rgba(255, 255, 255, 0.38);\\n --rj-chip-primary-text: #0f172a;\\n --rj-volume-track: rgba(148, 163, 184, 0.38);\\n --rj-volume-thumb: #ffffff;\\n --rj-volume-thumb-shadow: none;\\n --rj-expanded-icon: rgba(226, 232, 240, 0.82);\\n --rj-expanded-icon-hover: #ffffff;\\n --rj-next-border: rgba(255, 255, 255, 0.1);\\n --rj-next-bg:\\n linear-gradient(\\n \\n 180deg,\\n rgba(15, 23, 42, 0.96) 0%,\\n rgba(2, 6, 23, 0.98) 100% );\\n --rj-next-shadow: none;\\n}\\n.rj-root[data-chrome=wallet] {\\n --rj-dock-radius: 26px;\\n --rj-dock-padding: 6px;\\n --rj-dock-inner-radius: 20px;\\n --rj-dock-inner-gap: 8px;\\n --rj-dock-inner-padding-right: 0;\\n --rj-track-summary-radius: 20px;\\n --rj-track-summary-padding: 12px;\\n --rj-expanded-radius: 26px;\\n --rj-expanded-padding: 8px;\\n --rj-shell-radius: 20px;\\n --rj-shell-padding: 12px;\\n --rj-screen-frame-radius: 18px;\\n --rj-screen-frame-padding: 7px;\\n --rj-screen-radius: 14px;\\n --rj-screen-padding: 7px;\\n --rj-player-radius: 12px;\\n --rj-chip-radius: 14px;\\n --rj-chip-padding: 7px 10px;\\n --rj-volume-width: 68px;\\n --rj-expanded-control-gap: 8px;\\n --rj-next-radius: 16px;\\n --rj-next-padding: 9px 12px;\\n}\\n.rj-root[data-chrome=ride] {\\n --rj-font-family:\\n \\\"IBM Plex Sans\\\",\\n \\\"Segoe UI\\\",\\n ui-sans-serif,\\n system-ui,\\n -apple-system,\\n BlinkMacSystemFont,\\n sans-serif;\\n --rj-dock-radius: 18px;\\n --rj-dock-padding: 4px;\\n --rj-dock-backdrop: none;\\n --rj-dock-inner-radius: 14px;\\n --rj-dock-inner-gap: 4px;\\n --rj-dock-inner-padding-right: 4px;\\n --rj-track-summary-radius: 12px;\\n --rj-track-summary-padding: 12px;\\n --rj-expanded-radius: 18px;\\n --rj-expanded-padding: 6px;\\n --rj-expanded-backdrop: none;\\n --rj-shell-radius: 14px;\\n --rj-shell-padding: 12px;\\n --rj-screen-frame-radius: 12px;\\n --rj-screen-frame-padding: 6px;\\n --rj-screen-radius: 10px;\\n --rj-screen-padding: 6px;\\n --rj-player-radius: 8px;\\n --rj-chip-radius: 12px;\\n --rj-chip-padding: 10px 12px;\\n --rj-volume-width: 88px;\\n --rj-expanded-control-gap: 6px;\\n --rj-next-radius: 14px;\\n --rj-next-padding: 10px 12px;\\n}\\n.rj-root[data-theme=glass][data-chrome=wallet] {\\n --rj-text: #0f172a;\\n --rj-text-muted: #64748b;\\n --rj-border: rgba(191, 219, 254, 0.72);\\n --rj-panel: rgba(245, 249, 255, 0.96);\\n --rj-shadow: 0 22px 46px -28px rgba(37, 99, 235, 0.22);\\n --rj-gradient:\\n linear-gradient(\\n 135deg,\\n #eff6ff 0%,\\n #dbeafe 100%);\\n --rj-gradient-soft:\\n linear-gradient(\\n 180deg,\\n #ffffff 0%,\\n #eef4ff 100%);\\n --rj-button-text: #1e3a8a;\\n --rj-dock-bg: rgba(231, 240, 255, 0.86);\\n --rj-dock-shadow: 0 16px 36px -28px rgba(59, 130, 246, 0.26), inset 0 1px 0 rgba(255, 255, 255, 0.92);\\n --rj-dock-inner-bg: transparent;\\n --rj-track-hover-bg: rgba(219, 234, 254, 0.58);\\n --rj-level-gradient:\\n linear-gradient(\\n \\n 180deg,\\n #60a5fa 0%,\\n #2563eb 60%,\\n #1d4ed8 100% );\\n --rj-level-shadow-color: rgba(96, 165, 250, 0.2);\\n --rj-icon-hover-bg: rgba(59, 130, 246, 0.1);\\n --rj-shell-border: rgba(191, 219, 254, 0.88);\\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96);\\n --rj-screen-frame-border: #bfdbfe;\\n --rj-screen-frame-bg:\\n linear-gradient(\\n 180deg,\\n #ffffff 0%,\\n #eff6ff 100%);\\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96);\\n --rj-screen-border: #93c5fd;\\n --rj-screen-bg: #dbeafe;\\n --rj-player-bg:\\n radial-gradient(\\n circle at top,\\n rgba(255, 255, 255, 0.48),\\n transparent 48%),\\n linear-gradient(\\n 180deg,\\n #bfdbfe 0%,\\n #60a5fa 100%);\\n --rj-chip-border: rgba(147, 197, 253, 0.9);\\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.92);\\n --rj-chip-primary-border: rgba(59, 130, 246, 0.28);\\n --rj-chip-primary-text: #1e3a8a;\\n --rj-volume-track: #bfdbfe;\\n --rj-volume-thumb: #2563eb;\\n --rj-volume-thumb-shadow: 0 2px 8px rgba(37, 99, 235, 0.18);\\n --rj-expanded-icon: #2563eb;\\n --rj-expanded-icon-hover: #1d4ed8;\\n --rj-next-border: rgba(147, 197, 253, 0.72);\\n --rj-next-bg:\\n linear-gradient(\\n 180deg,\\n #ffffff 0%,\\n #eff6ff 100%);\\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.96);\\n}\\n.rj-root--inline {\\n z-index: 20;\\n width: min(100%, var(--rj-root-width));\\n max-width: 100%;\\n}\\n.rj-dock {\\n border: 1px solid var(--rj-border);\\n border-radius: var(--rj-dock-radius);\\n background: var(--rj-dock-bg);\\n padding: var(--rj-dock-padding);\\n box-shadow: var(--rj-dock-shadow);\\n backdrop-filter: var(--rj-dock-backdrop);\\n}\\n.rj-root--expanded .rj-dock {\\n width: var(--rj-root-expanded-width);\\n max-width: calc(100vw - 16px);\\n}\\n.rj-root--inline.rj-root--expanded .rj-dock {\\n width: min(100%, var(--rj-root-expanded-width));\\n max-width: 100%;\\n}\\n.rj-root[data-position$=right].rj-root--expanded .rj-dock {\\n margin-left: calc(var(--rj-root-width) - var(--rj-root-expanded-width));\\n}\\n.rj-dock__inner {\\n display: flex;\\n align-items: center;\\n gap: var(--rj-dock-inner-gap);\\n border-radius: var(--rj-dock-inner-radius);\\n background: var(--rj-dock-inner-bg);\\n padding-right: var(--rj-dock-inner-padding-right);\\n}\\n.rj-track-summary {\\n display: flex;\\n min-width: 0;\\n flex: 1;\\n align-items: center;\\n gap: 10px;\\n border: 0;\\n background: transparent;\\n border-radius: var(--rj-track-summary-radius);\\n padding: var(--rj-track-summary-padding);\\n text-align: left;\\n color: inherit;\\n cursor: pointer;\\n transition: background-color 0.2s ease;\\n}\\n.rj-track-summary:hover {\\n background: var(--rj-track-hover-bg);\\n}\\n.rj-track-summary--empty {\\n cursor: default;\\n}\\n.rj-track-summary--empty:hover {\\n background: transparent;\\n}\\n.rj-level-meter {\\n display: flex;\\n width: 28px;\\n height: 28px;\\n flex-shrink: 0;\\n align-items: flex-end;\\n justify-content: center;\\n gap: 4px;\\n}\\n.rj-level-bar {\\n width: 4px;\\n border-radius: 9999px;\\n background: var(--rj-level-gradient);\\n box-shadow: 0 0 0 1px var(--rj-level-shadow-color);\\n opacity: 0.55;\\n transform-origin: bottom center;\\n}\\n.rj-level-bar--playing {\\n opacity: 1;\\n animation: rj-jukebox-bounce 0.9s ease-in-out infinite;\\n}\\n.rj-track-summary__copy {\\n min-width: 0;\\n flex: 1;\\n}\\n.rj-track-summary__title {\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n font-size: 12px;\\n font-weight: 700;\\n letter-spacing: -0.01em;\\n}\\n.rj-track-summary__artist {\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n font-size: 11px;\\n color: var(--rj-text-muted);\\n}\\n.rj-chevron {\\n display: inline-flex;\\n height: 16px;\\n width: 16px;\\n flex-shrink: 0;\\n align-items: center;\\n justify-content: center;\\n color: var(--rj-text-muted);\\n}\\n.rj-chevron__icon {\\n display: block;\\n height: 100%;\\n width: 100%;\\n transform: rotate(0deg);\\n transition: transform 0.2s ease;\\n}\\n.rj-chevron__icon--expanded {\\n transform: rotate(180deg);\\n}\\n.rj-icon-button {\\n display: inline-flex;\\n height: 32px;\\n width: 32px;\\n flex-shrink: 0;\\n align-items: center;\\n justify-content: center;\\n border: 0;\\n border-radius: 9999px;\\n background: transparent;\\n color: var(--rj-text-muted);\\n cursor: pointer;\\n transition:\\n background-color 0.2s ease,\\n color 0.2s ease,\\n opacity 0.2s ease;\\n}\\n.rj-icon-button:hover {\\n background: var(--rj-icon-hover-bg);\\n color: var(--rj-text);\\n}\\n.rj-icon-button:disabled {\\n cursor: not-allowed;\\n opacity: 0.45;\\n}\\n.rj-icon-button__icon {\\n display: inline-flex;\\n height: 16px;\\n width: 16px;\\n}\\n.rj-icon-button__icon svg {\\n display: block;\\n height: 100%;\\n width: 100%;\\n}\\n.rj-expanded {\\n position: absolute;\\n width: var(--rj-root-expanded-width);\\n max-width: calc(100vw - 16px);\\n right: 0;\\n bottom: calc(100% + 10px);\\n left: auto;\\n z-index: 2;\\n border: 1px solid var(--rj-border);\\n border-radius: var(--rj-expanded-radius);\\n background: var(--rj-panel);\\n padding: var(--rj-expanded-padding);\\n box-shadow: var(--rj-shadow);\\n backdrop-filter: var(--rj-expanded-backdrop);\\n}\\n.rj-root--inline .rj-expanded {\\n width: min(100%, var(--rj-root-expanded-width));\\n max-width: 100%;\\n}\\n.rj-root[data-position$=left] .rj-expanded {\\n right: auto;\\n left: 0;\\n}\\n.rj-expanded--hidden {\\n visibility: hidden;\\n pointer-events: none;\\n opacity: 0;\\n}\\n.rj-root[data-position^=top] .rj-expanded {\\n top: calc(100% + 10px);\\n bottom: auto;\\n}\\n.rj-expanded__shell {\\n border-radius: var(--rj-shell-radius);\\n border: 1px solid var(--rj-shell-border);\\n background: var(--rj-gradient-soft);\\n padding: var(--rj-shell-padding);\\n box-shadow: var(--rj-shell-inset-shadow);\\n}\\n.rj-expanded__screen-frame {\\n border-radius: var(--rj-screen-frame-radius);\\n border: 1px solid var(--rj-screen-frame-border);\\n background: var(--rj-screen-frame-bg);\\n padding: var(--rj-screen-frame-padding);\\n box-shadow: var(--rj-screen-frame-shadow);\\n}\\n.rj-expanded__screen {\\n border-radius: var(--rj-screen-radius);\\n border: 1px solid var(--rj-screen-border);\\n background: var(--rj-screen-bg);\\n padding: var(--rj-screen-padding);\\n}\\n.rj-expanded__player {\\n overflow: hidden;\\n width: 100%;\\n aspect-ratio: 16 / 9;\\n border-radius: var(--rj-player-radius);\\n background: var(--rj-player-bg);\\n}\\n.rj-expanded__meta {\\n margin-top: 12px;\\n}\\n.rj-expanded__titles {\\n margin-bottom: 10px;\\n}\\n.rj-expanded__title {\\n font-size: 13px;\\n font-weight: 700;\\n}\\n.rj-expanded__artist {\\n margin-top: 2px;\\n font-size: 11px;\\n color: var(--rj-text-muted);\\n}\\n.rj-expanded__controls {\\n display: flex;\\n flex-wrap: nowrap;\\n min-width: 0;\\n align-items: center;\\n gap: var(--rj-expanded-control-gap);\\n}\\n.rj-expanded__transport,\\n.rj-expanded__utility {\\n display: flex;\\n min-width: 0;\\n align-items: center;\\n gap: var(--rj-expanded-control-gap);\\n}\\n.rj-expanded__transport {\\n flex: 0 0 auto;\\n}\\n.rj-expanded__utility {\\n flex: 1 1 auto;\\n margin-left: auto;\\n justify-content: flex-end;\\n}\\n.rj-chip-button {\\n border: 1px solid var(--rj-chip-border);\\n border-radius: var(--rj-chip-radius);\\n background: var(--rj-gradient-soft);\\n padding: var(--rj-chip-padding);\\n color: var(--rj-button-text);\\n font-size: 10px;\\n font-weight: 600;\\n cursor: pointer;\\n box-shadow: var(--rj-chip-shadow);\\n transition: transform 0.2s ease, opacity 0.2s ease;\\n}\\n.rj-chip-button:hover {\\n transform: translateY(-1px);\\n}\\n.rj-chip-button:disabled {\\n cursor: not-allowed;\\n opacity: 0.45;\\n transform: none;\\n}\\n.rj-chip-button--primary {\\n border-color: var(--rj-chip-primary-border);\\n background: var(--rj-gradient);\\n color: var(--rj-chip-primary-text);\\n font-weight: 700;\\n}\\n.rj-volume {\\n min-width: 0;\\n width: var(--rj-volume-width);\\n flex: 1 1 var(--rj-volume-width);\\n height: 4px;\\n cursor: pointer;\\n appearance: none;\\n border-radius: 9999px;\\n background: var(--rj-volume-track);\\n outline: none;\\n}\\n.rj-volume::-webkit-slider-runnable-track {\\n height: 4px;\\n border-radius: 9999px;\\n background: var(--rj-volume-track);\\n}\\n.rj-volume::-webkit-slider-thumb {\\n width: 12px;\\n height: 12px;\\n margin-top: -4px;\\n appearance: none;\\n border: 0;\\n border-radius: 9999px;\\n background: var(--rj-volume-thumb);\\n box-shadow: var(--rj-volume-thumb-shadow);\\n}\\n.rj-volume::-moz-range-track {\\n height: 4px;\\n border-radius: 9999px;\\n background: var(--rj-volume-track);\\n}\\n.rj-volume::-moz-range-thumb {\\n width: 12px;\\n height: 12px;\\n border: 0;\\n border-radius: 9999px;\\n background: var(--rj-volume-thumb);\\n box-shadow: var(--rj-volume-thumb-shadow);\\n}\\n.rj-expanded .rj-icon-button {\\n margin-left: 4px;\\n height: 24px;\\n width: 24px;\\n color: var(--rj-expanded-icon);\\n}\\n.rj-expanded .rj-icon-button:hover {\\n background: transparent;\\n color: var(--rj-expanded-icon-hover);\\n}\\n.rj-expanded .rj-icon-button__icon {\\n height: 12px;\\n width: 12px;\\n}\\n.rj-expanded__counter {\\n margin-left: auto;\\n flex-shrink: 0;\\n font-size: 10px;\\n color: var(--rj-text-muted);\\n white-space: nowrap;\\n font-family:\\n \\\"SF Mono\\\",\\n SFMono-Regular,\\n ui-monospace,\\n Menlo,\\n Consolas,\\n monospace;\\n}\\n.rj-next-track {\\n display: flex;\\n align-items: center;\\n justify-content: space-between;\\n gap: 8px;\\n margin-top: 10px;\\n border-radius: var(--rj-next-radius);\\n border: 1px solid var(--rj-next-border);\\n background: var(--rj-next-bg);\\n padding: var(--rj-next-padding);\\n font-size: 10px;\\n color: var(--rj-text-muted);\\n box-shadow: var(--rj-next-shadow);\\n}\\n.rj-next-track__label {\\n font-weight: 700;\\n}\\n.rj-next-track__button {\\n min-width: 0;\\n overflow: hidden;\\n text-overflow: ellipsis;\\n white-space: nowrap;\\n border: 0;\\n background: transparent;\\n color: var(--rj-button-text);\\n cursor: pointer;\\n}\\n.rj-next-track__button:disabled {\\n cursor: not-allowed;\\n opacity: 0.45;\\n}\\n.rj-root[data-chrome=wallet] .rj-dock {\\n border-color: var(--rj-border);\\n}\\n.rj-root[data-chrome=wallet] .rj-dock__inner {\\n align-items: center;\\n}\\n.rj-root[data-chrome=wallet] .rj-track-summary {\\n border: 1px solid var(--rj-border);\\n background: var(--rj-gradient-soft);\\n box-shadow: var(--rj-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.92);\\n}\\n.rj-root[data-chrome=wallet] .rj-level-meter {\\n width: 32px;\\n height: 32px;\\n border-radius: 14px;\\n background: var(--rj-gradient);\\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.94);\\n}\\n.rj-root[data-chrome=wallet] .rj-chevron {\\n width: 22px;\\n height: 22px;\\n border-radius: 9999px;\\n background: var(--rj-track-hover-bg);\\n}\\n.rj-root[data-chrome=wallet] .rj-track-summary__title {\\n font-size: 13px;\\n letter-spacing: -0.02em;\\n}\\n.rj-root[data-chrome=wallet] .rj-track-summary__artist {\\n margin-top: 3px;\\n font-size: 10px;\\n}\\n.rj-root[data-chrome=wallet] .rj-dock .rj-icon-button {\\n width: 34px;\\n height: 34px;\\n border: 1px solid var(--rj-border);\\n border-radius: 14px;\\n background: var(--rj-gradient-soft);\\n box-shadow: var(--rj-chip-shadow);\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded__meta {\\n margin-top: 10px;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded__controls {\\n align-items: center;\\n gap: 8px;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded__transport,\\n.rj-root[data-chrome=wallet] .rj-expanded__utility {\\n flex: 0 1 auto;\\n margin-left: 0;\\n min-width: 0;\\n border: 0;\\n background: transparent;\\n padding: 0;\\n box-shadow: none;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded__transport {\\n display: grid;\\n grid-template-columns: 30px auto 30px;\\n gap: 6px;\\n align-items: center;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded__utility {\\n margin-left: auto;\\n flex: 1 1 auto;\\n justify-content: flex-end;\\n gap: 6px;\\n}\\n.rj-root[data-chrome=wallet] .rj-chip-button {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n font-size: 11px;\\n min-height: 32px;\\n padding-inline: 10px;\\n border-radius: 9999px;\\n text-align: center;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded__transport .rj-chip-button:not(.rj-chip-button--primary) {\\n width: 30px;\\n min-width: 30px;\\n padding-inline: 0;\\n}\\n.rj-root[data-chrome=wallet] .rj-chip-button--primary {\\n justify-self: center;\\n min-width: 48px;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded .rj-icon-button {\\n margin-left: 0;\\n width: 18px;\\n height: 18px;\\n border-radius: 9999px;\\n background: transparent;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded .rj-icon-button__icon {\\n width: 14px;\\n height: 14px;\\n}\\n.rj-root[data-chrome=wallet] .rj-volume {\\n width: auto;\\n min-width: 40px;\\n}\\n.rj-root[data-chrome=wallet] .rj-expanded__counter {\\n margin-left: 0;\\n flex: 0 0 auto;\\n padding: 0;\\n border: 0;\\n background: transparent;\\n}\\n.rj-root[data-chrome=wallet] .rj-next-track {\\n gap: 10px;\\n}\\n.rj-root[data-chrome=wallet] .rj-next-track__label {\\n border-radius: 10px;\\n background: var(--rj-track-hover-bg);\\n padding: 4px 8px;\\n}\\n.rj-root[data-chrome=ride] .rj-dock {\\n border-color: var(--rj-border);\\n}\\n.rj-root[data-chrome=ride] .rj-track-summary__title,\\n.rj-root[data-chrome=ride] .rj-expanded__title,\\n.rj-root[data-chrome=ride] .rj-next-track__label {\\n letter-spacing: 0.06em;\\n text-transform: uppercase;\\n}\\n.rj-root[data-chrome=ride] .rj-track-summary__title {\\n font-size: 11px;\\n}\\n.rj-root[data-chrome=ride] .rj-track-summary__artist {\\n font-size: 10px;\\n}\\n.rj-root[data-chrome=ride] .rj-dock .rj-icon-button {\\n border-radius: 12px;\\n background: var(--rj-track-hover-bg);\\n}\\n.rj-root[data-chrome=ride] .rj-expanded {\\n overflow: hidden;\\n border-color: var(--rj-border);\\n background:\\n radial-gradient(\\n circle at top left,\\n var(--rj-track-hover-bg),\\n transparent 32%),\\n var(--rj-next-bg);\\n box-shadow: var(--rj-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.04);\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__shell {\\n position: relative;\\n background:\\n linear-gradient(\\n 180deg,\\n rgba(255, 255, 255, 0.04),\\n rgba(255, 255, 255, 0)),\\n var(--rj-panel);\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__shell::before {\\n content: \\\"\\\";\\n position: absolute;\\n top: 12px;\\n right: 12px;\\n width: 58px;\\n height: 58px;\\n border-radius: 50%;\\n background:\\n radial-gradient(\\n circle,\\n rgba(255, 255, 255, 0.12),\\n transparent 62%);\\n opacity: 0.75;\\n pointer-events: none;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__screen-frame {\\n border-color: var(--rj-screen-frame-border);\\n background: var(--rj-screen-frame-bg);\\n box-shadow: var(--rj-screen-frame-shadow), 0 16px 24px -24px rgba(0, 0, 0, 0.28);\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__screen {\\n border-color: var(--rj-screen-border);\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__meta {\\n margin-top: 14px;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__titles {\\n display: flex;\\n align-items: flex-start;\\n justify-content: space-between;\\n gap: 12px;\\n margin-bottom: 12px;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__artist {\\n max-width: 96px;\\n margin-top: 0;\\n text-align: right;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__controls {\\n display: flex;\\n flex-wrap: nowrap;\\n align-items: center;\\n gap: 8px;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__transport,\\n.rj-root[data-chrome=ride] .rj-expanded__utility {\\n margin-left: 0;\\n min-width: 0;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__transport {\\n display: grid;\\n grid-template-columns: 30px auto 30px;\\n gap: 4px;\\n align-items: center;\\n justify-content: flex-start;\\n width: auto;\\n max-width: none;\\n border: 1px solid var(--rj-border);\\n border-radius: 14px;\\n background: var(--rj-panel);\\n padding: 6px;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__utility {\\n display: flex;\\n flex: 1 1 auto;\\n margin-left: auto;\\n justify-content: flex-end;\\n gap: 8px;\\n align-items: center;\\n}\\n.rj-root[data-chrome=ride] .rj-chip-button {\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n min-width: 0;\\n min-height: 30px;\\n padding-inline: 0;\\n box-shadow: none;\\n text-align: center;\\n}\\n.rj-root[data-chrome=ride] .rj-chip-button--primary {\\n justify-self: center;\\n width: auto;\\n min-width: 46px;\\n padding-inline: 8px;\\n border-color: var(--rj-chip-primary-border);\\n border-radius: 13px;\\n background: var(--rj-gradient-soft);\\n color: var(--rj-chip-primary-text);\\n font-size: 9px;\\n line-height: 1;\\n letter-spacing: 0;\\n text-transform: none;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__transport .rj-chip-button:not(.rj-chip-button--primary) {\\n width: 30px;\\n min-width: 30px;\\n padding-inline: 0;\\n border-color: var(--rj-chip-border);\\n border-radius: 12px;\\n background: var(--rj-gradient-soft);\\n color: var(--rj-button-text);\\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);\\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.92), 0 8px 16px -14px rgba(15, 23, 42, 0.22);\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__transport .rj-chip-button:not(.rj-chip-button--primary):hover {\\n transform: translateY(0);\\n filter: brightness(1.02);\\n}\\n.rj-root[data-chrome=ride] .rj-expanded .rj-icon-button {\\n margin-left: 0;\\n width: 36px;\\n height: 36px;\\n border-radius: 12px;\\n background: var(--rj-track-hover-bg);\\n}\\n.rj-root[data-chrome=ride] .rj-volume {\\n width: auto;\\n min-width: 44px;\\n}\\n.rj-root[data-chrome=ride] .rj-expanded__counter {\\n margin-left: 0;\\n padding: 0;\\n border-top: 0;\\n letter-spacing: 0.12em;\\n text-transform: uppercase;\\n}\\n.rj-root[data-chrome=ride] .rj-next-track {\\n align-items: flex-start;\\n gap: 12px;\\n background: var(--rj-next-bg);\\n}\\n.rj-root[data-chrome=ride] .rj-next-track__button {\\n text-align: right;\\n}\\n@media (prefers-color-scheme: dark) {\\n .rj-root[data-theme=glass] {\\n --rj-text: #f8fafc;\\n --rj-text-muted: #b8c2cf;\\n --rj-border: rgba(255, 255, 255, 0.1);\\n --rj-panel: rgba(0, 0, 0, 0.48);\\n --rj-shadow: 0 22px 60px -30px rgba(15, 23, 42, 0.42);\\n --rj-gradient:\\n linear-gradient(\\n 180deg,\\n #f1f5f9 0%,\\n #d8e1ea 100%);\\n --rj-gradient-soft:\\n linear-gradient(\\n 180deg,\\n #2b3440 0%,\\n #1c232d 100%);\\n --rj-button-text: #e2e8f0;\\n --rj-dock-bg: rgba(0, 0, 0, 0.52);\\n --rj-dock-inner-bg:\\n linear-gradient(\\n \\n 180deg,\\n rgba(37, 42, 49, 0.94) 0%,\\n rgba(22, 27, 34, 0.98) 100% );\\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.08);\\n --rj-shell-border: rgba(255, 255, 255, 0.06);\\n --rj-next-border: rgba(255, 255, 255, 0.08);\\n --rj-screen-frame-border: rgba(255, 255, 255, 0.08);\\n --rj-expanded-icon: #94a3b8;\\n --rj-expanded-icon-hover: #ffffff;\\n --rj-volume-track: #475569;\\n --rj-volume-thumb: #e2e8f0;\\n }\\n .rj-root[data-theme=simple] {\\n --rj-text: #fafafa;\\n --rj-text-muted: #a1a1aa;\\n --rj-border: rgba(255, 255, 255, 0.08);\\n --rj-panel: rgba(10, 10, 11, 0.94);\\n --rj-shadow: 0 24px 54px -30px rgba(0, 0, 0, 0.58);\\n --rj-gradient:\\n linear-gradient(\\n 180deg,\\n #fafafa 0%,\\n #d4d4d8 100%);\\n --rj-gradient-soft:\\n linear-gradient(\\n 180deg,\\n #27272a 0%,\\n #18181b 100%);\\n --rj-button-text: #f4f4f5;\\n --rj-dock-bg: rgba(10, 10, 11, 0.92);\\n --rj-dock-shadow: 0 18px 40px -26px rgba(0, 0, 0, 0.5);\\n --rj-dock-inner-bg: #111113;\\n --rj-track-hover-bg: rgba(255, 255, 255, 0.04);\\n --rj-level-gradient:\\n linear-gradient(\\n \\n 180deg,\\n #fafafa 0%,\\n #a1a1aa 52%,\\n #3f3f46 100% );\\n --rj-level-shadow-color: rgba(255, 255, 255, 0.08);\\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.07);\\n --rj-shell-border: rgba(255, 255, 255, 0.08);\\n --rj-shell-inset-shadow: none;\\n --rj-screen-frame-border: #3f3f46;\\n --rj-screen-frame-bg:\\n linear-gradient(\\n 180deg,\\n #27272a 0%,\\n #18181b 100%);\\n --rj-screen-frame-shadow: none;\\n --rj-screen-border: #52525b;\\n --rj-screen-bg: #09090b;\\n --rj-player-bg:\\n radial-gradient(\\n \\n circle at top,\\n rgba(255, 255, 255, 0.06),\\n transparent 38% ),\\n linear-gradient(\\n 180deg,\\n #18181b 0%,\\n #020617 100%);\\n --rj-chip-border: #3f3f46;\\n --rj-chip-primary-border: #fafafa;\\n --rj-chip-primary-text: #171717;\\n --rj-volume-track: #3f3f46;\\n --rj-volume-thumb: #fafafa;\\n --rj-expanded-icon: #d4d4d8;\\n --rj-expanded-icon-hover: #ffffff;\\n --rj-next-border: #3f3f46;\\n --rj-next-bg:\\n linear-gradient(\\n 180deg,\\n #27272a 0%,\\n #18181b 100%);\\n }\\n .rj-root[data-theme=sunset] {\\n --rj-text: #fff1f2;\\n --rj-text-muted: #fecdd3;\\n --rj-border: rgba(255, 255, 255, 0.1);\\n --rj-panel: rgba(76, 5, 25, 0.78);\\n --rj-shadow: 0 24px 56px -28px rgba(76, 5, 25, 0.5);\\n --rj-gradient:\\n linear-gradient(\\n \\n 135deg,\\n #fde68a 0%,\\n #fb7185 54%,\\n #7c3aed 100% );\\n --rj-gradient-soft:\\n linear-gradient(\\n \\n 180deg,\\n rgba(136, 19, 55, 0.92) 0%,\\n rgba(76, 5, 25, 0.96) 100% );\\n --rj-button-text: #fff7ed;\\n --rj-dock-bg: rgba(76, 5, 25, 0.74);\\n --rj-dock-shadow: 0 -8px 24px -18px rgba(244, 114, 182, 0.24), 0 14px 34px -18px rgba(126, 34, 206, 0.3);\\n --rj-dock-inner-bg:\\n linear-gradient(\\n \\n 135deg,\\n rgba(136, 19, 55, 0.92) 0%,\\n rgba(76, 5, 25, 0.96) 100% );\\n --rj-track-hover-bg: rgba(255, 255, 255, 0.06);\\n --rj-level-gradient:\\n linear-gradient(\\n \\n 180deg,\\n #fde68a 0%,\\n #fb7185 52%,\\n #7c3aed 100% );\\n --rj-level-shadow-color: rgba(253, 224, 71, 0.16);\\n --rj-icon-hover-bg: rgba(255, 255, 255, 0.08);\\n --rj-shell-border: rgba(255, 255, 255, 0.08);\\n --rj-shell-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);\\n --rj-screen-frame-border: rgba(251, 113, 133, 0.4);\\n --rj-screen-frame-bg:\\n linear-gradient(\\n 180deg,\\n #881337 0%,\\n #4c0519 100%);\\n --rj-screen-frame-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\\n --rj-screen-border: rgba(253, 186, 116, 0.48);\\n --rj-screen-bg: #19030d;\\n --rj-player-bg:\\n radial-gradient(\\n circle at top,\\n rgba(253, 224, 71, 0.2),\\n transparent 40%),\\n radial-gradient(\\n \\n circle at 70% 30%,\\n rgba(192, 132, 252, 0.18),\\n transparent 36% ),\\n linear-gradient(\\n 180deg,\\n #4c0519 0%,\\n #1e1b4b 100%);\\n --rj-chip-border: rgba(253, 186, 116, 0.2);\\n --rj-chip-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\\n --rj-chip-primary-border: rgba(253, 224, 71, 0.18);\\n --rj-chip-primary-text: #4c0519;\\n --rj-volume-track: rgba(251, 113, 133, 0.48);\\n --rj-volume-thumb: #fde68a;\\n --rj-volume-thumb-shadow: 0 1px 8px rgba(253, 224, 71, 0.18);\\n --rj-expanded-icon: #fda4af;\\n --rj-expanded-icon-hover: #fff1f2;\\n --rj-next-border: rgba(251, 113, 133, 0.22);\\n --rj-next-bg:\\n linear-gradient(\\n \\n 180deg,\\n rgba(136, 19, 55, 0.96) 0%,\\n rgba(76, 5, 25, 0.96) 100% );\\n --rj-next-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);\\n }\\n .rj-root[data-chrome=ride] {\\n --rj-volume-thumb-shadow: none;\\n }\\n}\\n@media (max-width: 640px) {\\n .rj-root {\\n --rj-root-width: min(100vw - 16px, 320px);\\n --rj-root-expanded-width: min(100vw - 16px, 320px);\\n }\\n .rj-root--inline {\\n --rj-root-width: 100%;\\n --rj-root-expanded-width: 100%;\\n }\\n .rj-root--inline .rj-expanded {\\n padding: 6px;\\n }\\n .rj-root--inline .rj-expanded__shell {\\n padding: 10px;\\n }\\n .rj-root--inline .rj-next-track {\\n flex-wrap: wrap;\\n align-items: flex-start;\\n }\\n .rj-root--inline .rj-next-track__button {\\n width: 100%;\\n text-align: left;\\n }\\n .rj-root--inline[data-chrome=wallet] .rj-expanded__utility {\\n gap: 8px;\\n }\\n .rj-root--inline[data-chrome=wallet] .rj-expanded__counter {\\n margin-left: auto;\\n }\\n .rj-root--inline[data-chrome=ride] .rj-expanded__titles {\\n flex-direction: column;\\n gap: 4px;\\n }\\n .rj-root--inline[data-chrome=ride] .rj-expanded__artist {\\n max-width: none;\\n text-align: left;\\n }\\n .rj-root--inline[data-chrome=ride] .rj-expanded__transport {\\n width: auto;\\n }\\n .rj-root--inline[data-chrome=ride] .rj-expanded__counter {\\n justify-self: auto;\\n }\\n}\\n@keyframes rj-jukebox-bounce {\\n 0%, 100% {\\n transform: scaleY(0.35);\\n }\\n 50% {\\n transform: scaleY(1);\\n }\\n}\\n\")","import {\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport clsx from \"clsx\";\n\nimport { useJukeboxPlayer } from \"../hooks/useJukeboxPlayer\";\nimport {\n DEFAULT_CHROME,\n DEFAULT_POSITION,\n DEFAULT_THEME,\n getEffectiveChrome,\n getNextTrackIndex,\n getPositionStyle,\n LEVEL_BAR_ANIMATION_DELAY_MS,\n LEVEL_BAR_HEIGHTS,\n LEVEL_BAR_REST_HEIGHT,\n type JukeboxExpandedRenderProps,\n type JukeboxProps,\n type JukeboxTrack,\n} from \"../lib/shared\";\nimport { JukeboxExpandedPlayer } from \"./JukeboxExpandedPlayer\";\nimport \"../styles/jukebox.css\";\n\nfunction subscribeToClientRender() {\n return () => undefined;\n}\n\nfunction getClientRenderSnapshot() {\n return true;\n}\n\nfunction getServerRenderSnapshot() {\n return false;\n}\n\nfunction VolumeIcon({ isMuted }: { isMuted: boolean }) {\n if (isMuted) {\n return (\n <svg viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z\" />\n <path d=\"M10.28 5.22a.75.75 0 0 1 1.06 0L12 5.88l.66-.66a.75.75 0 1 1 1.06 1.06l-.66.66.66.66a.75.75 0 1 1-1.06 1.06L12 7.94l-.66.66a.75.75 0 0 1-1.06-1.06l.66-.66-.66-.66a.75.75 0 0 1 0-1.06Z\" />\n </svg>\n );\n }\n\n return (\n <svg viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M7.06 3.22a.75.75 0 0 1 1.19.61v8.34a.75.75 0 0 1-1.19.61L4.26 10.5H2.75A.75.75 0 0 1 2 9.75v-3.5c0-.41.34-.75.75-.75h1.51l2.8-2.28Z\" />\n <path d=\"M10.5 5.02a.75.75 0 0 1 1.06 0 3.86 3.86 0 0 1 0 5.46.75.75 0 1 1-1.06-1.06 2.36 2.36 0 0 0 0-3.34.75.75 0 0 1 0-1.06Z\" />\n <path d=\"M11.9 3.62a.75.75 0 0 1 1.06 0 5.84 5.84 0 0 1 0 8.76.75.75 0 1 1-1.06-1.06 4.34 4.34 0 0 0 0-6.64.75.75 0 0 1 0-1.06Z\" />\n </svg>\n );\n}\n\nfunction ChevronIcon({ isExpanded }: { isExpanded: boolean }) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n className={clsx(\"rj-chevron__icon\", {\n \"rj-chevron__icon--expanded\": isExpanded,\n })}>\n <path\n d=\"M4 6.5 8 10l4-3.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n\nfunction TrackSummary({\n currentTrack,\n isExpanded,\n isPlaying,\n onToggleExpanded,\n}: {\n currentTrack: JukeboxTrack | undefined;\n isExpanded: boolean;\n isPlaying: boolean;\n onToggleExpanded: () => void;\n}) {\n if (!currentTrack) {\n return (\n <div className=\"rj-track-summary rj-track-summary--empty\">\n <div className=\"rj-track-summary__copy\">\n <div className=\"rj-track-summary__title\">No tracks</div>\n <div className=\"rj-track-summary__artist\">\n Pass at least one YouTube video to start playback.\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <button\n type=\"button\"\n onClick={onToggleExpanded}\n aria-expanded={isExpanded}\n className=\"rj-track-summary\">\n <div className=\"rj-level-meter\">\n {LEVEL_BAR_HEIGHTS.map((height, index) => {\n const animationDelayMs = index * LEVEL_BAR_ANIMATION_DELAY_MS;\n\n return (\n <div\n key={`${height}-${index}`}\n aria-hidden=\"true\"\n className={clsx(\"rj-level-bar\", {\n \"rj-level-bar--playing\": isPlaying,\n })}\n style={\n {\n minHeight: `${LEVEL_BAR_REST_HEIGHT}px`,\n height: `${isPlaying ? height : LEVEL_BAR_REST_HEIGHT}px`,\n animationDelay: `${animationDelayMs}ms`,\n } satisfies CSSProperties\n }\n />\n );\n })}\n </div>\n <div className=\"rj-track-summary__copy\">\n <div className=\"rj-track-summary__title\">{currentTrack.title}</div>\n <div className=\"rj-track-summary__artist\">\n {currentTrack.artist ?? \"Unknown artist\"}\n </div>\n </div>\n <span className=\"rj-chevron\">\n <ChevronIcon isExpanded={isExpanded} />\n </span>\n </button>\n );\n}\n\nfunction ExpandedPanel({\n children,\n isExpanded,\n}: {\n children: ReactNode;\n isExpanded: boolean;\n}) {\n return (\n <div\n aria-hidden={!isExpanded}\n className={clsx(\"rj-expanded\", {\n \"rj-expanded--hidden\": !isExpanded,\n })}>\n {children}\n </div>\n );\n}\n\nexport function Jukebox({\n tracks,\n autoplay = true,\n position = DEFAULT_POSITION,\n theme = DEFAULT_THEME,\n chrome = DEFAULT_CHROME,\n offset,\n portal = true,\n className,\n renderExpandedContent,\n}: JukeboxProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const isMounted = useSyncExternalStore(\n subscribeToClientRender,\n getClientRenderSnapshot,\n getServerRenderSnapshot,\n );\n const {\n playerMountRef,\n currentIndex,\n isMuted,\n isPlaying,\n volume,\n setVolume,\n toggleMute,\n togglePlay,\n playNext,\n playPrev,\n } = useJukeboxPlayer({ autoplay, tracks });\n const effectiveChrome = getEffectiveChrome(chrome);\n\n const currentTrack = tracks[currentIndex];\n const nextTrack =\n tracks.length > 1\n ? tracks[getNextTrackIndex(currentIndex, 1, tracks.length)]\n : undefined;\n const effectiveIsExpanded = currentTrack ? isExpanded : false;\n const expandedRenderProps: JukeboxExpandedRenderProps | undefined =\n currentTrack\n ? {\n currentIndex,\n currentTrack,\n isExpanded: effectiveIsExpanded,\n isMuted,\n isPlaying,\n nextTrack,\n playerMountRef,\n totalTracks: tracks.length,\n volume,\n setVolume,\n toggleMute,\n togglePlay,\n playNext,\n playPrev,\n }\n : undefined;\n\n const handleToggleExpanded = () => {\n if (!currentTrack) {\n return;\n }\n\n setIsExpanded((expanded) => !expanded);\n };\n\n const content = (\n <div\n className={clsx(\n \"rj-root\",\n {\n \"rj-root--expanded\": effectiveIsExpanded,\n \"rj-root--portal\": portal,\n \"rj-root--inline\": !portal,\n },\n className,\n )}\n data-position={position}\n data-theme={theme}\n data-chrome={effectiveChrome}\n style={getPositionStyle(position, offset, portal)}>\n {expandedRenderProps ? (\n <ExpandedPanel isExpanded={effectiveIsExpanded}>\n {renderExpandedContent ? (\n renderExpandedContent(expandedRenderProps)\n ) : (\n <JukeboxExpandedPlayer {...expandedRenderProps} />\n )}\n </ExpandedPanel>\n ) : null}\n\n <div className=\"rj-dock\">\n <div className=\"rj-dock__inner\">\n <TrackSummary\n currentTrack={currentTrack}\n isExpanded={effectiveIsExpanded}\n isPlaying={isPlaying}\n onToggleExpanded={handleToggleExpanded}\n />\n\n <button\n type=\"button\"\n onClick={toggleMute}\n disabled={!currentTrack}\n aria-label={isMuted ? \"Unmute\" : \"Mute\"}\n className=\"rj-icon-button\">\n <span className=\"rj-icon-button__icon\">\n <VolumeIcon isMuted={isMuted} />\n </span>\n </button>\n </div>\n </div>\n </div>\n );\n\n if (!portal) {\n return content;\n }\n\n if (!isMounted) {\n return null;\n }\n\n return createPortal(content, document.body);\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "react-youtube-jukebox",
3
+ "version": "0.1.2",
4
+ "description": "react-youtube-jukebox is a floating YouTube jukebox and playlist player component for React.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "react",
8
+ "youtube",
9
+ "jukebox",
10
+ "player",
11
+ "audio",
12
+ "playlist",
13
+ "youtube-player",
14
+ "youtube-iframe",
15
+ "floating-player",
16
+ "music-player",
17
+ "audio-player",
18
+ "react-youtube-jukebox",
19
+ "youtube-jukebox"
20
+ ],
21
+ "homepage": "https://react-youtube-jukebox.com",
22
+ "bugs": {
23
+ "url": "https://github.com/madisonrubylee/react-youtube-jukebox/issues"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/madisonrubylee/react-youtube-jukebox.git",
28
+ "directory": "packages/react-youtube-jukebox"
29
+ },
30
+ "type": "module",
31
+ "sideEffects": [
32
+ "*.css",
33
+ "**/*.css"
34
+ ],
35
+ "files": [
36
+ "dist",
37
+ "README.md"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "typecheck": "tsc --project tsconfig.json --noEmit",
42
+ "lint": "eslint src",
43
+ "test": "vitest run",
44
+ "pack:check": "npm pack --dry-run --cache /tmp/react-youtube-jukebox-npm-cache"
45
+ },
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/index.d.ts",
49
+ "import": "./dist/index.js"
50
+ }
51
+ },
52
+ "main": "./dist/index.js",
53
+ "module": "./dist/index.js",
54
+ "types": "./dist/index.d.ts",
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
58
+ "peerDependencies": {
59
+ "react": "^18.3.0 || ^19.0.0",
60
+ "react-dom": "^18.3.0 || ^19.0.0"
61
+ },
62
+ "dependencies": {
63
+ "clsx": "^2.1.1"
64
+ }
65
+ }