@touchcastllc/napster-companion-api 1.0.0-alpha.34 → 1.0.0-alpha.36

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
@@ -20,6 +20,7 @@
20
20
  - **Zero External Dependencies:** Drop in a script tag and go
21
21
  - **TypeScript Support:** Fully typed APIs
22
22
  - **Tree-Shakeable:** Import only what you need
23
+ - **Screen Sharing:** Let the avatar see your screen in real time
23
24
  - **Production Ready:** Minified builds with type definitions
24
25
 
25
26
  ---
@@ -377,6 +378,7 @@ async function initAdvancedAvatar() {
377
378
  features: {
378
379
  inactiveTimeout: { enabled: true, duration: 120000, countdown: 15 },
379
380
  showSDKLoader: { enabled: true, bgColor: "#f0f0f0" },
381
+ screenShare: { enabled: true },
380
382
  },
381
383
 
382
384
  style: {
@@ -523,7 +525,149 @@ const instance = await NapsterCompanionApiSdk.init(token, {
523
525
  });
524
526
  ```
525
527
 
526
- ### 3.2 Position & Layout
528
+ ### 3.2 Face Tracking
529
+
530
+ Real-time face detection using TensorFlow.js + MediaPipe Face Mesh. Detects the user's face via their camera, extracts 468 facial landmarks, and determines talking state via mouth-opening ratio. Fully client-side — no backend involved.
531
+
532
+ #### Prerequisites
533
+
534
+ Install the optional peer dependencies (only needed when face tracking is enabled):
535
+
536
+ ```bash
537
+ npm install @tensorflow/tfjs @tensorflow-models/face-landmarks-detection
538
+ ```
539
+
540
+ These libraries (~2MB) are lazy-loaded at runtime — they are not included in the main SDK bundle.
541
+
542
+ #### Enable & Configure
543
+
544
+ ```typescript
545
+ const instance = await NapsterCompanionApiSdk.init(token, {
546
+ features: {
547
+ faceTracking: {
548
+ enabled: true,
549
+ fps: 15, // Detection rate: 1–30 FPS (default: 15)
550
+ talkingThreshold: 0.015, // Mouth opening ratio threshold (default: 0.015)
551
+ },
552
+ },
553
+ onFaceTrackingData: (data) => {
554
+ if (data) {
555
+ console.log("Talking:", data.isTalking);
556
+ console.log("Landmarks:", data.landmarks.length); // 468
557
+ console.log("Mouth:", data.mouthLandmarks);
558
+ } else {
559
+ console.log("No face detected");
560
+ }
561
+ },
562
+ onFaceTrackingError: (error) => {
563
+ console.error("Face tracking error:", error.message);
564
+ },
565
+ });
566
+ ```
567
+
568
+ #### Start & Stop
569
+
570
+ ```typescript
571
+ // Start detection (opens camera, loads model on first call)
572
+ await instance.startFaceTracking();
573
+
574
+ // Pause detection (stops camera, keeps model loaded for fast restart)
575
+ instance.stopFaceTracking();
576
+
577
+ // Restart — reuses model, only reopens camera
578
+ await instance.startFaceTracking();
579
+ ```
580
+
581
+ #### Face Tracking Data
582
+
583
+ Each detection frame emits a `FaceTrackingData` object (or `null` if no face is detected):
584
+
585
+ ```typescript
586
+ interface FaceTrackingData {
587
+ landmarks: Array<{ x: number; y: number; z: number }>; // 468 points, 0–1 normalized
588
+ mouthLandmarks: {
589
+ topLip: { x: number; y: number };
590
+ bottomLip: { x: number; y: number };
591
+ leftCorner: { x: number; y: number };
592
+ rightCorner: { x: number; y: number };
593
+ } | null;
594
+ isTalking: boolean; // smoothed mouth-opening ratio > threshold
595
+ }
596
+ ```
597
+
598
+ #### Error Codes
599
+
600
+ | Code | When |
601
+ | -------------------------- | ----------------------------------------- |
602
+ | `CAMERA_PERMISSION_DENIED` | User denied camera access |
603
+ | `CAMERA_NOT_FOUND` | No camera available |
604
+ | `MODEL_LOAD_FAILED` | TensorFlow.js / MediaPipe failed to load |
605
+ | `DETECTION_FAILED` | Runtime detection error |
606
+
607
+ #### Cleanup
608
+
609
+ Face tracking is automatically destroyed when `instance.destroy()` is called. The model is disposed and camera tracks are stopped.
610
+
611
+ ### 3.3 Screen Sharing
612
+
613
+ Share the user's screen with the avatar in real time. The SDK captures the display at 1 FPS, renders each frame onto an internal canvas, and streams the canvas track over WebRTC so the avatar can "see" what the user sees. Fully browser-based — no plugins or extensions required.
614
+
615
+ #### Enable & Configure
616
+
617
+ ```typescript
618
+ const instance = await NapsterCompanionApiSdk.init(token, {
619
+ features: {
620
+ screenShare: {
621
+ enabled: true,
622
+ },
623
+ },
624
+ });
625
+ ```
626
+
627
+ > **Note:** `features.screenShare.enabled` must be `true` for the screen share controls to appear and for `isScreenShareSupported` to return `true`.
628
+
629
+ #### Start & Stop
630
+
631
+ ```typescript
632
+ // Start sharing (opens browser's screen picker)
633
+ await instance.startScreenShare();
634
+
635
+ // Stop sharing
636
+ instance.stopScreenShare();
637
+
638
+ // Or toggle on/off
639
+ await instance.toggleScreenShare();
640
+ ```
641
+
642
+ The SDK sends `start_video` / `stop_video` commands to the avatar server automatically. If the user stops sharing via the browser's native "Stop sharing" button, the SDK detects this and cleans up.
643
+
644
+ #### Checking State
645
+
646
+ ```typescript
647
+ // Whether screen sharing is currently active
648
+ instance.isScreenSharing; // boolean
649
+
650
+ // Whether the browser supports screen sharing AND the feature is enabled
651
+ instance.isScreenShareSupported; // boolean
652
+ ```
653
+
654
+ #### How It Works
655
+
656
+ 1. **`getDisplayMedia`** captures the user's screen at max 1 FPS, 1280×720.
657
+ 2. **`MediaCapture`** renders each frame onto a hidden `<canvas>` via a Web Worker timer.
658
+ 3. **`canvas.captureStream(1)`** produces a video track that is added to the WebRTC peer connection at setup time.
659
+ 4. When the user starts sharing, frames begin flowing on the already-connected track and the SDK sends `start_video` through the data channel.
660
+ 5. When the user stops sharing, the display stream tracks are stopped and `stop_video` is sent.
661
+
662
+ #### Cleanup
663
+
664
+ Screen sharing is automatically stopped when:
665
+
666
+ - The user calls `instance.destroy()`
667
+ - The WebRTC connection closes (network failure, session end)
668
+ - The user clicks the browser's native "Stop sharing" button
669
+
670
+ ### 3.4 Position & Layout
527
671
 
528
672
  Customize the avatar's position on the screen using predefined positions or custom styles. Also you can override the position using style properties.
529
673
 
@@ -685,7 +829,75 @@ if (instance.isFeatureEnabled("disclaimer")) {
685
829
  }
686
830
  ```
687
831
 
688
- ### 5.4 Communication Methods
832
+ ### 5.4 Face Tracking Methods
833
+
834
+ #### `startFaceTracking(): Promise<void>`
835
+
836
+ Start face tracking. Opens the user's camera and begins the detection loop. The model is loaded on the first call and reused on subsequent calls.
837
+
838
+ ```typescript
839
+ await instance.startFaceTracking();
840
+ ```
841
+
842
+ > **Note:** Requires `features.faceTracking.enabled = true` in the init config. Throws a `FeatureError` if not enabled.
843
+
844
+ #### `stopFaceTracking(): void`
845
+
846
+ Pause face tracking. Stops the camera and detection loop but keeps the model loaded for fast restart.
847
+
848
+ ```typescript
849
+ instance.stopFaceTracking();
850
+ ```
851
+
852
+ ### 5.5 Screen Sharing Methods
853
+
854
+ #### `startScreenShare(): Promise<void>`
855
+
856
+ Start screen sharing. Opens the browser's screen picker and begins streaming frames to the avatar.
857
+
858
+ ```typescript
859
+ await instance.startScreenShare();
860
+ ```
861
+
862
+ > **Note:** Requires `features.screenShare.enabled = true` in the init config. Does nothing if already sharing.
863
+
864
+ #### `stopScreenShare(): void`
865
+
866
+ Stop screen sharing. Releases the display stream and notifies the server.
867
+
868
+ ```typescript
869
+ instance.stopScreenShare();
870
+ ```
871
+
872
+ #### `toggleScreenShare(): Promise<void>`
873
+
874
+ Toggle screen sharing on or off.
875
+
876
+ ```typescript
877
+ await instance.toggleScreenShare();
878
+ ```
879
+
880
+ #### `isScreenSharing: boolean` _(read-only)_
881
+
882
+ Whether screen sharing is currently active.
883
+
884
+ ```typescript
885
+ if (instance.isScreenSharing) {
886
+ console.log("User is sharing their screen");
887
+ }
888
+ ```
889
+
890
+ #### `isScreenShareSupported: boolean` _(read-only)_
891
+
892
+ Whether screen sharing is supported by the browser **and** enabled in the feature config.
893
+
894
+ ```typescript
895
+ if (instance.isScreenShareSupported) {
896
+ showScreenShareButton();
897
+ }
898
+ ```
899
+
900
+ ### 5.6 Communication Methods
689
901
 
690
902
  #### `sendCommand(command: { type: string; data?: object }): void`
691
903
 
@@ -736,6 +948,14 @@ interface NapsterCompanionApiConfig {
736
948
  };
737
949
  disclaimer?: { enabled: boolean; text?: string };
738
950
  showSDKLoader?: { enabled: boolean; bgColor?: string };
951
+ screenShare?: {
952
+ enabled: boolean;
953
+ };
954
+ faceTracking?: {
955
+ enabled: boolean;
956
+ fps?: number; // 1–30, default 15
957
+ talkingThreshold?: number; // default 0.015
958
+ };
739
959
  };
740
960
 
741
961
  // Configuration
@@ -749,6 +969,8 @@ interface NapsterCompanionApiConfig {
749
969
  onInactivityStatusChange?: (isInactive: boolean) => void;
750
970
  onDestroy?: () => void;
751
971
  onFeaturesUpdate?: (features: FeatureConfig) => void;
972
+ onFaceTrackingData?: (data: FaceTrackingData | null) => void;
973
+ onFaceTrackingError?: (error: Error) => void;
752
974
  }
753
975
  ```
754
976
 
@@ -767,6 +989,9 @@ npm install @touchcastllc/napster-companion-api
767
989
 
768
990
  # Install peer dependencies
769
991
  npm install @reduxjs/toolkit
992
+
993
+ # If using face tracking, also install:
994
+ npm install @tensorflow/tfjs @tensorflow-models/face-landmarks-detection
770
995
  ```
771
996
 
772
997
  **Problem: Invalid or expired token**
@@ -10,6 +10,9 @@ export interface AvatarOptions {
10
10
  onError?: (error: Error) => void;
11
11
  onInactivityStatusChange?: (isInactive: boolean) => void;
12
12
  features?: Partial<FeatureConfig>;
13
+ onScreenShareToggle?: () => void;
14
+ isScreenSharing?: boolean;
15
+ isScreenShareSupported?: boolean;
13
16
  }
14
17
  export interface StreamsUpdate {
15
18
  videoStream?: MediaStream | null;
@@ -22,6 +25,7 @@ export interface AvatarController extends BaseController<AvatarOptions> {
22
25
  updateFeatures: (features: Partial<FeatureConfig>) => void;
23
26
  setAvatarReady: (ready: boolean) => void;
24
27
  handleMessage: (data: EventMessage) => void;
28
+ updateScreenShareState: (isSharing: boolean) => void;
25
29
  }
26
30
  /**
27
31
  * Vanilla Avatar implementation.
@@ -10,10 +10,14 @@ export interface EmbedOptions {
10
10
  onError?: (error: Error) => void;
11
11
  onInactivityStatusChange?: (isInactive: boolean) => void;
12
12
  features?: Partial<FeatureConfig>;
13
+ onScreenShareToggle?: () => void;
14
+ isScreenSharing?: boolean;
15
+ isScreenShareSupported?: boolean;
13
16
  }
14
17
  export interface EmbedController extends BaseController<EmbedOptions> {
15
18
  updateStreams: (streams: StreamsUpdate) => void;
16
19
  handleMessage: (data: EventMessage) => void;
20
+ updateScreenShareState: (isSharing: boolean) => void;
17
21
  }
18
22
  /**
19
23
  * Vanilla Embed implementation that renders a simple live container with Avatar
@@ -11,6 +11,9 @@ export interface WaveFormOptions {
11
11
  onStopTalking?: () => void;
12
12
  features?: Partial<NapsterCompanionApiConfig["features"]>;
13
13
  onVolumeChange?: (volume: number) => void;
14
+ onScreenShareToggle?: () => void;
15
+ isScreenSharing?: boolean;
16
+ isScreenShareSupported?: boolean;
14
17
  }
15
18
  export type WaveFormController = BaseController<WaveFormOptions>;
16
19
  export declare function createWaveForm(mount: HTMLElement, options: WaveFormOptions): WaveFormController;
@@ -46,5 +46,7 @@ export declare enum WebSocketMessageType {
46
46
  export declare enum DataChannelMessageType {
47
47
  SEND_MESSAGE = "send_message",
48
48
  CANCEL = "cancel",
49
- SET_SETTINGS = "set_settings"
49
+ SET_SETTINGS = "set_settings",
50
+ START_VIDEO = "start_video",
51
+ STOP_VIDEO = "stop_video"
50
52
  }
package/lib/index.d.ts CHANGED
@@ -11,11 +11,15 @@ declare class NapsterCompanionApiSdkVanilla implements NapsterCompanionApiSDK {
11
11
  private rootEl;
12
12
  private embed;
13
13
  private webrtcController;
14
+ private screenShareController;
14
15
  private faceTrackingController;
15
16
  private config;
16
17
  private isInitialized;
18
+ private unloadHandler;
17
19
  static getInstance(): NapsterCompanionApiSdkVanilla;
20
+ constructor();
18
21
  private renderApp;
22
+ private handleScreenShareToggle;
19
23
  private initializeWebRTC;
20
24
  private destroyWebRTC;
21
25
  private destroyInstance;