synxed-sdk 0.1.1 β†’ 0.1.3

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 CHANGED
@@ -7,12 +7,12 @@ The official Synxed SDK for frontend developers to integrate high-quality music
7
7
 
8
8
  ## Features
9
9
 
10
- - ** Simple Integration**: Play songs or playlists with just a few lines of code.
11
- - **🎧 High-Fidelity Streaming**: Robust HLS support via `hls.js` for seamless playback across all browsers.
12
- - **πŸ“Š Built-in Analytics**: Automatic session tracking and stream event reporting.
13
- - **πŸ›  Framework Agnostic**: Works with React, Vue, Angular, or Vanilla JS.
14
- - **πŸ”’ Secure**: Designed for private, signed-URL streaming with backend proxy support.
15
- - **πŸ’ͺ Type Safe**: Fully written in TypeScript with comprehensive definitions.
10
+ - **Simple integration**: Play songs, playlists, or **24/7 live radio** with a few lines of code.
11
+ - **High-fidelity streaming**: Robust HLS support via `hls.js` for seamless playback across browsers (on-demand content).
12
+ - **Built-in analytics**: Session `stream_start` plus automatic **`heartbeat`** intervals from the server `initAck` (including radio, which uses `positionMs: 0`).
13
+ - **Framework-agnostic core**: `SynxedPlayer` works in React, Vue, Angular, or vanilla JS.
14
+ - **Optional web UI**: Vanilla DOM overlay (`SynxedWebPlayer`) with **mini**, **wide**, and **large** layouts β€” works in any framework; **theme** and **placement** are fully configurable.
15
+ - **Type-safe**: Written in TypeScript with full definitions.
16
16
 
17
17
  ## Installation
18
18
 
@@ -28,100 +28,169 @@ npm install hls.js
28
28
 
29
29
  ## Quick Start
30
30
 
31
- ### Initialize the Player
31
+ ### Initialize the player
32
32
 
33
33
  ```typescript
34
34
  import { SynxedPlayer } from "synxed-sdk";
35
35
 
36
36
  const player = new SynxedPlayer({
37
37
  apiKey: "YOUR_SYNXED_API_KEY",
38
- serverUrl: "https://api.synxed.com", // or your local backend URL
38
+ serverUrl: "https://api.synxed.com", // or your backend URL
39
39
  autoConnect: true,
40
40
  });
41
41
  ```
42
42
 
43
- ### Play a Playlist
44
-
45
- Provide a playlist code generated from the Synxed platform.
43
+ ### Play a playlist
46
44
 
47
45
  ```typescript
48
- // Start playback from a playlist
49
46
  player.playPlaylist({
50
47
  playlistCode: "sxpl_VZCCGQAQJV",
51
48
  });
52
49
  ```
53
50
 
54
- ### Play a Single Song
51
+ ### Play a single song
55
52
 
56
53
  ```typescript
57
- // Play a song from the global catalog
58
54
  player.playSong({
59
55
  catalogTrackId: "2dcad8e0-3695-4971-9e35-f762f3f9d3a5",
60
56
  });
61
57
 
62
- // Or an internal track from your own library
63
58
  player.playSong({
64
59
  internalTrackId: "track-uuid-here",
65
60
  });
66
61
  ```
67
62
 
68
- ### Control Playback
63
+ ### Live radio
64
+
65
+ Continuous **non-HLS** stream (not seekable). The SDK sends `CONTENT_KIND_RADIO` (`4`) over the same `/sdk` socket; the server returns a direct `playbackUrl` (e.g. MPEG stream).
66
+
67
+ ```typescript
68
+ await player.playRadio({ listenerId: "optional-stable-id" });
69
+ ```
70
+
71
+ - **`skip` / `previous` / `skipTo` / `seek`**: no-ops for radio (the live edge keeps moving on the server).
72
+ - **`pause` / `resume`**: local audio only; when the user resumes, they rejoin the live stream.
73
+ - **Metadata**: poll the REST endpoint (unauthenticated on most deployments):
74
+
75
+ ```typescript
76
+ import { fetchRadioNowPlaying } from "synxed-sdk";
77
+
78
+ const np = await fetchRadioNowPlaying("https://api.synxed.com");
79
+ // { title, station?, isLive? } β€” poll every 10–15s for β€œnow playing” UI
80
+ ```
81
+
82
+ ### Optional web player UI (vanilla β€” no React required)
83
+
84
+ ```typescript
85
+ import { SynxedWebPlayer } from "synxed-sdk";
86
+
87
+ const ui = SynxedWebPlayer.mount({
88
+ apiKey: "YOUR_SYNXED_API_KEY",
89
+ serverUrl: "https://api.synxed.com",
90
+ source: { type: "radio" },
91
+ mode: "wide",
92
+ avatarUrl: "/dj.webp",
93
+ attribution: "DJ Jesse Β· Synxed Γ— Your Brand",
94
+ theme: {
95
+ accent: "#ef4444",
96
+ glow: "rgba(239, 68, 68, 0.35)",
97
+ background: "#0a0707",
98
+ },
99
+ position: { placement: "top-left", offsetX: 16, offsetY: 16 },
100
+ });
101
+
102
+ // Later: ui.destroy();
103
+ ```
104
+
105
+ Mount into your own container instead of a fixed overlay:
106
+
107
+ ```typescript
108
+ SynxedWebPlayer.mount({
109
+ container: document.getElementById("player-slot")!,
110
+ apiKey: "...",
111
+ serverUrl: "...",
112
+ source: { type: "playlist", playlistCode: "sxpl_..." },
113
+ });
114
+ ```
115
+
116
+ **Modes**: `mini` (avatar only), `wide` (pill bar, default), `large` (card + visualizer). Skip controls are hidden for radio.
117
+
118
+ In React/Vue, create the player in `useEffect` / `onMounted` and call `destroy()` on teardown β€” same class, no separate React package.
119
+
120
+ ### Playback controls
69
121
 
70
122
  ```typescript
71
123
  player.pause();
72
124
  player.resume();
73
125
  player.stop();
74
- player.seek(30000); // Seek to 30 seconds
126
+ player.seek(30000); // on-demand only; no-op for radio
75
127
  player.setVolume(0.8);
76
- player.skip(); // Skip to next track in playlist
128
+ player.skip();
129
+ player.previous();
130
+ player.skipTo(2);
77
131
  ```
78
132
 
79
- ## Listening for Events
133
+ ## Listening for events
80
134
 
81
135
  ```typescript
82
- // Track state changes (idle, loading, playing, paused, error)
83
136
  player.on("stateChange", (state) => {
84
137
  console.log("Player status:", state.status);
85
138
  });
86
139
 
87
- // High-resolution time updates (60fps)
88
140
  player.on("timeUpdate", ({ currentTime, duration }) => {
89
141
  const progress = (currentTime / duration) * 100;
90
142
  console.log(`Progress: ${progress.toFixed(2)}%`);
91
143
  });
92
144
 
93
- // Error handling
94
145
  player.on("error", (err) => {
95
146
  console.error("Playback Error:", err.message);
96
147
  });
97
148
  ```
98
149
 
99
- ## API Reference
150
+ ## API reference
100
151
 
101
152
  ### `new SynxedPlayer(config)`
102
153
 
103
- - `apiKey`: `string` (Required) Your developer API key.
104
- - `serverUrl`: `string` (Required) Your Synxed backend URL.
105
- - `autoConnect`: `boolean` (Default: `true`) Connect to the streaming socket immediately.
154
+ - `apiKey`: `string` (required)
155
+ - `serverUrl`: `string` (required)
156
+ - `autoConnect`: `boolean` (default `true`)
106
157
 
107
158
  ### Methods
108
159
 
109
- - `playSong(options)`: `Promise<void>` Plays a single track by `catalogTrackId` or `internalTrackId`.
110
- - `playPlaylist(options)`: `Promise<void>` Starts playback from a `playlistCode`.
111
- - `pause()`: `void` Pauses the current stream.
112
- - `resume()`: `void` Resumes a paused stream.
113
- - `stop()`: `void` Stops playback and releases audio resources.
114
- - `skip()`: `void` Skips to the next track in the playlist.
115
- - `seek(ms)`: `void` Seeks to a specific position in milliseconds.
116
- - `setVolume(0-1)`: `void` Sets the volume.
117
- - `destroy()`: `void` Cleans up the player, listeners, and disconnects.
160
+ - `playSong(options)`: `Promise<void>`
161
+ - `playPlaylist(options)`: `Promise<void>`
162
+ - `playRadio(options?)`: `Promise<void>` β€” live radio (`listenerId` optional)
163
+ - `pause()` / `resume()` / `stop()`
164
+ - `skip()` / `previous()` / `skipTo(index)` β€” playlist / on-demand only
165
+ - `seek(ms)` β€” on-demand only (no-op for radio)
166
+ - `setVolume(0–1)`
167
+ - `destroy()`
168
+
169
+ ### Getters
170
+
171
+ - `currentTrack`: current `TrackInfo | null`
172
+ - `contentKind`: last `play*` kind (`ContentKind` enum, including `RADIO`)
173
+
174
+ ### `fetchRadioNowPlaying(serverUrl, init?)`
175
+
176
+ `Promise<RadioNowPlaying | null>` β€” wraps `GET {serverUrl}/radio/now-playing`.
118
177
 
119
178
  ### Events
120
179
 
121
- - `stateChange`: `(state: PlayerState)` Fired when the engine state changes.
122
- - `timeUpdate`: `({ currentTime, duration })` Fired during playback.
123
- - `trackChange`: `(track: any)` Fired when a new track starts playing in a playlist.
124
- - `error`: `(error: Error)` Fired on playback or connection failures.
180
+ - `stateChange`, `timeUpdate`, `trackChange`, `queueUpdated`, `error`, `connected`, `disconnected`
181
+
182
+ ### `TrackInfo`
183
+
184
+ ```typescript
185
+ interface TrackInfo {
186
+ id: string;
187
+ kind: "catalog" | "internal";
188
+ title?: string;
189
+ artist?: string;
190
+ duration?: number;
191
+ albumArt?: string;
192
+ }
193
+ ```
125
194
 
126
195
  ## License
127
196
 
package/dist/index.d.mts CHANGED
@@ -7,6 +7,23 @@ declare class EventEmitter<Events extends Record<string, any>> {
7
7
  removeAllListeners(): void;
8
8
  }
9
9
 
10
+ declare enum ContentKind {
11
+ UNSPECIFIED = 0,
12
+ SONG = 1,
13
+ PLAYLIST = 2,
14
+ CATEGORY = 3,
15
+ RADIO = 4
16
+ }
17
+ declare enum ErrorCode {
18
+ UNSPECIFIED = 0,
19
+ UNAUTHORIZED = 1,
20
+ VALIDATION_ERROR = 2,
21
+ NOT_FOUND = 3,
22
+ PROCESSING = 4,
23
+ SERVICE_UNAVAILABLE = 5,
24
+ BAD_PROTOBUF = 6
25
+ }
26
+
10
27
  interface SynxedConfig {
11
28
  apiKey: string;
12
29
  serverUrl: string;
@@ -21,8 +38,18 @@ interface PlayPlaylistOptions {
21
38
  playlistCode: string;
22
39
  listenerId?: string;
23
40
  }
41
+ interface PlayRadioOptions {
42
+ listenerId?: string;
43
+ }
44
+ /** Response shape from `GET /radio/now-playing` */
45
+ interface RadioNowPlaying {
46
+ title: string;
47
+ station?: string;
48
+ isLive?: boolean;
49
+ }
24
50
  interface TrackInfo {
25
51
  id: string;
52
+ kind: "catalog" | "internal";
26
53
  title?: string;
27
54
  artist?: string;
28
55
  duration?: number;
@@ -30,7 +57,7 @@ interface TrackInfo {
30
57
  }
31
58
  interface PlayerState {
32
59
  status: 'idle' | 'loading' | 'playing' | 'paused' | 'error';
33
- currentTrack?: TrackInfo;
60
+ currentTrack: TrackInfo | null;
34
61
  currentTime: number;
35
62
  duration: number;
36
63
  volume: number;
@@ -42,6 +69,7 @@ interface SynxedEvents {
42
69
  duration: number;
43
70
  }) => void;
44
71
  trackChange: (track: TrackInfo) => void;
72
+ queueUpdated: (tracks: TrackInfo[]) => void;
45
73
  error: (error: Error) => void;
46
74
  connected: () => void;
47
75
  disconnected: (reason: string) => void;
@@ -53,18 +81,31 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
53
81
  private playlist;
54
82
  private config;
55
83
  private status;
84
+ private volume;
85
+ private activeContentKind;
86
+ private heartbeatTimer;
56
87
  constructor(config: SynxedConfig);
88
+ get currentTrack(): TrackInfo | null;
89
+ /** Active playback init kind from the last `play*` call (RADIO, PLAYLIST, SONG, …). */
90
+ get contentKind(): ContentKind;
91
+ private controlPositionMs;
57
92
  private setupListeners;
58
93
  private connect;
59
94
  playSong(options: PlaySongOptions): Promise<void>;
60
95
  playPlaylist(options: PlayPlaylistOptions): Promise<void>;
96
+ playRadio(options?: PlayRadioOptions): Promise<void>;
61
97
  pause(): void;
62
98
  resume(): void;
63
99
  stop(): void;
64
100
  skip(): void;
101
+ previous(): void;
102
+ skipTo(index: number): void;
65
103
  seek(ms: number): void;
66
104
  setVolume(volume: number): void;
105
+ private positionMsForAnalytics;
67
106
  private emitAnalytics;
107
+ private clearHeartbeat;
108
+ private startHeartbeat;
68
109
  private handleServerMessage;
69
110
  private handleTrackEnded;
70
111
  private updateStatus;
@@ -85,20 +126,102 @@ declare class SynxedProtocolError extends SynxedError {
85
126
  constructor(message: string);
86
127
  }
87
128
 
88
- declare enum ContentKind {
89
- UNSPECIFIED = 0,
90
- SONG = 1,
91
- PLAYLIST = 2,
92
- CATEGORY = 3
129
+ /**
130
+ * Fetches live radio "now playing" metadata from the Synxed backend.
131
+ * Poll every 10–15 seconds to keep UI in sync (see integration docs).
132
+ */
133
+ declare function fetchRadioNowPlaying(serverUrl: string, init?: RequestInit): Promise<RadioNowPlaying | null>;
134
+
135
+ type SynxedWebPlayerMode = 'mini' | 'wide' | 'large';
136
+ type SynxedWebPlayerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom-center';
137
+ interface SynxedWebPlayerTheme {
138
+ accent?: string;
139
+ accentMuted?: string;
140
+ background?: string;
141
+ backgroundInner?: string;
142
+ border?: string;
143
+ text?: string;
144
+ textMuted?: string;
145
+ stationText?: string;
146
+ liveDot?: string;
147
+ glow?: string;
93
148
  }
94
- declare enum ErrorCode {
95
- UNSPECIFIED = 0,
96
- UNAUTHORIZED = 1,
97
- VALIDATION_ERROR = 2,
98
- NOT_FOUND = 3,
99
- PROCESSING = 4,
100
- SERVICE_UNAVAILABLE = 5,
101
- BAD_PROTOBUF = 6
149
+ interface SynxedWebPlayerPosition {
150
+ placement?: SynxedWebPlayerPlacement;
151
+ offsetX?: number;
152
+ offsetY?: number;
153
+ }
154
+ type SynxedWebPlayerSource = {
155
+ type: 'playlist';
156
+ playlistCode: string;
157
+ } | {
158
+ type: 'radio';
159
+ };
160
+ interface SynxedWebPlayerOptions {
161
+ /** Mount target. If omitted, a fixed overlay root is appended to `document.body`. */
162
+ container?: HTMLElement;
163
+ apiKey: string;
164
+ serverUrl: string;
165
+ source: SynxedWebPlayerSource;
166
+ mode?: SynxedWebPlayerMode;
167
+ theme?: SynxedWebPlayerTheme;
168
+ position?: SynxedWebPlayerPosition;
169
+ avatarUrl?: string;
170
+ attribution?: string;
171
+ nowPlayingPollMs?: number;
172
+ powerByLabel?: string;
173
+ className?: string;
174
+ /** Extra inline styles on the outer shell */
175
+ style?: Partial<CSSStyleDeclaration>;
176
+ onMiniClick?: () => void;
177
+ }
178
+
179
+ /**
180
+ * Framework-agnostic web overlay player (vanilla DOM).
181
+ * Works in React, Vue, Angular, or plain HTML β€” no React dependency.
182
+ */
183
+ declare class SynxedWebPlayer {
184
+ private readonly options;
185
+ private readonly root;
186
+ private readonly ownsRoot;
187
+ private readonly theme;
188
+ private engine;
189
+ private pollTimer;
190
+ private destroyed;
191
+ private isPlaying;
192
+ private currentTrack;
193
+ private nowPlaying;
194
+ private avatarRing;
195
+ private titleEl;
196
+ private artistInlineEl;
197
+ private artistBlockEl;
198
+ private footerEl;
199
+ private playBtn;
200
+ constructor(options: SynxedWebPlayerOptions);
201
+ /** Convenience factory β€” same as `new SynxedWebPlayer(options)`. */
202
+ static mount(options: SynxedWebPlayerOptions): SynxedWebPlayer;
203
+ /** Underlying streaming engine (advanced integrations). */
204
+ get player(): SynxedPlayer | null;
205
+ get element(): HTMLElement;
206
+ destroy(): void;
207
+ private get isRadio();
208
+ private get mode();
209
+ private mountShell;
210
+ private buildAvatar;
211
+ private buildMini;
212
+ private buildWide;
213
+ private buildLarge;
214
+ private roundControlStyle;
215
+ private createPlayButton;
216
+ private buildVisualizer;
217
+ private decoLines;
218
+ private initEngine;
219
+ private startNowPlayingPoll;
220
+ private stopNowPlayingPoll;
221
+ private displayLine;
222
+ private refreshLabels;
223
+ private setPlayingVisual;
224
+ private togglePlay;
102
225
  }
103
226
 
104
- export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlaySongOptions, type PlayerState, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, type TrackInfo };
227
+ export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlayRadioOptions, type PlaySongOptions, type PlayerState, type RadioNowPlaying, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, SynxedWebPlayer, type SynxedWebPlayerMode, type SynxedWebPlayerOptions, type SynxedWebPlayerPlacement, type SynxedWebPlayerPosition, type SynxedWebPlayerSource, type SynxedWebPlayerTheme, type TrackInfo, fetchRadioNowPlaying };
package/dist/index.d.ts CHANGED
@@ -7,6 +7,23 @@ declare class EventEmitter<Events extends Record<string, any>> {
7
7
  removeAllListeners(): void;
8
8
  }
9
9
 
10
+ declare enum ContentKind {
11
+ UNSPECIFIED = 0,
12
+ SONG = 1,
13
+ PLAYLIST = 2,
14
+ CATEGORY = 3,
15
+ RADIO = 4
16
+ }
17
+ declare enum ErrorCode {
18
+ UNSPECIFIED = 0,
19
+ UNAUTHORIZED = 1,
20
+ VALIDATION_ERROR = 2,
21
+ NOT_FOUND = 3,
22
+ PROCESSING = 4,
23
+ SERVICE_UNAVAILABLE = 5,
24
+ BAD_PROTOBUF = 6
25
+ }
26
+
10
27
  interface SynxedConfig {
11
28
  apiKey: string;
12
29
  serverUrl: string;
@@ -21,8 +38,18 @@ interface PlayPlaylistOptions {
21
38
  playlistCode: string;
22
39
  listenerId?: string;
23
40
  }
41
+ interface PlayRadioOptions {
42
+ listenerId?: string;
43
+ }
44
+ /** Response shape from `GET /radio/now-playing` */
45
+ interface RadioNowPlaying {
46
+ title: string;
47
+ station?: string;
48
+ isLive?: boolean;
49
+ }
24
50
  interface TrackInfo {
25
51
  id: string;
52
+ kind: "catalog" | "internal";
26
53
  title?: string;
27
54
  artist?: string;
28
55
  duration?: number;
@@ -30,7 +57,7 @@ interface TrackInfo {
30
57
  }
31
58
  interface PlayerState {
32
59
  status: 'idle' | 'loading' | 'playing' | 'paused' | 'error';
33
- currentTrack?: TrackInfo;
60
+ currentTrack: TrackInfo | null;
34
61
  currentTime: number;
35
62
  duration: number;
36
63
  volume: number;
@@ -42,6 +69,7 @@ interface SynxedEvents {
42
69
  duration: number;
43
70
  }) => void;
44
71
  trackChange: (track: TrackInfo) => void;
72
+ queueUpdated: (tracks: TrackInfo[]) => void;
45
73
  error: (error: Error) => void;
46
74
  connected: () => void;
47
75
  disconnected: (reason: string) => void;
@@ -53,18 +81,31 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
53
81
  private playlist;
54
82
  private config;
55
83
  private status;
84
+ private volume;
85
+ private activeContentKind;
86
+ private heartbeatTimer;
56
87
  constructor(config: SynxedConfig);
88
+ get currentTrack(): TrackInfo | null;
89
+ /** Active playback init kind from the last `play*` call (RADIO, PLAYLIST, SONG, …). */
90
+ get contentKind(): ContentKind;
91
+ private controlPositionMs;
57
92
  private setupListeners;
58
93
  private connect;
59
94
  playSong(options: PlaySongOptions): Promise<void>;
60
95
  playPlaylist(options: PlayPlaylistOptions): Promise<void>;
96
+ playRadio(options?: PlayRadioOptions): Promise<void>;
61
97
  pause(): void;
62
98
  resume(): void;
63
99
  stop(): void;
64
100
  skip(): void;
101
+ previous(): void;
102
+ skipTo(index: number): void;
65
103
  seek(ms: number): void;
66
104
  setVolume(volume: number): void;
105
+ private positionMsForAnalytics;
67
106
  private emitAnalytics;
107
+ private clearHeartbeat;
108
+ private startHeartbeat;
68
109
  private handleServerMessage;
69
110
  private handleTrackEnded;
70
111
  private updateStatus;
@@ -85,20 +126,102 @@ declare class SynxedProtocolError extends SynxedError {
85
126
  constructor(message: string);
86
127
  }
87
128
 
88
- declare enum ContentKind {
89
- UNSPECIFIED = 0,
90
- SONG = 1,
91
- PLAYLIST = 2,
92
- CATEGORY = 3
129
+ /**
130
+ * Fetches live radio "now playing" metadata from the Synxed backend.
131
+ * Poll every 10–15 seconds to keep UI in sync (see integration docs).
132
+ */
133
+ declare function fetchRadioNowPlaying(serverUrl: string, init?: RequestInit): Promise<RadioNowPlaying | null>;
134
+
135
+ type SynxedWebPlayerMode = 'mini' | 'wide' | 'large';
136
+ type SynxedWebPlayerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom-center';
137
+ interface SynxedWebPlayerTheme {
138
+ accent?: string;
139
+ accentMuted?: string;
140
+ background?: string;
141
+ backgroundInner?: string;
142
+ border?: string;
143
+ text?: string;
144
+ textMuted?: string;
145
+ stationText?: string;
146
+ liveDot?: string;
147
+ glow?: string;
93
148
  }
94
- declare enum ErrorCode {
95
- UNSPECIFIED = 0,
96
- UNAUTHORIZED = 1,
97
- VALIDATION_ERROR = 2,
98
- NOT_FOUND = 3,
99
- PROCESSING = 4,
100
- SERVICE_UNAVAILABLE = 5,
101
- BAD_PROTOBUF = 6
149
+ interface SynxedWebPlayerPosition {
150
+ placement?: SynxedWebPlayerPlacement;
151
+ offsetX?: number;
152
+ offsetY?: number;
153
+ }
154
+ type SynxedWebPlayerSource = {
155
+ type: 'playlist';
156
+ playlistCode: string;
157
+ } | {
158
+ type: 'radio';
159
+ };
160
+ interface SynxedWebPlayerOptions {
161
+ /** Mount target. If omitted, a fixed overlay root is appended to `document.body`. */
162
+ container?: HTMLElement;
163
+ apiKey: string;
164
+ serverUrl: string;
165
+ source: SynxedWebPlayerSource;
166
+ mode?: SynxedWebPlayerMode;
167
+ theme?: SynxedWebPlayerTheme;
168
+ position?: SynxedWebPlayerPosition;
169
+ avatarUrl?: string;
170
+ attribution?: string;
171
+ nowPlayingPollMs?: number;
172
+ powerByLabel?: string;
173
+ className?: string;
174
+ /** Extra inline styles on the outer shell */
175
+ style?: Partial<CSSStyleDeclaration>;
176
+ onMiniClick?: () => void;
177
+ }
178
+
179
+ /**
180
+ * Framework-agnostic web overlay player (vanilla DOM).
181
+ * Works in React, Vue, Angular, or plain HTML β€” no React dependency.
182
+ */
183
+ declare class SynxedWebPlayer {
184
+ private readonly options;
185
+ private readonly root;
186
+ private readonly ownsRoot;
187
+ private readonly theme;
188
+ private engine;
189
+ private pollTimer;
190
+ private destroyed;
191
+ private isPlaying;
192
+ private currentTrack;
193
+ private nowPlaying;
194
+ private avatarRing;
195
+ private titleEl;
196
+ private artistInlineEl;
197
+ private artistBlockEl;
198
+ private footerEl;
199
+ private playBtn;
200
+ constructor(options: SynxedWebPlayerOptions);
201
+ /** Convenience factory β€” same as `new SynxedWebPlayer(options)`. */
202
+ static mount(options: SynxedWebPlayerOptions): SynxedWebPlayer;
203
+ /** Underlying streaming engine (advanced integrations). */
204
+ get player(): SynxedPlayer | null;
205
+ get element(): HTMLElement;
206
+ destroy(): void;
207
+ private get isRadio();
208
+ private get mode();
209
+ private mountShell;
210
+ private buildAvatar;
211
+ private buildMini;
212
+ private buildWide;
213
+ private buildLarge;
214
+ private roundControlStyle;
215
+ private createPlayButton;
216
+ private buildVisualizer;
217
+ private decoLines;
218
+ private initEngine;
219
+ private startNowPlayingPoll;
220
+ private stopNowPlayingPoll;
221
+ private displayLine;
222
+ private refreshLabels;
223
+ private setPlayingVisual;
224
+ private togglePlay;
102
225
  }
103
226
 
104
- export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlaySongOptions, type PlayerState, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, type TrackInfo };
227
+ export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlayRadioOptions, type PlaySongOptions, type PlayerState, type RadioNowPlaying, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, SynxedWebPlayer, type SynxedWebPlayerMode, type SynxedWebPlayerOptions, type SynxedWebPlayerPlacement, type SynxedWebPlayerPosition, type SynxedWebPlayerSource, type SynxedWebPlayerTheme, type TrackInfo, fetchRadioNowPlaying };