@thestatic-tv/dcl-sdk 2.0.3 → 2.2.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/dist/index.d.mts CHANGED
@@ -91,6 +91,8 @@ interface GuideResponse {
91
91
  interface SessionResponse {
92
92
  success: boolean;
93
93
  sessionId?: string;
94
+ keyType?: 'scene' | 'channel';
95
+ sdkType?: 'lite' | 'full';
94
96
  version: string;
95
97
  }
96
98
  /**
@@ -247,9 +249,16 @@ declare class SessionModule {
247
249
  private sessionId;
248
250
  private heartbeatTimerId;
249
251
  private isActive;
252
+ private _sdkType;
250
253
  constructor(client: StaticTVClient);
254
+ /**
255
+ * Get the SDK type returned by the server
256
+ * Determines what features are available (lite = sessions only, full = guide + chat + more)
257
+ */
258
+ get sdkType(): 'lite' | 'full';
251
259
  /**
252
260
  * Get the appropriate session endpoint based on key type
261
+ * All dcls_ keys use /scene-session, dclk_ keys use /session
253
262
  */
254
263
  private getEndpoint;
255
264
  /**
@@ -420,6 +429,7 @@ declare class GuideUIModule {
420
429
  private handleChannelClick;
421
430
  /**
422
431
  * Get the guide UI component for rendering
432
+ * Returns toggle button when hidden, full panel when visible
423
433
  */
424
434
  getComponent: () => any;
425
435
  private renderLeftPanel;
@@ -428,6 +438,7 @@ declare class GuideUIModule {
428
438
  private renderCardGrid;
429
439
  private renderGuideCard;
430
440
  private renderCloseButton;
441
+ private renderToggleButton;
431
442
  }
432
443
 
433
444
  /**
@@ -508,6 +519,7 @@ declare class ChatUIModule {
508
519
  private formatTime;
509
520
  /**
510
521
  * Get the chat UI component for rendering
522
+ * Returns toggle button when hidden, full panel when visible
511
523
  */
512
524
  getComponent: () => any;
513
525
  private getPositionStyle;
@@ -519,17 +531,18 @@ declare class ChatUIModule {
519
531
  private renderUserInfoBar;
520
532
  private renderInputArea;
521
533
  private renderChannelDropdown;
534
+ private renderToggleButton;
522
535
  }
523
536
 
524
537
  /**
525
538
  * StaticTVClient - Main client for connecting DCL scenes to thestatic.tv
526
539
  *
527
- * Supports two key types:
528
- * - dclk_* : Channel keys (full access - guide, heartbeat, interactions, UI)
529
- * - dcls_* : Scene-only keys (lite - session tracking only)
540
+ * All keys use dcls_ prefix. Features enabled based on sdkType from server:
541
+ * - lite: Session tracking only
542
+ * - full: Guide, Chat, Heartbeat, Interactions + session tracking
530
543
  */
531
544
 
532
- /** Key type constants */
545
+ /** Key type constants (legacy - kept for compatibility) */
533
546
  declare const KEY_TYPE_CHANNEL = "channel";
534
547
  declare const KEY_TYPE_SCENE = "scene";
535
548
  declare class StaticTVClient {
@@ -537,18 +550,19 @@ declare class StaticTVClient {
537
550
  private baseUrl;
538
551
  private _keyType;
539
552
  private _disabled;
540
- /** Guide module - fetch channel lineup (channel keys only) */
541
- readonly guide: GuideModule | null;
553
+ private _fullFeaturesEnabled;
554
+ /** Guide module - fetch channel lineup (full SDK only) */
555
+ guide: GuideModule | null;
542
556
  /** Session module - track visitor sessions (all keys, null when disabled) */
543
- readonly session: SessionModule | null;
544
- /** Heartbeat module - track video watching (channel keys only) */
545
- readonly heartbeat: HeartbeatModule | null;
546
- /** Interactions module - like/follow channels (channel keys only) */
547
- readonly interactions: InteractionsModule | null;
548
- /** Guide UI module - channel browser UI (channel keys only) */
549
- readonly guideUI: GuideUIModule | null;
550
- /** Chat UI module - real-time chat UI (channel keys only) */
551
- readonly chatUI: ChatUIModule | null;
557
+ session: SessionModule | null;
558
+ /** Heartbeat module - track video watching (full SDK only) */
559
+ heartbeat: HeartbeatModule | null;
560
+ /** Interactions module - like/follow channels (full SDK only) */
561
+ interactions: InteractionsModule | null;
562
+ /** Guide UI module - channel browser UI (full SDK only) */
563
+ guideUI: GuideUIModule | null;
564
+ /** Chat UI module - real-time chat UI (full SDK only) */
565
+ chatUI: ChatUIModule | null;
552
566
  /**
553
567
  * Create a new StaticTVClient
554
568
  *
@@ -578,7 +592,8 @@ declare class StaticTVClient {
578
592
  */
579
593
  get isDisabled(): boolean;
580
594
  /**
581
- * Check if this is a lite (scene-only) client
595
+ * Check if this is a lite client (no full features)
596
+ * Returns true until session confirms sdkType is 'full'
582
597
  */
583
598
  get isLite(): boolean;
584
599
  /**
@@ -596,6 +611,25 @@ declare class StaticTVClient {
596
611
  * @internal
597
612
  */
598
613
  getConfig(): StaticTVConfig;
614
+ /**
615
+ * Initialize full feature modules (guide, heartbeat, interactions, UI)
616
+ * @internal
617
+ */
618
+ private _initFullModules;
619
+ /**
620
+ * Called by SessionModule when server confirms sdkType is 'full'
621
+ * Enables guide, chat, heartbeat, and interactions modules
622
+ * @internal
623
+ */
624
+ _enableFullFeatures(): void;
625
+ /**
626
+ * Check if full features are enabled (server confirmed sdkType: 'full')
627
+ */
628
+ get hasFullFeatures(): boolean;
629
+ /**
630
+ * Get the SDK type (lite or full) - only available after session starts
631
+ */
632
+ get sdkType(): 'lite' | 'full';
599
633
  /**
600
634
  * Cleanup when done (call before scene unload)
601
635
  */
package/dist/index.d.ts CHANGED
@@ -91,6 +91,8 @@ interface GuideResponse {
91
91
  interface SessionResponse {
92
92
  success: boolean;
93
93
  sessionId?: string;
94
+ keyType?: 'scene' | 'channel';
95
+ sdkType?: 'lite' | 'full';
94
96
  version: string;
95
97
  }
96
98
  /**
@@ -247,9 +249,16 @@ declare class SessionModule {
247
249
  private sessionId;
248
250
  private heartbeatTimerId;
249
251
  private isActive;
252
+ private _sdkType;
250
253
  constructor(client: StaticTVClient);
254
+ /**
255
+ * Get the SDK type returned by the server
256
+ * Determines what features are available (lite = sessions only, full = guide + chat + more)
257
+ */
258
+ get sdkType(): 'lite' | 'full';
251
259
  /**
252
260
  * Get the appropriate session endpoint based on key type
261
+ * All dcls_ keys use /scene-session, dclk_ keys use /session
253
262
  */
254
263
  private getEndpoint;
255
264
  /**
@@ -420,6 +429,7 @@ declare class GuideUIModule {
420
429
  private handleChannelClick;
421
430
  /**
422
431
  * Get the guide UI component for rendering
432
+ * Returns toggle button when hidden, full panel when visible
423
433
  */
424
434
  getComponent: () => any;
425
435
  private renderLeftPanel;
@@ -428,6 +438,7 @@ declare class GuideUIModule {
428
438
  private renderCardGrid;
429
439
  private renderGuideCard;
430
440
  private renderCloseButton;
441
+ private renderToggleButton;
431
442
  }
432
443
 
433
444
  /**
@@ -508,6 +519,7 @@ declare class ChatUIModule {
508
519
  private formatTime;
509
520
  /**
510
521
  * Get the chat UI component for rendering
522
+ * Returns toggle button when hidden, full panel when visible
511
523
  */
512
524
  getComponent: () => any;
513
525
  private getPositionStyle;
@@ -519,17 +531,18 @@ declare class ChatUIModule {
519
531
  private renderUserInfoBar;
520
532
  private renderInputArea;
521
533
  private renderChannelDropdown;
534
+ private renderToggleButton;
522
535
  }
523
536
 
524
537
  /**
525
538
  * StaticTVClient - Main client for connecting DCL scenes to thestatic.tv
526
539
  *
527
- * Supports two key types:
528
- * - dclk_* : Channel keys (full access - guide, heartbeat, interactions, UI)
529
- * - dcls_* : Scene-only keys (lite - session tracking only)
540
+ * All keys use dcls_ prefix. Features enabled based on sdkType from server:
541
+ * - lite: Session tracking only
542
+ * - full: Guide, Chat, Heartbeat, Interactions + session tracking
530
543
  */
531
544
 
532
- /** Key type constants */
545
+ /** Key type constants (legacy - kept for compatibility) */
533
546
  declare const KEY_TYPE_CHANNEL = "channel";
534
547
  declare const KEY_TYPE_SCENE = "scene";
535
548
  declare class StaticTVClient {
@@ -537,18 +550,19 @@ declare class StaticTVClient {
537
550
  private baseUrl;
538
551
  private _keyType;
539
552
  private _disabled;
540
- /** Guide module - fetch channel lineup (channel keys only) */
541
- readonly guide: GuideModule | null;
553
+ private _fullFeaturesEnabled;
554
+ /** Guide module - fetch channel lineup (full SDK only) */
555
+ guide: GuideModule | null;
542
556
  /** Session module - track visitor sessions (all keys, null when disabled) */
543
- readonly session: SessionModule | null;
544
- /** Heartbeat module - track video watching (channel keys only) */
545
- readonly heartbeat: HeartbeatModule | null;
546
- /** Interactions module - like/follow channels (channel keys only) */
547
- readonly interactions: InteractionsModule | null;
548
- /** Guide UI module - channel browser UI (channel keys only) */
549
- readonly guideUI: GuideUIModule | null;
550
- /** Chat UI module - real-time chat UI (channel keys only) */
551
- readonly chatUI: ChatUIModule | null;
557
+ session: SessionModule | null;
558
+ /** Heartbeat module - track video watching (full SDK only) */
559
+ heartbeat: HeartbeatModule | null;
560
+ /** Interactions module - like/follow channels (full SDK only) */
561
+ interactions: InteractionsModule | null;
562
+ /** Guide UI module - channel browser UI (full SDK only) */
563
+ guideUI: GuideUIModule | null;
564
+ /** Chat UI module - real-time chat UI (full SDK only) */
565
+ chatUI: ChatUIModule | null;
552
566
  /**
553
567
  * Create a new StaticTVClient
554
568
  *
@@ -578,7 +592,8 @@ declare class StaticTVClient {
578
592
  */
579
593
  get isDisabled(): boolean;
580
594
  /**
581
- * Check if this is a lite (scene-only) client
595
+ * Check if this is a lite client (no full features)
596
+ * Returns true until session confirms sdkType is 'full'
582
597
  */
583
598
  get isLite(): boolean;
584
599
  /**
@@ -596,6 +611,25 @@ declare class StaticTVClient {
596
611
  * @internal
597
612
  */
598
613
  getConfig(): StaticTVConfig;
614
+ /**
615
+ * Initialize full feature modules (guide, heartbeat, interactions, UI)
616
+ * @internal
617
+ */
618
+ private _initFullModules;
619
+ /**
620
+ * Called by SessionModule when server confirms sdkType is 'full'
621
+ * Enables guide, chat, heartbeat, and interactions modules
622
+ * @internal
623
+ */
624
+ _enableFullFeatures(): void;
625
+ /**
626
+ * Check if full features are enabled (server confirmed sdkType: 'full')
627
+ */
628
+ get hasFullFeatures(): boolean;
629
+ /**
630
+ * Get the SDK type (lite or full) - only available after session starts
631
+ */
632
+ get sdkType(): 'lite' | 'full';
599
633
  /**
600
634
  * Cleanup when done (call before scene unload)
601
635
  */
package/dist/index.js CHANGED
@@ -249,13 +249,22 @@ var SessionModule = class {
249
249
  this.sessionId = null;
250
250
  this.heartbeatTimerId = null;
251
251
  this.isActive = false;
252
+ this._sdkType = "lite";
252
253
  this.client = client;
253
254
  }
255
+ /**
256
+ * Get the SDK type returned by the server
257
+ * Determines what features are available (lite = sessions only, full = guide + chat + more)
258
+ */
259
+ get sdkType() {
260
+ return this._sdkType;
261
+ }
254
262
  /**
255
263
  * Get the appropriate session endpoint based on key type
264
+ * All dcls_ keys use /scene-session, dclk_ keys use /session
256
265
  */
257
266
  getEndpoint() {
258
- return this.client.isLite ? "/scene-session" : "/session";
267
+ return this.client.keyType === "scene" ? "/scene-session" : "/session";
259
268
  }
260
269
  /**
261
270
  * Get player wallet - prioritize config, fallback to runtime detection
@@ -296,8 +305,12 @@ var SessionModule = class {
296
305
  if (response.success && response.sessionId) {
297
306
  this.sessionId = response.sessionId;
298
307
  this.isActive = true;
308
+ this._sdkType = response.sdkType || "lite";
299
309
  this.startHeartbeat();
300
- this.client.log(`Session started: ${this.sessionId}`);
310
+ this.client.log(`Session started: ${this.sessionId}, sdkType: ${this._sdkType}`);
311
+ if (this._sdkType === "full") {
312
+ this.client._enableFullFeatures();
313
+ }
301
314
  return this.sessionId;
302
315
  }
303
316
  return null;
@@ -765,9 +778,12 @@ var GuideUIModule = class {
765
778
  // =============================================================================
766
779
  /**
767
780
  * Get the guide UI component for rendering
781
+ * Returns toggle button when hidden, full panel when visible
768
782
  */
769
783
  this.getComponent = () => {
770
- if (!this._isVisible) return null;
784
+ if (!this._isVisible) {
785
+ return this.renderToggleButton();
786
+ }
771
787
  const windowW = this.s(UI_DIMENSIONS.guide.width);
772
788
  return import_react_ecs2.default.createElement(import_react_ecs2.UiEntity, {
773
789
  uiTransform: {
@@ -1323,6 +1339,30 @@ var GuideUIModule = class {
1323
1339
  ]
1324
1340
  });
1325
1341
  }
1342
+ renderToggleButton() {
1343
+ return import_react_ecs2.default.createElement(import_react_ecs2.UiEntity, {
1344
+ uiTransform: {
1345
+ positionType: "absolute",
1346
+ position: { right: 20, top: "40%" },
1347
+ width: this.s(80),
1348
+ height: this.s(40),
1349
+ justifyContent: "center",
1350
+ alignItems: "center"
1351
+ },
1352
+ uiBackground: { color: import_math3.Color4.create(0, 0.5, 0.5, 0.9) },
1353
+ onMouseDown: () => this.show(),
1354
+ children: [
1355
+ import_react_ecs2.default.createElement(import_react_ecs2.UiEntity, {
1356
+ uiText: {
1357
+ value: "GUIDE",
1358
+ fontSize: this.s(16),
1359
+ color: THEME.colors.white,
1360
+ textAlign: "middle-center"
1361
+ }
1362
+ })
1363
+ ]
1364
+ });
1365
+ }
1326
1366
  };
1327
1367
 
1328
1368
  // src/ui/chat-ui.tsx
@@ -1367,9 +1407,12 @@ var ChatUIModule = class {
1367
1407
  // =============================================================================
1368
1408
  /**
1369
1409
  * Get the chat UI component for rendering
1410
+ * Returns toggle button when hidden, full panel when visible
1370
1411
  */
1371
1412
  this.getComponent = () => {
1372
- if (!this._isVisible) return null;
1413
+ if (!this._isVisible) {
1414
+ return this.renderToggleButton();
1415
+ }
1373
1416
  const scaledTheme = scaleChatTheme(DEFAULT_CHAT_THEME, this.fontScale);
1374
1417
  const positionStyle = this.getPositionStyle();
1375
1418
  return import_react_ecs3.default.createElement(import_react_ecs3.UiEntity, {
@@ -2094,6 +2137,31 @@ var ChatUIModule = class {
2094
2137
  ]
2095
2138
  });
2096
2139
  }
2140
+ renderToggleButton() {
2141
+ const unreadBadge = this._unreadCount > 0 ? ` (${this._unreadCount})` : "";
2142
+ return import_react_ecs3.default.createElement(import_react_ecs3.UiEntity, {
2143
+ uiTransform: {
2144
+ positionType: "absolute",
2145
+ position: { right: 20, top: "48%" },
2146
+ width: 80,
2147
+ height: 40,
2148
+ justifyContent: "center",
2149
+ alignItems: "center"
2150
+ },
2151
+ uiBackground: { color: import_math4.Color4.create(0.6, 0, 0.5, 0.9) },
2152
+ onMouseDown: () => this.show(),
2153
+ children: [
2154
+ import_react_ecs3.default.createElement(import_react_ecs3.UiEntity, {
2155
+ uiText: {
2156
+ value: `CHAT${unreadBadge}`,
2157
+ fontSize: 16,
2158
+ color: THEME.colors.white,
2159
+ textAlign: "middle-center"
2160
+ }
2161
+ })
2162
+ ]
2163
+ });
2164
+ }
2097
2165
  };
2098
2166
 
2099
2167
  // src/StaticTVClient.ts
@@ -2123,6 +2191,19 @@ var StaticTVClient = class {
2123
2191
  constructor(config) {
2124
2192
  this._keyType = null;
2125
2193
  this._disabled = false;
2194
+ this._fullFeaturesEnabled = false;
2195
+ /** Guide module - fetch channel lineup (full SDK only) */
2196
+ this.guide = null;
2197
+ /** Session module - track visitor sessions (all keys, null when disabled) */
2198
+ this.session = null;
2199
+ /** Heartbeat module - track video watching (full SDK only) */
2200
+ this.heartbeat = null;
2201
+ /** Interactions module - like/follow channels (full SDK only) */
2202
+ this.interactions = null;
2203
+ /** Guide UI module - channel browser UI (full SDK only) */
2204
+ this.guideUI = null;
2205
+ /** Chat UI module - real-time chat UI (full SDK only) */
2206
+ this.chatUI = null;
2126
2207
  this.config = {
2127
2208
  autoStartSession: true,
2128
2209
  sessionHeartbeatInterval: 3e4,
@@ -2151,27 +2232,11 @@ var StaticTVClient = class {
2151
2232
  console.log("[StaticTV] Invalid apiKey format (must start with dclk_ or dcls_) - tracking disabled. Scene will load normally.");
2152
2233
  this._disabled = true;
2153
2234
  this._keyType = null;
2154
- this.session = null;
2155
- this.guide = null;
2156
- this.heartbeat = null;
2157
- this.interactions = null;
2158
- this.guideUI = null;
2159
- this.chatUI = null;
2160
2235
  return;
2161
2236
  }
2162
2237
  this.session = new SessionModule(this);
2163
2238
  if (this._keyType === KEY_TYPE_CHANNEL) {
2164
- this.guide = new GuideModule(this);
2165
- this.heartbeat = new HeartbeatModule(this);
2166
- this.interactions = new InteractionsModule(this);
2167
- this.guideUI = new GuideUIModule(this, config.guideUI);
2168
- this.chatUI = new ChatUIModule(this, config.chatUI);
2169
- } else {
2170
- this.guide = null;
2171
- this.heartbeat = null;
2172
- this.interactions = null;
2173
- this.guideUI = null;
2174
- this.chatUI = null;
2239
+ this._initFullModules();
2175
2240
  }
2176
2241
  if (this.config.autoStartSession) {
2177
2242
  fetchUserData().then(() => {
@@ -2200,10 +2265,11 @@ var StaticTVClient = class {
2200
2265
  return this._disabled;
2201
2266
  }
2202
2267
  /**
2203
- * Check if this is a lite (scene-only) client
2268
+ * Check if this is a lite client (no full features)
2269
+ * Returns true until session confirms sdkType is 'full'
2204
2270
  */
2205
2271
  get isLite() {
2206
- return this._keyType === KEY_TYPE_SCENE;
2272
+ return !this._fullFeaturesEnabled;
2207
2273
  }
2208
2274
  /**
2209
2275
  * Make an authenticated API request
@@ -2236,6 +2302,40 @@ var StaticTVClient = class {
2236
2302
  getConfig() {
2237
2303
  return this.config;
2238
2304
  }
2305
+ /**
2306
+ * Initialize full feature modules (guide, heartbeat, interactions, UI)
2307
+ * @internal
2308
+ */
2309
+ _initFullModules() {
2310
+ if (this._fullFeaturesEnabled) return;
2311
+ this.guide = new GuideModule(this);
2312
+ this.heartbeat = new HeartbeatModule(this);
2313
+ this.interactions = new InteractionsModule(this);
2314
+ this.guideUI = new GuideUIModule(this, this.config.guideUI);
2315
+ this.chatUI = new ChatUIModule(this, this.config.chatUI);
2316
+ this._fullFeaturesEnabled = true;
2317
+ this.log("Full features enabled (guide, chat, heartbeat, interactions)");
2318
+ }
2319
+ /**
2320
+ * Called by SessionModule when server confirms sdkType is 'full'
2321
+ * Enables guide, chat, heartbeat, and interactions modules
2322
+ * @internal
2323
+ */
2324
+ _enableFullFeatures() {
2325
+ this._initFullModules();
2326
+ }
2327
+ /**
2328
+ * Check if full features are enabled (server confirmed sdkType: 'full')
2329
+ */
2330
+ get hasFullFeatures() {
2331
+ return this._fullFeaturesEnabled;
2332
+ }
2333
+ /**
2334
+ * Get the SDK type (lite or full) - only available after session starts
2335
+ */
2336
+ get sdkType() {
2337
+ return this.session?.sdkType || "lite";
2338
+ }
2239
2339
  /**
2240
2340
  * Cleanup when done (call before scene unload)
2241
2341
  */
package/dist/index.mjs CHANGED
@@ -209,13 +209,22 @@ var SessionModule = class {
209
209
  this.sessionId = null;
210
210
  this.heartbeatTimerId = null;
211
211
  this.isActive = false;
212
+ this._sdkType = "lite";
212
213
  this.client = client;
213
214
  }
215
+ /**
216
+ * Get the SDK type returned by the server
217
+ * Determines what features are available (lite = sessions only, full = guide + chat + more)
218
+ */
219
+ get sdkType() {
220
+ return this._sdkType;
221
+ }
214
222
  /**
215
223
  * Get the appropriate session endpoint based on key type
224
+ * All dcls_ keys use /scene-session, dclk_ keys use /session
216
225
  */
217
226
  getEndpoint() {
218
- return this.client.isLite ? "/scene-session" : "/session";
227
+ return this.client.keyType === "scene" ? "/scene-session" : "/session";
219
228
  }
220
229
  /**
221
230
  * Get player wallet - prioritize config, fallback to runtime detection
@@ -256,8 +265,12 @@ var SessionModule = class {
256
265
  if (response.success && response.sessionId) {
257
266
  this.sessionId = response.sessionId;
258
267
  this.isActive = true;
268
+ this._sdkType = response.sdkType || "lite";
259
269
  this.startHeartbeat();
260
- this.client.log(`Session started: ${this.sessionId}`);
270
+ this.client.log(`Session started: ${this.sessionId}, sdkType: ${this._sdkType}`);
271
+ if (this._sdkType === "full") {
272
+ this.client._enableFullFeatures();
273
+ }
261
274
  return this.sessionId;
262
275
  }
263
276
  return null;
@@ -725,9 +738,12 @@ var GuideUIModule = class {
725
738
  // =============================================================================
726
739
  /**
727
740
  * Get the guide UI component for rendering
741
+ * Returns toggle button when hidden, full panel when visible
728
742
  */
729
743
  this.getComponent = () => {
730
- if (!this._isVisible) return null;
744
+ if (!this._isVisible) {
745
+ return this.renderToggleButton();
746
+ }
731
747
  const windowW = this.s(UI_DIMENSIONS.guide.width);
732
748
  return ReactEcs2.createElement(UiEntity2, {
733
749
  uiTransform: {
@@ -1283,6 +1299,30 @@ var GuideUIModule = class {
1283
1299
  ]
1284
1300
  });
1285
1301
  }
1302
+ renderToggleButton() {
1303
+ return ReactEcs2.createElement(UiEntity2, {
1304
+ uiTransform: {
1305
+ positionType: "absolute",
1306
+ position: { right: 20, top: "40%" },
1307
+ width: this.s(80),
1308
+ height: this.s(40),
1309
+ justifyContent: "center",
1310
+ alignItems: "center"
1311
+ },
1312
+ uiBackground: { color: Color43.create(0, 0.5, 0.5, 0.9) },
1313
+ onMouseDown: () => this.show(),
1314
+ children: [
1315
+ ReactEcs2.createElement(UiEntity2, {
1316
+ uiText: {
1317
+ value: "GUIDE",
1318
+ fontSize: this.s(16),
1319
+ color: THEME.colors.white,
1320
+ textAlign: "middle-center"
1321
+ }
1322
+ })
1323
+ ]
1324
+ });
1325
+ }
1286
1326
  };
1287
1327
 
1288
1328
  // src/ui/chat-ui.tsx
@@ -1327,9 +1367,12 @@ var ChatUIModule = class {
1327
1367
  // =============================================================================
1328
1368
  /**
1329
1369
  * Get the chat UI component for rendering
1370
+ * Returns toggle button when hidden, full panel when visible
1330
1371
  */
1331
1372
  this.getComponent = () => {
1332
- if (!this._isVisible) return null;
1373
+ if (!this._isVisible) {
1374
+ return this.renderToggleButton();
1375
+ }
1333
1376
  const scaledTheme = scaleChatTheme(DEFAULT_CHAT_THEME, this.fontScale);
1334
1377
  const positionStyle = this.getPositionStyle();
1335
1378
  return ReactEcs3.createElement(UiEntity3, {
@@ -2054,6 +2097,31 @@ var ChatUIModule = class {
2054
2097
  ]
2055
2098
  });
2056
2099
  }
2100
+ renderToggleButton() {
2101
+ const unreadBadge = this._unreadCount > 0 ? ` (${this._unreadCount})` : "";
2102
+ return ReactEcs3.createElement(UiEntity3, {
2103
+ uiTransform: {
2104
+ positionType: "absolute",
2105
+ position: { right: 20, top: "48%" },
2106
+ width: 80,
2107
+ height: 40,
2108
+ justifyContent: "center",
2109
+ alignItems: "center"
2110
+ },
2111
+ uiBackground: { color: Color44.create(0.6, 0, 0.5, 0.9) },
2112
+ onMouseDown: () => this.show(),
2113
+ children: [
2114
+ ReactEcs3.createElement(UiEntity3, {
2115
+ uiText: {
2116
+ value: `CHAT${unreadBadge}`,
2117
+ fontSize: 16,
2118
+ color: THEME.colors.white,
2119
+ textAlign: "middle-center"
2120
+ }
2121
+ })
2122
+ ]
2123
+ });
2124
+ }
2057
2125
  };
2058
2126
 
2059
2127
  // src/StaticTVClient.ts
@@ -2083,6 +2151,19 @@ var StaticTVClient = class {
2083
2151
  constructor(config) {
2084
2152
  this._keyType = null;
2085
2153
  this._disabled = false;
2154
+ this._fullFeaturesEnabled = false;
2155
+ /** Guide module - fetch channel lineup (full SDK only) */
2156
+ this.guide = null;
2157
+ /** Session module - track visitor sessions (all keys, null when disabled) */
2158
+ this.session = null;
2159
+ /** Heartbeat module - track video watching (full SDK only) */
2160
+ this.heartbeat = null;
2161
+ /** Interactions module - like/follow channels (full SDK only) */
2162
+ this.interactions = null;
2163
+ /** Guide UI module - channel browser UI (full SDK only) */
2164
+ this.guideUI = null;
2165
+ /** Chat UI module - real-time chat UI (full SDK only) */
2166
+ this.chatUI = null;
2086
2167
  this.config = {
2087
2168
  autoStartSession: true,
2088
2169
  sessionHeartbeatInterval: 3e4,
@@ -2111,27 +2192,11 @@ var StaticTVClient = class {
2111
2192
  console.log("[StaticTV] Invalid apiKey format (must start with dclk_ or dcls_) - tracking disabled. Scene will load normally.");
2112
2193
  this._disabled = true;
2113
2194
  this._keyType = null;
2114
- this.session = null;
2115
- this.guide = null;
2116
- this.heartbeat = null;
2117
- this.interactions = null;
2118
- this.guideUI = null;
2119
- this.chatUI = null;
2120
2195
  return;
2121
2196
  }
2122
2197
  this.session = new SessionModule(this);
2123
2198
  if (this._keyType === KEY_TYPE_CHANNEL) {
2124
- this.guide = new GuideModule(this);
2125
- this.heartbeat = new HeartbeatModule(this);
2126
- this.interactions = new InteractionsModule(this);
2127
- this.guideUI = new GuideUIModule(this, config.guideUI);
2128
- this.chatUI = new ChatUIModule(this, config.chatUI);
2129
- } else {
2130
- this.guide = null;
2131
- this.heartbeat = null;
2132
- this.interactions = null;
2133
- this.guideUI = null;
2134
- this.chatUI = null;
2199
+ this._initFullModules();
2135
2200
  }
2136
2201
  if (this.config.autoStartSession) {
2137
2202
  fetchUserData().then(() => {
@@ -2160,10 +2225,11 @@ var StaticTVClient = class {
2160
2225
  return this._disabled;
2161
2226
  }
2162
2227
  /**
2163
- * Check if this is a lite (scene-only) client
2228
+ * Check if this is a lite client (no full features)
2229
+ * Returns true until session confirms sdkType is 'full'
2164
2230
  */
2165
2231
  get isLite() {
2166
- return this._keyType === KEY_TYPE_SCENE;
2232
+ return !this._fullFeaturesEnabled;
2167
2233
  }
2168
2234
  /**
2169
2235
  * Make an authenticated API request
@@ -2196,6 +2262,40 @@ var StaticTVClient = class {
2196
2262
  getConfig() {
2197
2263
  return this.config;
2198
2264
  }
2265
+ /**
2266
+ * Initialize full feature modules (guide, heartbeat, interactions, UI)
2267
+ * @internal
2268
+ */
2269
+ _initFullModules() {
2270
+ if (this._fullFeaturesEnabled) return;
2271
+ this.guide = new GuideModule(this);
2272
+ this.heartbeat = new HeartbeatModule(this);
2273
+ this.interactions = new InteractionsModule(this);
2274
+ this.guideUI = new GuideUIModule(this, this.config.guideUI);
2275
+ this.chatUI = new ChatUIModule(this, this.config.chatUI);
2276
+ this._fullFeaturesEnabled = true;
2277
+ this.log("Full features enabled (guide, chat, heartbeat, interactions)");
2278
+ }
2279
+ /**
2280
+ * Called by SessionModule when server confirms sdkType is 'full'
2281
+ * Enables guide, chat, heartbeat, and interactions modules
2282
+ * @internal
2283
+ */
2284
+ _enableFullFeatures() {
2285
+ this._initFullModules();
2286
+ }
2287
+ /**
2288
+ * Check if full features are enabled (server confirmed sdkType: 'full')
2289
+ */
2290
+ get hasFullFeatures() {
2291
+ return this._fullFeaturesEnabled;
2292
+ }
2293
+ /**
2294
+ * Get the SDK type (lite or full) - only available after session starts
2295
+ */
2296
+ get sdkType() {
2297
+ return this.session?.sdkType || "lite";
2298
+ }
2199
2299
  /**
2200
2300
  * Cleanup when done (call before scene unload)
2201
2301
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thestatic-tv/dcl-sdk",
3
- "version": "2.0.3",
3
+ "version": "2.2.0",
4
4
  "description": "Connect your Decentraland scene to thestatic.tv - full channel lineup, metrics tracking, and interactions",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",