@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/dist/index.d.mts CHANGED
@@ -1,3 +1,15 @@
1
+ import ReactEcs from '@dcl/sdk/react-ecs';
2
+
3
+ /**
4
+ * JSX namespace declaration for DTS generation compatibility.
5
+ * React-ECS uses custom JSX factory, this ensures types compile.
6
+ */
7
+ declare global {
8
+ namespace JSX {
9
+ interface Element {
10
+ }
11
+ }
12
+ }
1
13
  /**
2
14
  * Player identity data from DCL
3
15
  */
@@ -85,14 +97,22 @@ interface GuideResponse {
85
97
  vodCount: number;
86
98
  };
87
99
  }
100
+ /**
101
+ * SDK tier levels - determines available features
102
+ * - free: Session tracking only (visitor metrics)
103
+ * - standard: Guide, Chat, Heartbeat, Interactions + session tracking
104
+ * - pro: Everything in Standard + Admin Panel
105
+ */
106
+ type SDKTier = 'free' | 'standard' | 'pro';
88
107
  /**
89
108
  * Session response from the API
90
109
  */
91
110
  interface SessionResponse {
92
111
  success: boolean;
93
112
  sessionId?: string;
113
+ keyId?: string;
94
114
  keyType?: 'scene' | 'channel';
95
- sdkType?: 'lite' | 'full';
115
+ sdkType?: SDKTier | 'lite' | 'full';
96
116
  version: string;
97
117
  }
98
118
  /**
@@ -187,7 +207,10 @@ interface ChatMessage {
187
207
  username: string;
188
208
  text: string;
189
209
  uid: string;
190
- createdAt: any;
210
+ createdAt: Date | {
211
+ seconds: number;
212
+ nanoseconds: number;
213
+ } | string | number;
191
214
  isSystem?: boolean;
192
215
  }
193
216
  /**
@@ -203,6 +226,117 @@ interface ChatUIConfig {
203
226
  */
204
227
  fontScale?: number;
205
228
  }
229
+ /**
230
+ * Stream channel data from the API
231
+ */
232
+ interface StreamData {
233
+ hasChannel: boolean;
234
+ channelId: string | null;
235
+ channelName?: string;
236
+ isLive: boolean;
237
+ hlsUrl: string | null;
238
+ rtmpUrl?: string | null;
239
+ streamKey?: string | null;
240
+ currentViewers: number;
241
+ sparksBalance: number;
242
+ tier?: string;
243
+ /** True if user can claim the one-time streaming trial */
244
+ trialAvailable?: boolean;
245
+ /** Number of Sparks included in trial (240 = 4 hours) */
246
+ trialSparks?: number;
247
+ }
248
+ /**
249
+ * Video slot configuration
250
+ */
251
+ interface VideoSlot {
252
+ id: string;
253
+ name: string;
254
+ url: string;
255
+ }
256
+ /**
257
+ * Command handler for scene-specific commands
258
+ */
259
+ type CommandHandler = (type: string, payload?: Record<string, unknown>) => void;
260
+ /**
261
+ * Custom scene tab definition for Pro tier
262
+ */
263
+ interface SceneTabDefinition {
264
+ /** Tab label shown in the tab bar */
265
+ label: string;
266
+ /** Unique tab ID */
267
+ id: string;
268
+ /** React-ECS component that renders the tab content */
269
+ render: () => JSX.Element | null;
270
+ }
271
+ /**
272
+ * Configuration options for AdminPanelModule
273
+ */
274
+ interface AdminPanelConfig {
275
+ /**
276
+ * Scene ID for API calls.
277
+ * Optional - defaults to API key ID if not provided.
278
+ */
279
+ sceneId?: string;
280
+ /**
281
+ * Panel title (shown in header)
282
+ */
283
+ title?: string;
284
+ /**
285
+ * Header background color (default: red)
286
+ */
287
+ headerColor?: {
288
+ r: number;
289
+ g: number;
290
+ b: number;
291
+ a: number;
292
+ };
293
+ /**
294
+ * Show Video tab (default: true for Pro tier)
295
+ */
296
+ showVideoTab?: boolean;
297
+ /**
298
+ * Show Mod tab (default: true for owners)
299
+ */
300
+ showModTab?: boolean;
301
+ /**
302
+ * Custom scene tabs (Pro tier)
303
+ */
304
+ sceneTabs?: SceneTabDefinition[];
305
+ /**
306
+ * Command handler for scene-specific commands (e.g., elevator, lights)
307
+ * Called when admin triggers a command from the panel
308
+ */
309
+ onCommand?: CommandHandler;
310
+ /**
311
+ * Callback when video URL should be played
312
+ */
313
+ onVideoPlay?: (url: string) => void;
314
+ /**
315
+ * Callback when video playback should stop
316
+ */
317
+ onVideoStop?: () => void;
318
+ /**
319
+ * Callback when video slot is selected
320
+ */
321
+ onVideoSlotPlay?: (slot: string) => void;
322
+ /**
323
+ * Callback for broadcast messages
324
+ */
325
+ onBroadcast?: (text: string) => void;
326
+ /**
327
+ * Link shown in footer (default: scene page on thestatic.tv)
328
+ */
329
+ footerLink?: string;
330
+ /**
331
+ * Enable debug logging
332
+ */
333
+ debug?: boolean;
334
+ /**
335
+ * Force admin access (for testing without server endpoint)
336
+ * WARNING: Only use during development!
337
+ */
338
+ forceAdmin?: boolean;
339
+ }
206
340
 
207
341
  /**
208
342
  * Guide module - fetch channel lineup from thestatic.tv
@@ -247,13 +381,24 @@ declare class GuideModule {
247
381
  declare class SessionModule {
248
382
  private client;
249
383
  private sessionId;
384
+ private _keyId;
250
385
  private heartbeatTimerId;
251
386
  private isActive;
252
- private _sdkType;
387
+ private _tier;
253
388
  constructor(client: StaticTVClient);
254
389
  /**
255
- * Get the SDK type returned by the server
256
- * Determines what features are available (lite = sessions only, full = guide + chat + more)
390
+ * Get the API key ID (used as default sceneId for Pro users)
391
+ */
392
+ get keyId(): string | null;
393
+ /**
394
+ * Get the SDK tier returned by the server
395
+ * - free: Session tracking only
396
+ * - standard: Guide, Chat, Heartbeat, Interactions
397
+ * - pro: Everything + Admin Panel
398
+ */
399
+ get tier(): SDKTier;
400
+ /**
401
+ * @deprecated Use `tier` instead. Returns mapped value for compatibility.
257
402
  */
258
403
  get sdkType(): 'lite' | 'full';
259
404
  /**
@@ -379,7 +524,6 @@ declare class GuideUIModule {
379
524
  private currentPage;
380
525
  private itemsPerPage;
381
526
  private searchQuery;
382
- private uiScale;
383
527
  private _currentVideoId;
384
528
  constructor(client: StaticTVClient, config?: GuideUIConfig);
385
529
  /**
@@ -429,7 +573,7 @@ declare class GuideUIModule {
429
573
  private handleChannelClick;
430
574
  /**
431
575
  * Get the guide UI component for rendering
432
- * Returns toggle button when hidden, full panel when visible
576
+ * Always renders toggle button, plus panel when visible
433
577
  */
434
578
  getComponent: () => any;
435
579
  private renderLeftPanel;
@@ -463,7 +607,6 @@ declare class ChatUIModule {
463
607
  private inputText;
464
608
  private chatScrollOffset;
465
609
  private position;
466
- private fontScale;
467
610
  private chatTimerId;
468
611
  private playerInfoTimerId;
469
612
  private isInitialized;
@@ -517,9 +660,11 @@ declare class ChatUIModule {
517
660
  private resetChatTimer;
518
661
  private parseServerTime;
519
662
  private formatTime;
663
+ /** Scale a dimension by shared uiScale */
664
+ private s;
520
665
  /**
521
666
  * Get the chat UI component for rendering
522
- * Returns toggle button when hidden, full panel when visible
667
+ * Always renders toggle button, plus panel when visible
523
668
  */
524
669
  getComponent: () => any;
525
670
  private getPositionStyle;
@@ -534,12 +679,129 @@ declare class ChatUIModule {
534
679
  private renderToggleButton;
535
680
  }
536
681
 
682
+ /**
683
+ * Admin Panel UI Module for The Static TV SDK (Pro Tier)
684
+ *
685
+ * Provides in-scene admin controls:
686
+ * - Video Tab: Stream control, video playback, slots
687
+ * - Mod Tab: Scene admins, banned wallets, broadcast
688
+ * - Custom Scene Tabs: Pro tier
689
+ */
690
+
691
+ declare class AdminPanelUIModule {
692
+ private client;
693
+ private config;
694
+ private baseUrl;
695
+ private isAdmin;
696
+ private isOwner;
697
+ private panelOpen;
698
+ private activeTab;
699
+ private playerWallet;
700
+ private customVideoUrl;
701
+ private streamData;
702
+ private streamFetched;
703
+ private videoState;
704
+ private videoStateFetched;
705
+ private channelCreating;
706
+ private channelCreateError;
707
+ private channelDeleting;
708
+ private channelDeleteError;
709
+ private keyRotating;
710
+ private keyRotateStatus;
711
+ private streamControlling;
712
+ private streamControlStatus;
713
+ private pollIntervalId;
714
+ private trialClaiming;
715
+ private trialClaimError;
716
+ private sceneAdmins;
717
+ private bannedWallets;
718
+ private newAdminWallet;
719
+ private newBanWallet;
720
+ private broadcastText;
721
+ private modStatus;
722
+ private modsFetched;
723
+ constructor(client: StaticTVClient, config: AdminPanelConfig);
724
+ private log;
725
+ /** Scale a dimension by shared uiScale */
726
+ private s;
727
+ /** Get scaled theme */
728
+ private get theme();
729
+ /**
730
+ * Initialize the admin panel - checks admin status and fetches video state
731
+ */
732
+ init(): Promise<void>;
733
+ /**
734
+ * Check if current player is an admin for this scene
735
+ */
736
+ checkAdminStatus(): Promise<void>;
737
+ /**
738
+ * Toggle the admin panel open/closed
739
+ */
740
+ toggle(): void;
741
+ /**
742
+ * Show the admin panel
743
+ */
744
+ show(): void;
745
+ /**
746
+ * Hide the admin panel
747
+ */
748
+ hide(): void;
749
+ /**
750
+ * Check if the panel is currently open
751
+ */
752
+ get isOpen(): boolean;
753
+ /**
754
+ * Check if current user has admin access
755
+ */
756
+ get hasAccess(): boolean;
757
+ /**
758
+ * Register a custom scene tab (Pro tier)
759
+ */
760
+ registerSceneTab(tab: SceneTabDefinition): void;
761
+ private startStreamPolling;
762
+ private stopStreamPolling;
763
+ private fetchStreamData;
764
+ private refreshStreamStatus;
765
+ private fetchVideoState;
766
+ /**
767
+ * Play a video slot by ID - looks up URL and calls onVideoPlay
768
+ */
769
+ playSlot(slotId: string): void;
770
+ /**
771
+ * Auto-play the default slot if configured
772
+ */
773
+ private autoPlayDefault;
774
+ private createChannel;
775
+ private claimTrial;
776
+ private deleteChannel;
777
+ private startStream;
778
+ private stopStream;
779
+ private rotateStreamKey;
780
+ private fetchModData;
781
+ private addSceneAdmin;
782
+ private removeSceneAdmin;
783
+ private banWallet;
784
+ private unbanWallet;
785
+ private banKickPlayer;
786
+ private sendBroadcast;
787
+ private setActiveTab;
788
+ private SectionHead;
789
+ private TabBtn;
790
+ private VideoTab;
791
+ private ModTab;
792
+ /**
793
+ * Get the React-ECS component for the admin panel
794
+ */
795
+ getComponent: () => ReactEcs.JSX.Element | null;
796
+ }
797
+
537
798
  /**
538
799
  * StaticTVClient - Main client for connecting DCL scenes to thestatic.tv
539
800
  *
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
801
+ * All keys use dcls_ prefix. Features enabled based on tier from server:
802
+ * - free: Session tracking only (visitor metrics)
803
+ * - standard: Guide, Chat, Heartbeat, Interactions + session tracking
804
+ * - pro: Everything in Standard + Admin Panel (Video + Mod tabs)
543
805
  */
544
806
 
545
807
  /** Key type constants (legacy - kept for compatibility) */
@@ -549,20 +811,30 @@ declare class StaticTVClient {
549
811
  private config;
550
812
  private baseUrl;
551
813
  private _keyType;
814
+ private _keyId;
552
815
  private _disabled;
553
- private _fullFeaturesEnabled;
554
- /** Guide module - fetch channel lineup (full SDK only) */
816
+ private _tier;
817
+ private _standardFeaturesEnabled;
818
+ private _proFeaturesEnabled;
819
+ private _pendingProConfig;
820
+ /** Guide module - fetch channel lineup (standard/pro tier) */
555
821
  guide: GuideModule | null;
556
- /** Session module - track visitor sessions (all keys, null when disabled) */
822
+ /** Session module - track visitor sessions (all tiers, null when disabled) */
557
823
  session: SessionModule | null;
558
- /** Heartbeat module - track video watching (full SDK only) */
824
+ /** Heartbeat module - track video watching (standard/pro tier) */
559
825
  heartbeat: HeartbeatModule | null;
560
- /** Interactions module - like/follow channels (full SDK only) */
826
+ /** Interactions module - like/follow channels (standard/pro tier) */
561
827
  interactions: InteractionsModule | null;
562
- /** Guide UI module - channel browser UI (full SDK only) */
828
+ /** Guide UI module - channel browser UI (standard/pro tier) */
563
829
  guideUI: GuideUIModule | null;
564
- /** Chat UI module - real-time chat UI (full SDK only) */
830
+ /** Chat UI module - real-time chat UI (standard/pro tier) */
565
831
  chatUI: ChatUIModule | null;
832
+ /** Admin Panel module - Video/Mod tabs (pro tier only) */
833
+ adminPanel: AdminPanelUIModule | null;
834
+ /** UI scale - fixed at 1.0. DCL's UI system auto-scales based on viewport. */
835
+ readonly uiScale: number;
836
+ /** Get the API base URL (for internal module use) */
837
+ getBaseUrl(): string;
566
838
  /**
567
839
  * Create a new StaticTVClient
568
840
  *
@@ -570,16 +842,15 @@ declare class StaticTVClient {
570
842
  *
571
843
  * @example
572
844
  * ```typescript
573
- * // Full access with channel key
574
- * const staticTV = new StaticTVClient({
575
- * apiKey: 'dclk_your_channel_key_here',
576
- * debug: true
577
- * });
845
+ * let staticTV: StaticTVClient
578
846
  *
579
- * // Lite mode with scene key (visitors only)
580
- * const staticTV = new StaticTVClient({
581
- * apiKey: 'dcls_your_scene_key_here'
582
- * });
847
+ * export function main() {
848
+ * // All keys use dcls_ prefix - features determined by subscription
849
+ * staticTV = new StaticTVClient({
850
+ * apiKey: 'dcls_your_key_here'
851
+ * })
852
+ * // Session tracking starts automatically!
853
+ * }
583
854
  * ```
584
855
  */
585
856
  constructor(config: StaticTVConfig);
@@ -592,8 +863,16 @@ declare class StaticTVClient {
592
863
  */
593
864
  get isDisabled(): boolean;
594
865
  /**
595
- * Check if this is a lite client (no full features)
596
- * Returns true until session confirms sdkType is 'full'
866
+ * Get the current SDK tier (free, standard, or pro)
867
+ */
868
+ get tier(): SDKTier;
869
+ /**
870
+ * Check if this is a free tier client (session tracking only)
871
+ * Returns true until session confirms a higher tier
872
+ */
873
+ get isFree(): boolean;
874
+ /**
875
+ * @deprecated Use `isFree` instead. Kept for backward compatibility.
597
876
  */
598
877
  get isLite(): boolean;
599
878
  /**
@@ -602,40 +881,173 @@ declare class StaticTVClient {
602
881
  */
603
882
  request<T>(endpoint: string, options?: RequestInit): Promise<T>;
604
883
  /**
605
- * Log a message if debug is enabled
884
+ * Log a debug message (only when debug: true)
606
885
  * @internal
607
886
  */
608
887
  log(message: string, ...args: unknown[]): void;
888
+ /**
889
+ * Log a warning (always shown)
890
+ * @internal
891
+ */
892
+ warn(message: string): void;
893
+ /**
894
+ * Log an error (always shown, user-friendly format)
895
+ * @internal
896
+ */
897
+ error(message: string, err?: unknown): void;
609
898
  /**
610
899
  * Get the current configuration
611
900
  * @internal
612
901
  */
613
902
  getConfig(): StaticTVConfig;
614
903
  /**
615
- * Initialize full feature modules (guide, heartbeat, interactions, UI)
904
+ * Initialize standard feature modules (guide, heartbeat, interactions, UI)
905
+ * @internal
906
+ */
907
+ private _initStandardModules;
908
+ /**
909
+ * Initialize pro feature modules (admin panel)
616
910
  * @internal
617
911
  */
618
- private _initFullModules;
912
+ private _initProModules;
619
913
  /**
620
- * Called by SessionModule when server confirms sdkType is 'full'
621
- * Enables guide, chat, heartbeat, and interactions modules
914
+ * Called by SessionModule when server returns the tier
915
+ * Enables modules based on tier level
916
+ * @internal
917
+ */
918
+ _enableFeaturesForTier(tier: SDKTier, keyId?: string | null): void;
919
+ /**
920
+ * @deprecated Use `_enableFeaturesForTier` instead
622
921
  * @internal
623
922
  */
624
923
  _enableFullFeatures(): void;
625
924
  /**
626
- * Check if full features are enabled (server confirmed sdkType: 'full')
925
+ * Check if standard features are enabled (guide, chat, etc.)
926
+ */
927
+ get hasStandardFeatures(): boolean;
928
+ /**
929
+ * @deprecated Use `hasStandardFeatures` instead
627
930
  */
628
931
  get hasFullFeatures(): boolean;
629
932
  /**
630
- * Get the SDK type (lite or full) - only available after session starts
933
+ * Check if pro features are enabled (admin panel)
934
+ */
935
+ get hasProFeatures(): boolean;
936
+ /**
937
+ * @deprecated Use `tier` instead
631
938
  */
632
939
  get sdkType(): 'lite' | 'full';
940
+ /**
941
+ * Configure Pro features (Admin Panel with Video + Mod tabs)
942
+ * Call this after creating the client to configure admin panel.
943
+ * The panel will auto-enable when server confirms Pro tier.
944
+ *
945
+ * @param config Admin panel configuration (optional - defaults work for basic usage)
946
+ *
947
+ * @example
948
+ * ```typescript
949
+ * const staticTV = new StaticTVClient({ apiKey: 'dcls_...' })
950
+ *
951
+ * // Simplest usage - just enable with callbacks:
952
+ * staticTV.enableProFeatures({
953
+ * onVideoPlay: (url) => videoPlayer.play(url),
954
+ * onVideoStop: () => videoPlayer.stop()
955
+ * })
956
+ *
957
+ * // Advanced usage - custom sceneId and title:
958
+ * staticTV.enableProFeatures({
959
+ * sceneId: 'my-scene', // optional - defaults to API key ID
960
+ * title: 'MY SCENE ADMIN',
961
+ * onVideoPlay: (url) => videoPlayer.play(url),
962
+ * onVideoStop: () => videoPlayer.stop(),
963
+ * onBroadcast: (text) => showNotification(text)
964
+ * })
965
+ * ```
966
+ */
967
+ enableProFeatures(config?: AdminPanelConfig): void;
968
+ /**
969
+ * Register a custom scene tab for the admin panel (Pro tier)
970
+ * Must call enableProFeatures() first.
971
+ *
972
+ * @param tab The tab definition with label, id, and render function
973
+ *
974
+ * @example
975
+ * ```typescript
976
+ * staticTV.registerSceneTab({
977
+ * label: 'LIGHTS',
978
+ * id: 'lights',
979
+ * render: () => <MyLightsControls />
980
+ * })
981
+ * ```
982
+ */
983
+ registerSceneTab(tab: SceneTabDefinition): void;
984
+ /**
985
+ * Close Admin/Guide panels (they share the same screen space)
986
+ * Chat is independent and stays open.
987
+ * @param except The panel that should stay open: 'admin' | 'guide'
988
+ */
989
+ closeOtherPanels(except: 'admin' | 'guide'): void;
990
+ /**
991
+ * Set up the UI renderer for all SDK panels
992
+ * Call this in your scene's main() function to render Guide, Chat, Admin panels.
993
+ * No need to create your own ui.tsx - the SDK handles everything.
994
+ *
995
+ * @example
996
+ * ```typescript
997
+ * const staticTV = new StaticTVClient({ apiKey: 'dcls_...' })
998
+ *
999
+ * export function main() {
1000
+ * staticTV.setupUI()
1001
+ * // That's it! All panels will render automatically
1002
+ * }
1003
+ * ```
1004
+ */
1005
+ setupUI(): void;
1006
+ /**
1007
+ * Show a notification message on screen
1008
+ * Works with both SDK-rendered UI and custom UI setups.
1009
+ *
1010
+ * @param message The message to display
1011
+ * @param durationMs How long to show (default 5000ms)
1012
+ *
1013
+ * @example
1014
+ * ```typescript
1015
+ * staticTV.showNotification('Stream started!')
1016
+ * staticTV.showNotification('Custom message', 10000) // 10 seconds
1017
+ * ```
1018
+ */
1019
+ showNotification(message: string, durationMs?: number): void;
633
1020
  /**
634
1021
  * Cleanup when done (call before scene unload)
635
1022
  */
636
1023
  destroy(): Promise<void>;
637
1024
  }
638
1025
 
1026
+ /**
1027
+ * UI Renderer - SDK-level UI component that renders all panels
1028
+ *
1029
+ * This allows scenes to use the SDK's UI without needing their own ui.tsx file.
1030
+ * Just call staticTV.setupUI() and all panels will render automatically.
1031
+ */
1032
+
1033
+ /**
1034
+ * Show a notification message on screen
1035
+ * @param message The message to display
1036
+ * @param durationMs How long to show (default 5000ms)
1037
+ */
1038
+ declare function showNotification(message: string, durationMs?: number): void;
1039
+ /**
1040
+ * Set up the UI renderer for a StaticTVClient
1041
+ * Call this in your scene's main() function after creating the client.
1042
+ *
1043
+ * @example
1044
+ * ```typescript
1045
+ * const staticTV = new StaticTVClient({ apiKey: 'dcls_...' })
1046
+ * setupStaticUI(staticTV)
1047
+ * ```
1048
+ */
1049
+ declare function setupStaticUI(client: StaticTVClient): void;
1050
+
639
1051
  /**
640
1052
  * Identity utilities for getting player information
641
1053
  * Uses getUserData from ~system/UserIdentity for reliable data in both
@@ -660,4 +1072,4 @@ declare function getPlayerWallet(): string | null;
660
1072
  */
661
1073
  declare function getPlayerDisplayName(): string | null;
662
1074
 
663
- export { type Channel, type ChatChannel, type ChatMessage, type ChatUIConfig, ChatUIModule, type GuideFeaturedPlaylist, GuideModule, type GuideResponse, type GuideUIConfig, GuideUIModule, type GuideVideo, HeartbeatModule, type HeartbeatResponse, type InteractionResponse, InteractionsModule, KEY_TYPE_CHANNEL, KEY_TYPE_SCENE, type PlayerData, type SceneStats, type SceneStatsResponse, SessionModule, type SessionResponse, StaticTVClient, type StaticTVConfig, type Vod, fetchUserData, getPlayerDisplayName, getPlayerWallet };
1075
+ export { type AdminPanelConfig, AdminPanelUIModule, type Channel, type ChatChannel, type ChatMessage, type ChatUIConfig, ChatUIModule, type CommandHandler, type GuideFeaturedPlaylist, GuideModule, type GuideResponse, type GuideUIConfig, GuideUIModule, type GuideVideo, HeartbeatModule, type HeartbeatResponse, type InteractionResponse, InteractionsModule, KEY_TYPE_CHANNEL, KEY_TYPE_SCENE, type PlayerData, type SDKTier, type SceneStats, type SceneStatsResponse, type SceneTabDefinition, SessionModule, type SessionResponse, StaticTVClient, type StaticTVConfig, type StreamData, type VideoSlot, type Vod, fetchUserData, getPlayerDisplayName, getPlayerWallet, setupStaticUI, showNotification };