synxed-sdk 0.2.4 → 0.2.6
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 +109 -126
- package/dist/index.d.mts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,198 +1,181 @@
|
|
|
1
1
|
# Synxed SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add Synxed music to your website or app in a few minutes — playlists, live radio, voice commands, and a ready-made player UI.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/synxed-sdk)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
6
|
|
|
8
|
-
##
|
|
7
|
+
## What you need
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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.
|
|
9
|
+
1. A **Synxed API key** (from your Synxed developer account `https://portal.synxed.com`).
|
|
10
|
+
2. Your **API URL** (Our api url: `https://api.synxed.com`).
|
|
11
|
+
3. A **playlist code**, **Single Song** or choose **live radio**.
|
|
16
12
|
|
|
17
|
-
##
|
|
13
|
+
## Install
|
|
18
14
|
|
|
19
15
|
```bash
|
|
20
16
|
npm install synxed-sdk
|
|
21
17
|
```
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
## Easiest setup — built-in player
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
npm install hls.js
|
|
27
|
-
```
|
|
21
|
+
This adds a floating music bar to your page. No React or special framework required.
|
|
28
22
|
|
|
29
|
-
|
|
23
|
+
### Play a playlist
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
```html
|
|
26
|
+
<script type="module">
|
|
27
|
+
import { SynxedWebPlayer } from "synxed-sdk";
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
SynxedWebPlayer.mount({
|
|
30
|
+
apiKey: "YOUR_SYNXED_API_KEY",
|
|
31
|
+
serverUrl: "https://api.synxed.com",
|
|
32
|
+
source: { type: "playlist", playlistCode: "sxpl_YOUR_CODE" },
|
|
33
|
+
});
|
|
34
|
+
</script>
|
|
35
|
+
```
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
### Play live radio
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
SynxedWebPlayer.mount({
|
|
37
41
|
apiKey: "YOUR_SYNXED_API_KEY",
|
|
38
42
|
serverUrl: "https://api.synxed.com",
|
|
39
|
-
|
|
43
|
+
source: { type: "radio" },
|
|
40
44
|
});
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
###
|
|
47
|
+
### Put the player inside your own box
|
|
44
48
|
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
```javascript
|
|
50
|
+
SynxedWebPlayer.mount({
|
|
51
|
+
container: document.getElementById("my-player"),
|
|
52
|
+
apiKey: "YOUR_SYNXED_API_KEY",
|
|
53
|
+
serverUrl: "https://api.synxed.com",
|
|
54
|
+
source: { type: "playlist", playlistCode: "sxpl_YOUR_CODE" },
|
|
48
55
|
});
|
|
49
56
|
```
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
## Player sizes
|
|
52
59
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
| Mode | What it looks like |
|
|
61
|
+
| ------- | ------------------------------- |
|
|
62
|
+
| `wide` | Bar across the bottom (default) |
|
|
63
|
+
| `mini` | Small round DJ button |
|
|
64
|
+
| `large` | Bigger card with visualizer |
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
```javascript
|
|
67
|
+
SynxedWebPlayer.mount({
|
|
68
|
+
apiKey: "...",
|
|
69
|
+
serverUrl: "...",
|
|
70
|
+
source: { type: "playlist", playlistCode: "sxpl_..." },
|
|
71
|
+
mode: "mini",
|
|
60
72
|
});
|
|
61
73
|
```
|
|
62
74
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Continuous **non-HLS** stream (not seekable). The SDK sends `CONTENT_KIND_RADIO` (`4`) over the same native `/sdk` WebSocket; 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):
|
|
75
|
+
## Move the player on screen
|
|
74
76
|
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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",
|
|
77
|
+
```javascript
|
|
78
|
+
SynxedWebPlayer.mount({
|
|
79
|
+
apiKey: "...",
|
|
80
|
+
serverUrl: "...",
|
|
90
81
|
source: { type: "radio" },
|
|
91
|
-
mode: "wide",
|
|
92
|
-
attribution: "DJ Jesse · Synxed × Your Brand",
|
|
93
|
-
theme: {
|
|
94
|
-
accent: "#ef4444",
|
|
95
|
-
glow: "rgba(239, 68, 68, 0.35)",
|
|
96
|
-
background: "#0a0707",
|
|
97
|
-
},
|
|
98
82
|
position: { placement: "top-left", offsetX: 16, offsetY: 16 },
|
|
99
83
|
draggable: true,
|
|
100
84
|
});
|
|
101
|
-
|
|
102
|
-
// Later: ui.destroy();
|
|
103
85
|
```
|
|
104
86
|
|
|
105
|
-
|
|
87
|
+
## Match your brand colors
|
|
106
88
|
|
|
107
|
-
```
|
|
89
|
+
```javascript
|
|
108
90
|
SynxedWebPlayer.mount({
|
|
109
|
-
container: document.getElementById("player-slot")!,
|
|
110
91
|
apiKey: "...",
|
|
111
92
|
serverUrl: "...",
|
|
112
93
|
source: { type: "playlist", playlistCode: "sxpl_..." },
|
|
94
|
+
theme: {
|
|
95
|
+
accent: "#22c55e",
|
|
96
|
+
background: "#0a0a0a",
|
|
97
|
+
glow: "rgba(34, 197, 94, 0.35)",
|
|
98
|
+
},
|
|
113
99
|
});
|
|
114
100
|
```
|
|
115
101
|
|
|
116
|
-
|
|
117
|
-
|
|
102
|
+
## Talk to the DJ (voice playlists)
|
|
103
|
+
|
|
104
|
+
**Hold** the Synxed DJ avatar (the spinning circle) and say what you want to hear — for example _“play upbeat workout songs”_.
|
|
118
105
|
|
|
119
|
-
|
|
106
|
+
- **Tap** the DJ → play or pause the music.
|
|
107
|
+
- **Hold** the DJ → speak your request; release when done (or stop talking and it sends automatically).
|
|
108
|
+
- While you speak, the DJ icon switches to the AI listening view.
|
|
109
|
+
- Music pauses while you talk and can resume when you tap again.
|
|
120
110
|
|
|
121
|
-
|
|
111
|
+
To turn voice off:
|
|
122
112
|
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
player.setVolume(0.8);
|
|
129
|
-
player.skip();
|
|
130
|
-
player.previous();
|
|
131
|
-
player.skipTo(2);
|
|
113
|
+
```javascript
|
|
114
|
+
SynxedWebPlayer.mount({
|
|
115
|
+
enableVoice: false,
|
|
116
|
+
// ...other options
|
|
117
|
+
});
|
|
132
118
|
```
|
|
133
119
|
|
|
134
|
-
##
|
|
120
|
+
## Skip button and ads
|
|
135
121
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
});
|
|
122
|
+
- During normal playback, the **skip** button jumps to the next song.
|
|
123
|
+
- During an ad, the same button shows a **short countdown** (about 5 seconds), then you can skip the ad.
|
|
124
|
+
- After the ad, the skip button works again for the next song.
|
|
140
125
|
|
|
141
|
-
|
|
142
|
-
const progress = (currentTime / duration) * 100;
|
|
143
|
-
console.log(`Progress: ${progress.toFixed(2)}%`);
|
|
144
|
-
});
|
|
126
|
+
## Build your own UI (optional)
|
|
145
127
|
|
|
146
|
-
|
|
147
|
-
console.error("Playback Error:", err.message);
|
|
148
|
-
});
|
|
149
|
-
```
|
|
128
|
+
If you want full control over buttons and layout, use `SynxedPlayer` instead of `SynxedWebPlayer`:
|
|
150
129
|
|
|
151
|
-
|
|
130
|
+
```javascript
|
|
131
|
+
import { SynxedPlayer } from "synxed-sdk";
|
|
152
132
|
|
|
153
|
-
|
|
133
|
+
const player = new SynxedPlayer({
|
|
134
|
+
apiKey: "YOUR_SYNXED_API_KEY",
|
|
135
|
+
serverUrl: "https://api.synxed.com",
|
|
136
|
+
autoConnect: true,
|
|
137
|
+
});
|
|
154
138
|
|
|
155
|
-
|
|
156
|
-
- `serverUrl`: `string` (required)
|
|
157
|
-
- `autoConnect`: `boolean` (default `true`)
|
|
139
|
+
await player.playPlaylist({ playlistCode: "sxpl_YOUR_CODE" });
|
|
158
140
|
|
|
159
|
-
|
|
141
|
+
document.getElementById("play").onclick = () => player.resume();
|
|
142
|
+
document.getElementById("pause").onclick = () => player.pause();
|
|
143
|
+
document.getElementById("skip").onclick = () => player.skip();
|
|
144
|
+
```
|
|
160
145
|
|
|
161
|
-
|
|
162
|
-
- `playPlaylist(options)`: `Promise<void>`
|
|
163
|
-
- `playRadio(options?)`: `Promise<void>` — live radio (`listenerId` optional)
|
|
164
|
-
- `pause()` / `resume()` / `stop()`
|
|
165
|
-
- `skip()` / `previous()` / `skipTo(index)` — playlist / on-demand only
|
|
166
|
-
- `seek(ms)` — on-demand only (no-op for radio)
|
|
167
|
-
- `setVolume(0–1)`
|
|
168
|
-
- `destroy()`
|
|
146
|
+
### Voice with your own buttons
|
|
169
147
|
|
|
170
|
-
|
|
148
|
+
```javascript
|
|
149
|
+
const dj = document.getElementById("dj-avatar");
|
|
171
150
|
|
|
172
|
-
|
|
173
|
-
|
|
151
|
+
dj.addEventListener("pointerdown", () => player.beginVoiceHold());
|
|
152
|
+
dj.addEventListener("pointerup", () => player.endVoiceHold());
|
|
153
|
+
```
|
|
174
154
|
|
|
175
|
-
|
|
155
|
+
## Clean up when leaving the page
|
|
176
156
|
|
|
177
|
-
|
|
157
|
+
```javascript
|
|
158
|
+
const ui = SynxedWebPlayer.mount({
|
|
159
|
+
/* ... */
|
|
160
|
+
});
|
|
178
161
|
|
|
179
|
-
|
|
162
|
+
// When your app unmounts or navigates away:
|
|
163
|
+
ui.destroy();
|
|
164
|
+
```
|
|
180
165
|
|
|
181
|
-
|
|
166
|
+
## Radio “now playing” title
|
|
182
167
|
|
|
183
|
-
|
|
168
|
+
For radio, you can show the current song name on your own label:
|
|
184
169
|
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
artist?: string;
|
|
191
|
-
duration?: number;
|
|
192
|
-
albumArt?: string;
|
|
193
|
-
}
|
|
170
|
+
```javascript
|
|
171
|
+
import { fetchRadioNowPlaying } from "synxed-sdk";
|
|
172
|
+
|
|
173
|
+
const info = await fetchRadioNowPlaying("https://api.synxed.com");
|
|
174
|
+
if (info) console.log(info.title);
|
|
194
175
|
```
|
|
195
176
|
|
|
177
|
+
Poll every 10–15 seconds to keep the title fresh.
|
|
178
|
+
|
|
196
179
|
## License
|
|
197
180
|
|
|
198
181
|
Copyright © [Synxed.com](https://synxed.com)
|
package/dist/index.d.mts
CHANGED
|
@@ -155,6 +155,7 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
|
|
|
155
155
|
private voiceListenerId;
|
|
156
156
|
private pendingPlaybackLoad;
|
|
157
157
|
private voiceAutoEndOnSilence;
|
|
158
|
+
private pausedForVoice;
|
|
158
159
|
constructor(config: SynxedConfig);
|
|
159
160
|
get currentTrack(): TrackInfo | null;
|
|
160
161
|
/** Active playback init kind from the last `play*` call (RADIO, PLAYLIST, SONG, …). */
|
|
@@ -185,6 +186,12 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
|
|
|
185
186
|
endVoiceHold(): Promise<void>;
|
|
186
187
|
/** Cancel an in-progress voice hold without sending audio to the server. */
|
|
187
188
|
cancelVoiceHold(): void;
|
|
189
|
+
/**
|
|
190
|
+
* Close the voice UI: cancel recording if active and resume playback if it was
|
|
191
|
+
* paused for voice.
|
|
192
|
+
*/
|
|
193
|
+
dismissVoiceAndResume(): void;
|
|
194
|
+
get isVoiceUiActive(): boolean;
|
|
188
195
|
pause(): void;
|
|
189
196
|
resume(): void;
|
|
190
197
|
stop(): void;
|
|
@@ -337,8 +344,6 @@ interface SynxedWebPlayerOptions {
|
|
|
337
344
|
mode?: SynxedWebPlayerMode;
|
|
338
345
|
theme?: SynxedWebPlayerTheme;
|
|
339
346
|
position?: SynxedWebPlayerPosition;
|
|
340
|
-
avatarUrl?: string;
|
|
341
|
-
voiceAvatarUrl?: string;
|
|
342
347
|
attribution?: string;
|
|
343
348
|
nowPlayingPollMs?: number;
|
|
344
349
|
powerByLabel?: string;
|
|
@@ -347,8 +352,10 @@ interface SynxedWebPlayerOptions {
|
|
|
347
352
|
style?: Partial<CSSStyleDeclaration>;
|
|
348
353
|
onMiniClick?: () => void;
|
|
349
354
|
draggable?: boolean;
|
|
350
|
-
/** Press-and-hold DJ avatar for voice
|
|
355
|
+
/** Press-and-hold DJ avatar for voice. Set `false` to disable. Default `true`. */
|
|
351
356
|
enableVoice?: boolean;
|
|
357
|
+
/** Ms to hold before voice starts (prevents opening AI on quick tap). Default `450`. */
|
|
358
|
+
voiceHoldMs?: number;
|
|
352
359
|
listenerId?: string;
|
|
353
360
|
}
|
|
354
361
|
|
|
@@ -380,6 +387,12 @@ declare class SynxedWebPlayer {
|
|
|
380
387
|
private avatarVoiceHolding;
|
|
381
388
|
private suppressMiniClick;
|
|
382
389
|
private voiceAvatarPreview;
|
|
390
|
+
private voiceHoldActivated;
|
|
391
|
+
private voiceUiDismissed;
|
|
392
|
+
private voiceHoldTimer;
|
|
393
|
+
private readonly defaultVoiceHoldMs;
|
|
394
|
+
/** Avatar to restore after an ad (DJ / album art — not ad or voice AI). */
|
|
395
|
+
private avatarImageBeforeAd;
|
|
383
396
|
constructor(options: SynxedWebPlayerOptions);
|
|
384
397
|
/** Convenience factory — same as `new SynxedWebPlayer(options)`. */
|
|
385
398
|
static mount(options: SynxedWebPlayerOptions): SynxedWebPlayer;
|
|
@@ -404,14 +417,21 @@ declare class SynxedWebPlayer {
|
|
|
404
417
|
private initEngine;
|
|
405
418
|
/** Press-and-hold DJ avatar to stream voice; release or silence auto-ends capture. */
|
|
406
419
|
private wireAvatarVoiceHold;
|
|
420
|
+
private isVoiceUiOpen;
|
|
421
|
+
private dismissVoiceUi;
|
|
422
|
+
private resetVoiceHoldState;
|
|
407
423
|
private applyVoiceVisual;
|
|
408
424
|
private isVoiceAvatarActive;
|
|
425
|
+
private getContentAvatarUrl;
|
|
409
426
|
private getAvatarImageUrl;
|
|
410
427
|
private refreshAvatarImage;
|
|
411
428
|
private startNowPlayingPoll;
|
|
412
429
|
private stopNowPlayingPoll;
|
|
413
430
|
private handleSkipClick;
|
|
414
|
-
/**
|
|
431
|
+
/**
|
|
432
|
+
* During ads: swap skip icon for countdown text in the same control (no style changes).
|
|
433
|
+
* After ads: restore skip icon for the next track.
|
|
434
|
+
*/
|
|
415
435
|
private applyAdSkipUi;
|
|
416
436
|
private restoreTrackSkipButton;
|
|
417
437
|
private displayLine;
|
package/dist/index.d.ts
CHANGED
|
@@ -155,6 +155,7 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
|
|
|
155
155
|
private voiceListenerId;
|
|
156
156
|
private pendingPlaybackLoad;
|
|
157
157
|
private voiceAutoEndOnSilence;
|
|
158
|
+
private pausedForVoice;
|
|
158
159
|
constructor(config: SynxedConfig);
|
|
159
160
|
get currentTrack(): TrackInfo | null;
|
|
160
161
|
/** Active playback init kind from the last `play*` call (RADIO, PLAYLIST, SONG, …). */
|
|
@@ -185,6 +186,12 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
|
|
|
185
186
|
endVoiceHold(): Promise<void>;
|
|
186
187
|
/** Cancel an in-progress voice hold without sending audio to the server. */
|
|
187
188
|
cancelVoiceHold(): void;
|
|
189
|
+
/**
|
|
190
|
+
* Close the voice UI: cancel recording if active and resume playback if it was
|
|
191
|
+
* paused for voice.
|
|
192
|
+
*/
|
|
193
|
+
dismissVoiceAndResume(): void;
|
|
194
|
+
get isVoiceUiActive(): boolean;
|
|
188
195
|
pause(): void;
|
|
189
196
|
resume(): void;
|
|
190
197
|
stop(): void;
|
|
@@ -337,8 +344,6 @@ interface SynxedWebPlayerOptions {
|
|
|
337
344
|
mode?: SynxedWebPlayerMode;
|
|
338
345
|
theme?: SynxedWebPlayerTheme;
|
|
339
346
|
position?: SynxedWebPlayerPosition;
|
|
340
|
-
avatarUrl?: string;
|
|
341
|
-
voiceAvatarUrl?: string;
|
|
342
347
|
attribution?: string;
|
|
343
348
|
nowPlayingPollMs?: number;
|
|
344
349
|
powerByLabel?: string;
|
|
@@ -347,8 +352,10 @@ interface SynxedWebPlayerOptions {
|
|
|
347
352
|
style?: Partial<CSSStyleDeclaration>;
|
|
348
353
|
onMiniClick?: () => void;
|
|
349
354
|
draggable?: boolean;
|
|
350
|
-
/** Press-and-hold DJ avatar for voice
|
|
355
|
+
/** Press-and-hold DJ avatar for voice. Set `false` to disable. Default `true`. */
|
|
351
356
|
enableVoice?: boolean;
|
|
357
|
+
/** Ms to hold before voice starts (prevents opening AI on quick tap). Default `450`. */
|
|
358
|
+
voiceHoldMs?: number;
|
|
352
359
|
listenerId?: string;
|
|
353
360
|
}
|
|
354
361
|
|
|
@@ -380,6 +387,12 @@ declare class SynxedWebPlayer {
|
|
|
380
387
|
private avatarVoiceHolding;
|
|
381
388
|
private suppressMiniClick;
|
|
382
389
|
private voiceAvatarPreview;
|
|
390
|
+
private voiceHoldActivated;
|
|
391
|
+
private voiceUiDismissed;
|
|
392
|
+
private voiceHoldTimer;
|
|
393
|
+
private readonly defaultVoiceHoldMs;
|
|
394
|
+
/** Avatar to restore after an ad (DJ / album art — not ad or voice AI). */
|
|
395
|
+
private avatarImageBeforeAd;
|
|
383
396
|
constructor(options: SynxedWebPlayerOptions);
|
|
384
397
|
/** Convenience factory — same as `new SynxedWebPlayer(options)`. */
|
|
385
398
|
static mount(options: SynxedWebPlayerOptions): SynxedWebPlayer;
|
|
@@ -404,14 +417,21 @@ declare class SynxedWebPlayer {
|
|
|
404
417
|
private initEngine;
|
|
405
418
|
/** Press-and-hold DJ avatar to stream voice; release or silence auto-ends capture. */
|
|
406
419
|
private wireAvatarVoiceHold;
|
|
420
|
+
private isVoiceUiOpen;
|
|
421
|
+
private dismissVoiceUi;
|
|
422
|
+
private resetVoiceHoldState;
|
|
407
423
|
private applyVoiceVisual;
|
|
408
424
|
private isVoiceAvatarActive;
|
|
425
|
+
private getContentAvatarUrl;
|
|
409
426
|
private getAvatarImageUrl;
|
|
410
427
|
private refreshAvatarImage;
|
|
411
428
|
private startNowPlayingPoll;
|
|
412
429
|
private stopNowPlayingPoll;
|
|
413
430
|
private handleSkipClick;
|
|
414
|
-
/**
|
|
431
|
+
/**
|
|
432
|
+
* During ads: swap skip icon for countdown text in the same control (no style changes).
|
|
433
|
+
* After ads: restore skip icon for the next track.
|
|
434
|
+
*/
|
|
415
435
|
private applyAdSkipUi;
|
|
416
436
|
private restoreTrackSkipButton;
|
|
417
437
|
private displayLine;
|