@thestatic-tv/dcl-sdk 2.2.9 → 2.3.0-beta.0

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
@@ -14,11 +14,14 @@ This SDK allows DCL scene builders to:
14
14
 
15
15
  ## Pricing
16
16
 
17
- | Mode | Price | Key Prefix | Features |
18
- |------|-------|------------|----------|
19
- | **Lite** | $5/mo | `dcls_` | Session/visitor tracking only |
20
- | **Full** | $10/mo | `dclk_` | Guide, Heartbeat, Sessions, Interactions, **Guide UI**, **Chat UI** |
17
+ | Tier | Price | Features |
18
+ |------|-------|----------|
19
+ | **Free** | $5/mo | Session/visitor tracking only |
20
+ | **Standard** | $10/mo | Free + Guide UI, Chat UI, Heartbeat, Interactions |
21
+ | **Pro** | $15/mo | Standard + Admin Panel (Video tab, Mod tab, Custom scene tabs) |
21
22
 
23
+ > **All keys use `dcls_` prefix** - the server determines your subscription level.
24
+ >
22
25
  > **Free Trial**: All new scene keys include a 7-day free trial!
23
26
  >
24
27
  > **Expiry Warnings**: You'll receive dashboard notifications at 7, 3, and 1 days before your subscription expires.
@@ -35,16 +38,16 @@ If you need to rotate your API key (e.g., if compromised), you can do so from th
35
38
 
36
39
  ## Example Scene
37
40
 
38
- Clone our starter scene to get started quickly:
41
+ Clone our example scene to get started quickly:
39
42
 
40
43
  ```bash
41
- git clone https://github.com/thestatic-tv/thestatic-dcl-starter.git
42
- cd thestatic-dcl-starter
44
+ git clone https://github.com/thestatic-tv/thestatic-dcl-example.git
45
+ cd thestatic-dcl-example
43
46
  npm install
44
47
  npm start
45
48
  ```
46
49
 
47
- See the [starter repo](https://github.com/thestatic-tv/thestatic-dcl-starter) for a complete working scene.
50
+ See the [example repo](https://github.com/thestatic-tv/thestatic-dcl-example) for a complete working scene with video player integration.
48
51
 
49
52
  ## Installation
50
53
 
@@ -56,18 +59,24 @@ npm install @thestatic-tv/dcl-sdk
56
59
 
57
60
  1. Get your API key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) (DCL Scenes tab)
58
61
 
59
- 2. Initialize the client in your scene:
62
+ 2. Initialize the client in your scene's `main()` function:
60
63
 
61
64
  ```typescript
65
+ import {} from '@dcl/sdk/math'
66
+ import { engine } from '@dcl/sdk/ecs'
62
67
  import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
63
68
 
64
- const staticTV = new StaticTVClient({
65
- apiKey: 'dclk_your_api_key_here',
66
- debug: true // Enable for development
67
- })
69
+ let staticTV: StaticTVClient
70
+
71
+ export function main() {
72
+ staticTV = new StaticTVClient({
73
+ apiKey: 'dcls_your_api_key_here'
74
+ })
75
+ // Session tracking starts automatically!
76
+ }
68
77
  ```
69
78
 
70
- 3. Fetch channels and display them:
79
+ 3. Fetch channels and display them (Full mode only):
71
80
 
72
81
  ```typescript
73
82
  // Get all channels
@@ -106,33 +115,40 @@ await staticTV.interactions.follow('channel-slug')
106
115
  await staticTV.destroy()
107
116
  ```
108
117
 
109
- ## Lite Mode (Visitor Tracking Only)
118
+ ## Free Tier (Visitor Tracking Only)
110
119
 
111
120
  If you don't have a channel but want to track visitors to your scene:
112
121
 
113
122
  1. Get a scene key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) (DCL Scenes tab)
114
123
 
115
- 2. Initialize with your scene key:
124
+ 2. Initialize with your scene key in `main()`:
116
125
 
117
126
  ```typescript
127
+ import {} from '@dcl/sdk/math'
128
+ import { engine } from '@dcl/sdk/ecs'
118
129
  import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
119
130
 
120
- const staticTV = new StaticTVClient({
121
- apiKey: 'dcls_your_scene_key_here'
122
- })
131
+ let staticTV: StaticTVClient
123
132
 
124
- // Session tracking starts automatically
125
- // Visitors are tracked when they enter your scene
133
+ export function main() {
134
+ staticTV = new StaticTVClient({
135
+ apiKey: 'dcls_your_scene_key_here'
136
+ })
137
+ // Session tracking starts automatically!
138
+ }
126
139
  ```
127
140
 
128
- 3. Check if running in lite mode:
141
+ 3. Check the current tier:
129
142
 
130
143
  ```typescript
131
- if (staticTV.isLite) {
132
- console.log('Running in lite mode - session tracking only')
144
+ // Check tier (free, standard, or pro)
145
+ console.log('Current tier:', staticTV.tier)
146
+
147
+ if (staticTV.isFree) {
148
+ console.log('Running in free tier - session tracking only')
133
149
  }
134
150
 
135
- // guide, heartbeat, and interactions are null in lite mode
151
+ // guide, heartbeat, and interactions are null in free tier
136
152
  if (staticTV.guide) {
137
153
  const channels = await staticTV.guide.getChannels()
138
154
  }
@@ -148,7 +164,7 @@ Main client class for interacting with thestatic.tv.
148
164
 
149
165
  | Option | Type | Default | Description |
150
166
  |--------|------|---------|-------------|
151
- | `apiKey` | `string` | required | Your API key (`dclk_` for full, `dcls_` for lite) |
167
+ | `apiKey` | `string` | required | Your API key (all keys use `dcls_` prefix) |
152
168
  | `autoStartSession` | `boolean` | `true` | Automatically start session tracking |
153
169
  | `sessionHeartbeatInterval` | `number` | `30000` | Session heartbeat interval (ms) |
154
170
  | `watchHeartbeatInterval` | `number` | `60000` | Watch heartbeat interval (ms) |
@@ -159,15 +175,28 @@ Main client class for interacting with thestatic.tv.
159
175
  | Property | Type | Description |
160
176
  |----------|------|-------------|
161
177
  | `keyType` | `'channel' \| 'scene'` | The detected key type |
162
- | `isLite` | `boolean` | `true` if using a scene key (lite mode) |
163
- | `guide` | `GuideModule \| null` | Guide module (null in lite mode) |
178
+ | `tier` | `'free' \| 'standard' \| 'pro'` | Current subscription tier |
179
+ | `isFree` | `boolean` | `true` if free tier (session tracking only) |
180
+ | `hasStandardFeatures` | `boolean` | `true` if standard features enabled |
181
+ | `hasProFeatures` | `boolean` | `true` if pro features enabled |
182
+ | `guide` | `GuideModule \| null` | Guide module (null in free tier) |
164
183
  | `session` | `SessionModule` | Session module (always available) |
165
- | `heartbeat` | `HeartbeatModule \| null` | Heartbeat module (null in lite mode) |
166
- | `interactions` | `InteractionsModule \| null` | Interactions module (null in lite mode) |
167
- | `guideUI` | `GuideUIModule \| null` | Guide UI module (null in lite mode) |
168
- | `chatUI` | `ChatUIModule \| null` | Chat UI module (null in lite mode) |
184
+ | `heartbeat` | `HeartbeatModule \| null` | Heartbeat module (null in free tier) |
185
+ | `interactions` | `InteractionsModule \| null` | Interactions module (null in free tier) |
186
+ | `guideUI` | `GuideUIModule \| null` | Guide UI module (null in free tier) |
187
+ | `chatUI` | `ChatUIModule \| null` | Chat UI module (null in free tier) |
188
+ | `adminPanel` | `AdminPanelUIModule \| null` | Admin panel (null until enableProFeatures() called) |
189
+ | `isLite` | `boolean` | **Deprecated**: Use `isFree` instead |
169
190
 
170
- ### Guide Module (Full Mode Only)
191
+ #### Methods
192
+
193
+ | Method | Description |
194
+ |--------|-------------|
195
+ | `enableProFeatures(config)` | Enable Pro tier admin panel |
196
+ | `registerSceneTab(tab)` | Add custom scene tab (Pro tier) |
197
+ | `destroy()` | Cleanup before scene unload |
198
+
199
+ ### Guide Module (Standard/Pro Tier)
171
200
 
172
201
  ```typescript
173
202
  staticTV.guide.getChannels() // Get all channels (cached 30s)
@@ -186,9 +215,18 @@ staticTV.session.startSession() // Manually start session
186
215
  staticTV.session.endSession() // End session
187
216
  staticTV.session.getSessionId() // Get current session ID
188
217
  staticTV.session.isSessionActive() // Check if session is active
218
+
219
+ // Get scene stats (visitors, sessions, etc.)
220
+ const stats = await staticTV.session.getStats()
221
+ if (stats) {
222
+ console.log('Total sessions today:', stats.totalSessions)
223
+ console.log('Unique visitors:', stats.uniqueVisitors)
224
+ console.log('Total minutes watched:', stats.totalMinutes)
225
+ console.log('You are visitor #', stats.visitorNumber)
226
+ }
189
227
  ```
190
228
 
191
- ### Heartbeat Module (Full Mode Only)
229
+ ### Heartbeat Module (Standard/Pro Tier)
192
230
 
193
231
  Track video watching. Each heartbeat represents 1 minute watched.
194
232
 
@@ -199,7 +237,7 @@ staticTV.heartbeat.getCurrentChannel() // Get currently watched channel
199
237
  staticTV.heartbeat.isCurrentlyWatching() // Check if watching
200
238
  ```
201
239
 
202
- ### Interactions Module (Full Mode Only)
240
+ ### Interactions Module (Standard/Pro Tier)
203
241
 
204
242
  Like and follow channels. Requires wallet connection.
205
243
 
@@ -208,24 +246,27 @@ staticTV.interactions.like(channelSlug) // Like a channel
208
246
  staticTV.interactions.follow(channelSlug) // Follow a channel
209
247
  ```
210
248
 
211
- ### Guide UI Module (Full Mode Only)
249
+ ### Guide UI Module (Standard/Pro Tier)
212
250
 
213
251
  Built-in channel browser UI. You provide a callback to handle video selection.
214
252
 
215
253
  ```typescript
216
- // Configure in constructor
217
- const staticTV = new StaticTVClient({
218
- apiKey: 'dclk_your_api_key',
219
- guideUI: {
220
- onVideoSelect: (video) => {
221
- // Handle video playback in your scene
222
- console.log('Playing:', video.name, video.src)
254
+ let staticTV: StaticTVClient
255
+
256
+ export function main() {
257
+ staticTV = new StaticTVClient({
258
+ apiKey: 'dcls_your_api_key',
259
+ guideUI: {
260
+ onVideoSelect: (video) => {
261
+ // Handle video playback in your scene
262
+ console.log('Playing:', video.name, video.src)
263
+ }
223
264
  }
224
- }
225
- })
265
+ })
226
266
 
227
- // Initialize the UI (call once after client is created)
228
- await staticTV.guideUI.init()
267
+ // Initialize the UI (call once after client is created)
268
+ staticTV.guideUI.init()
269
+ }
229
270
 
230
271
  // Show/hide the guide
231
272
  staticTV.guideUI.show()
@@ -242,22 +283,25 @@ staticTV.guideUI.currentVideoId = 'video-id'
242
283
  await staticTV.guideUI.refresh()
243
284
  ```
244
285
 
245
- ### Chat UI Module (Full Mode Only)
286
+ ### Chat UI Module (Standard/Pro Tier)
246
287
 
247
288
  Real-time chat with Firebase authentication.
248
289
 
249
290
  ```typescript
250
- // Configure in constructor
251
- const staticTV = new StaticTVClient({
252
- apiKey: 'dclk_your_api_key',
253
- chatUI: {
254
- position: 'right', // 'left', 'center', or 'right'
255
- fontScale: 1.0
256
- }
257
- })
291
+ let staticTV: StaticTVClient
292
+
293
+ export function main() {
294
+ staticTV = new StaticTVClient({
295
+ apiKey: 'dcls_your_api_key',
296
+ chatUI: {
297
+ position: 'right', // 'left', 'center', or 'right'
298
+ fontScale: 1.0
299
+ }
300
+ })
258
301
 
259
- // Initialize the chat (call once after client is created)
260
- await staticTV.chatUI.init()
302
+ // Initialize the chat (call once after client is created)
303
+ staticTV.chatUI.init()
304
+ }
261
305
 
262
306
  // Show/hide the chat
263
307
  staticTV.chatUI.show()
@@ -274,23 +318,131 @@ const unread = staticTV.chatUI.unreadCount
274
318
  staticTV.chatUI.setChannel('channel-slug')
275
319
  ```
276
320
 
277
- ### Rendering UI Components
321
+ ### Admin Panel Module (Pro Tier)
278
322
 
279
- The UI modules provide `getComponent()` methods that return React-ECS elements. Call these in your UI renderer:
323
+ In-scene admin panel with Video and Mod tabs. Allows scene owners/admins to:
324
+ - Control live streaming (start/stop, rotate keys)
325
+ - Play videos from URL or predefined slots
326
+ - Manage scene admins and banned wallets
327
+ - Send broadcast messages to all players
280
328
 
281
329
  ```typescript
282
- import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
330
+ import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
331
+ import ReactEcs, { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
332
+
333
+ let staticTV: StaticTVClient
334
+
335
+ export function main() {
336
+ staticTV = new StaticTVClient({
337
+ apiKey: 'dcls_your_api_key'
338
+ })
339
+
340
+ // Enable admin panel (Pro tier)
341
+ staticTV.enableProFeatures({
342
+ // sceneId is optional - defaults to your API key ID
343
+ title: 'MY SCENE ADMIN', // Panel header title
344
+ onVideoPlay: (url) => {
345
+ // Handle video playback in your scene
346
+ videoPlayer.play(url)
347
+ },
348
+ onVideoStop: () => {
349
+ videoPlayer.stop()
350
+ },
351
+ onVideoSlotPlay: (slot) => {
352
+ // Play a predefined video slot (slot1-slot5)
353
+ fetchAndPlaySlot(slot)
354
+ },
355
+ onBroadcast: (text) => {
356
+ // Show notification to all players
357
+ showNotification(text)
358
+ },
359
+ onCommand: (type, payload) => {
360
+ // Handle custom commands (kickAll, kickBanned, etc.)
361
+ handleCommand(type, payload)
362
+ }
363
+ })
364
+ }
283
365
 
284
- // In your scene setup
366
+ // Render the admin panel
285
367
  ReactEcsRenderer.setUiRenderer(() => {
286
- return (
287
- <>
288
- {/* Your other UI elements */}
289
- {staticTV.guideUI?.getComponent()}
290
- {staticTV.chatUI?.getComponent()}
291
- </>
368
+ return staticTV.adminPanel?.getComponent()
369
+ })
370
+
371
+ // Toggle panel visibility
372
+ staticTV.adminPanel.toggle()
373
+
374
+ // Check if user has admin access
375
+ if (staticTV.adminPanel.hasAccess) { ... }
376
+ ```
377
+
378
+ #### Admin Panel Configuration Options
379
+
380
+ | Option | Type | Default | Description |
381
+ |--------|------|---------|-------------|
382
+ | `sceneId` | `string` | API key ID | Scene ID for API calls (optional - defaults to your API key ID) |
383
+ | `title` | `string` | `'ADMIN PANEL'` | Header title |
384
+ | `headerColor` | `{r,g,b,a}` | red | Header background color |
385
+ | `showVideoTab` | `boolean` | `true` | Show Video tab |
386
+ | `showModTab` | `boolean` | `true` | Show Mod tab (owners only) |
387
+ | `onVideoPlay` | `(url) => void` | - | Called when video should play |
388
+ | `onVideoStop` | `() => void` | - | Called when video should stop |
389
+ | `onVideoSlotPlay` | `(slot) => void` | - | Called when slot is selected |
390
+ | `onBroadcast` | `(text) => void` | - | Called for broadcast messages |
391
+ | `onCommand` | `(type, payload) => void` | - | Called for custom commands |
392
+ | `footerLink` | `string` | scene page | Link shown in footer |
393
+ | `debug` | `boolean` | `false` | Enable debug logging |
394
+
395
+ ### Custom Scene Tabs (Pro Tier)
396
+
397
+ Add your own scene-specific control tabs to the admin panel:
398
+
399
+ ```typescript
400
+ import ReactEcs, { UiEntity, Button, Label } from '@dcl/sdk/react-ecs'
401
+
402
+ // Enable pro features first
403
+ staticTV.enableProFeatures({ sceneId: 'my-scene', ... })
404
+
405
+ // Register custom tabs
406
+ staticTV.registerSceneTab({
407
+ label: 'LIGHTS',
408
+ id: 'lights',
409
+ render: () => (
410
+ <UiEntity uiTransform={{ flexDirection: 'column', padding: 8 }}>
411
+ <Label value="Light Controls" fontSize={14} />
412
+ <Button value="Disco Mode" onMouseDown={() => setDiscoLights()} />
413
+ <Button value="Ambient" onMouseDown={() => setAmbientLights()} />
414
+ <Button value="Off" onMouseDown={() => turnOffLights()} />
415
+ </UiEntity>
292
416
  )
293
417
  })
418
+
419
+ staticTV.registerSceneTab({
420
+ label: 'DOORS',
421
+ id: 'doors',
422
+ render: () => <MyDoorsControls />
423
+ })
424
+ ```
425
+
426
+ Custom tabs appear before the Video and Mod tabs in the tab bar.
427
+
428
+ ### Rendering UI Components
429
+
430
+ The UI modules provide `getComponent()` methods that return React-ECS elements. Add the renderer **outside** your `main()` function:
431
+
432
+ ```typescript
433
+ import ReactEcs, { ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
434
+
435
+ // Outside main() - required by DCL
436
+ ReactEcsRenderer.setUiRenderer(() => {
437
+ if (!staticTV) return null
438
+ return ReactEcs.createElement(UiEntity, {
439
+ uiTransform: { width: '100%', height: '100%', positionType: 'absolute' },
440
+ children: [
441
+ staticTV.guideUI?.getComponent(),
442
+ staticTV.chatUI?.getComponent()
443
+ ].filter(Boolean)
444
+ })
445
+ })
294
446
  ```
295
447
 
296
448
  ## Metrics Attribution