@thestatic-tv/dcl-sdk 2.2.10 → 2.3.0-beta.1

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,10 +14,11 @@ This SDK allows DCL scene builders to:
14
14
 
15
15
  ## Pricing
16
16
 
17
- | Mode | Price | Features |
17
+ | Tier | Price | Features |
18
18
  |------|-------|----------|
19
- | **Lite** | $5/mo | Session/visitor tracking only |
20
- | **Full** | $10/mo | Guide UI, Chat UI, Heartbeat, Sessions, Interactions |
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
 
22
23
  > **All keys use `dcls_` prefix** - the server determines your subscription level.
23
24
  >
@@ -37,16 +38,16 @@ If you need to rotate your API key (e.g., if compromised), you can do so from th
37
38
 
38
39
  ## Example Scene
39
40
 
40
- Clone our starter scene to get started quickly:
41
+ Clone our example scene to get started quickly:
41
42
 
42
43
  ```bash
43
- git clone https://github.com/thestatic-tv/thestatic-dcl-starter.git
44
- cd thestatic-dcl-starter
44
+ git clone https://github.com/thestatic-tv/thestatic-dcl-example.git
45
+ cd thestatic-dcl-example
45
46
  npm install
46
47
  npm start
47
48
  ```
48
49
 
49
- 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.
50
51
 
51
52
  ## Installation
52
53
 
@@ -58,18 +59,24 @@ npm install @thestatic-tv/dcl-sdk
58
59
 
59
60
  1. Get your API key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) (DCL Scenes tab)
60
61
 
61
- 2. Initialize the client in your scene:
62
+ 2. Initialize the client in your scene's `main()` function:
62
63
 
63
64
  ```typescript
65
+ import {} from '@dcl/sdk/math'
66
+ import { engine } from '@dcl/sdk/ecs'
64
67
  import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
65
68
 
66
- const staticTV = new StaticTVClient({
67
- apiKey: 'dcls_your_api_key_here',
68
- debug: true // Enable for development
69
- })
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
+ }
70
77
  ```
71
78
 
72
- 3. Fetch channels and display them:
79
+ 3. Fetch channels and display them (Full mode only):
73
80
 
74
81
  ```typescript
75
82
  // Get all channels
@@ -108,33 +115,40 @@ await staticTV.interactions.follow('channel-slug')
108
115
  await staticTV.destroy()
109
116
  ```
110
117
 
111
- ## Lite Mode (Visitor Tracking Only)
118
+ ## Free Tier (Visitor Tracking Only)
112
119
 
113
120
  If you don't have a channel but want to track visitors to your scene:
114
121
 
115
122
  1. Get a scene key from [thestatic.tv/dashboard](https://thestatic.tv/dashboard) (DCL Scenes tab)
116
123
 
117
- 2. Initialize with your scene key:
124
+ 2. Initialize with your scene key in `main()`:
118
125
 
119
126
  ```typescript
127
+ import {} from '@dcl/sdk/math'
128
+ import { engine } from '@dcl/sdk/ecs'
120
129
  import { StaticTVClient } from '@thestatic-tv/dcl-sdk'
121
130
 
122
- const staticTV = new StaticTVClient({
123
- apiKey: 'dcls_your_scene_key_here'
124
- })
131
+ let staticTV: StaticTVClient
125
132
 
126
- // Session tracking starts automatically
127
- // 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
+ }
128
139
  ```
129
140
 
130
- 3. Check if running in lite mode:
141
+ 3. Check the current tier:
131
142
 
132
143
  ```typescript
133
- if (staticTV.isLite) {
134
- 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')
135
149
  }
136
150
 
137
- // guide, heartbeat, and interactions are null in lite mode
151
+ // guide, heartbeat, and interactions are null in free tier
138
152
  if (staticTV.guide) {
139
153
  const channels = await staticTV.guide.getChannels()
140
154
  }
@@ -161,15 +175,28 @@ Main client class for interacting with thestatic.tv.
161
175
  | Property | Type | Description |
162
176
  |----------|------|-------------|
163
177
  | `keyType` | `'channel' \| 'scene'` | The detected key type |
164
- | `isLite` | `boolean` | `true` if using a scene key (lite mode) |
165
- | `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) |
166
183
  | `session` | `SessionModule` | Session module (always available) |
167
- | `heartbeat` | `HeartbeatModule \| null` | Heartbeat module (null in lite mode) |
168
- | `interactions` | `InteractionsModule \| null` | Interactions module (null in lite mode) |
169
- | `guideUI` | `GuideUIModule \| null` | Guide UI module (null in lite mode) |
170
- | `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 |
190
+
191
+ #### Methods
171
192
 
172
- ### Guide Module (Full Mode Only)
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)
173
200
 
174
201
  ```typescript
175
202
  staticTV.guide.getChannels() // Get all channels (cached 30s)
@@ -188,9 +215,18 @@ staticTV.session.startSession() // Manually start session
188
215
  staticTV.session.endSession() // End session
189
216
  staticTV.session.getSessionId() // Get current session ID
190
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
+ }
191
227
  ```
192
228
 
193
- ### Heartbeat Module (Full Mode Only)
229
+ ### Heartbeat Module (Standard/Pro Tier)
194
230
 
195
231
  Track video watching. Each heartbeat represents 1 minute watched.
196
232
 
@@ -201,7 +237,7 @@ staticTV.heartbeat.getCurrentChannel() // Get currently watched channel
201
237
  staticTV.heartbeat.isCurrentlyWatching() // Check if watching
202
238
  ```
203
239
 
204
- ### Interactions Module (Full Mode Only)
240
+ ### Interactions Module (Standard/Pro Tier)
205
241
 
206
242
  Like and follow channels. Requires wallet connection.
207
243
 
@@ -210,24 +246,27 @@ staticTV.interactions.like(channelSlug) // Like a channel
210
246
  staticTV.interactions.follow(channelSlug) // Follow a channel
211
247
  ```
212
248
 
213
- ### Guide UI Module (Full Mode Only)
249
+ ### Guide UI Module (Standard/Pro Tier)
214
250
 
215
251
  Built-in channel browser UI. You provide a callback to handle video selection.
216
252
 
217
253
  ```typescript
218
- // Configure in constructor
219
- const staticTV = new StaticTVClient({
220
- apiKey: 'dcls_your_api_key',
221
- guideUI: {
222
- onVideoSelect: (video) => {
223
- // Handle video playback in your scene
224
- 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
+ }
225
264
  }
226
- }
227
- })
265
+ })
228
266
 
229
- // Initialize the UI (call once after client is created)
230
- await staticTV.guideUI.init()
267
+ // Initialize the UI (call once after client is created)
268
+ staticTV.guideUI.init()
269
+ }
231
270
 
232
271
  // Show/hide the guide
233
272
  staticTV.guideUI.show()
@@ -244,22 +283,25 @@ staticTV.guideUI.currentVideoId = 'video-id'
244
283
  await staticTV.guideUI.refresh()
245
284
  ```
246
285
 
247
- ### Chat UI Module (Full Mode Only)
286
+ ### Chat UI Module (Standard/Pro Tier)
248
287
 
249
288
  Real-time chat with Firebase authentication.
250
289
 
251
290
  ```typescript
252
- // Configure in constructor
253
- const staticTV = new StaticTVClient({
254
- apiKey: 'dcls_your_api_key',
255
- chatUI: {
256
- position: 'right', // 'left', 'center', or 'right'
257
- fontScale: 1.0
258
- }
259
- })
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
+ })
260
301
 
261
- // Initialize the chat (call once after client is created)
262
- await staticTV.chatUI.init()
302
+ // Initialize the chat (call once after client is created)
303
+ staticTV.chatUI.init()
304
+ }
263
305
 
264
306
  // Show/hide the chat
265
307
  staticTV.chatUI.show()
@@ -276,23 +318,131 @@ const unread = staticTV.chatUI.unreadCount
276
318
  staticTV.chatUI.setChannel('channel-slug')
277
319
  ```
278
320
 
279
- ### Rendering UI Components
321
+ ### Admin Panel Module (Pro Tier)
280
322
 
281
- 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
282
328
 
283
329
  ```typescript
284
- 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
285
334
 
286
- // In your scene setup
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
+ }
365
+
366
+ // Render the admin panel
287
367
  ReactEcsRenderer.setUiRenderer(() => {
288
- return (
289
- <>
290
- {/* Your other UI elements */}
291
- {staticTV.guideUI?.getComponent()}
292
- {staticTV.chatUI?.getComponent()}
293
- </>
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>
294
416
  )
295
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
+ })
296
446
  ```
297
447
 
298
448
  ## Metrics Attribution