@touchcastllc/napster-companion-api 1.0.0-alpha.8 → 1.0.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 +733 -531
- package/lib/CompanionApiProvider.d.ts +21 -0
- package/lib/CompanionApiProvider.d.ts.map +1 -0
- package/lib/assets/fonts/segoe/SegoeUI-Bold.eot +0 -0
- package/lib/assets/fonts/segoe/SegoeUI-Bold.svg +1541 -0
- package/lib/assets/fonts/segoe/SegoeUI-Bold.ttf +0 -0
- package/lib/assets/fonts/segoe/SegoeUI-Bold.woff +0 -0
- package/lib/assets/fonts/segoe/SegoeUI-Bold.woff2 +0 -0
- package/lib/assets/fonts/segoe/SegoeUI.eot +0 -0
- package/lib/assets/fonts/segoe/SegoeUI.svg +1521 -0
- package/lib/assets/fonts/segoe/SegoeUI.ttf +0 -0
- package/lib/assets/fonts/segoe/SegoeUI.woff +0 -0
- package/lib/assets/fonts/segoe/SegoeUI.woff2 +0 -0
- package/lib/components/Avatar/Avatar.d.ts +11 -0
- package/lib/components/Avatar/Avatar.d.ts.map +1 -0
- package/lib/components/Avatar/index.d.ts +2 -30
- package/lib/components/Avatar/index.d.ts.map +1 -0
- package/lib/components/Embed/Embed.d.ts +16 -0
- package/lib/components/Embed/Embed.d.ts.map +1 -0
- package/lib/components/Embed/index.d.ts +2 -20
- package/lib/components/Embed/index.d.ts.map +1 -0
- package/lib/components/InactiveOverlay/InactiveOverlay.d.ts +12 -0
- package/lib/components/InactiveOverlay/InactiveOverlay.d.ts.map +1 -0
- package/lib/components/InactiveOverlay/index.d.ts +2 -15
- package/lib/components/InactiveOverlay/index.d.ts.map +1 -0
- package/lib/components/Preview/Preview.d.ts +13 -0
- package/lib/components/Preview/Preview.d.ts.map +1 -0
- package/lib/components/Preview/index.d.ts +2 -18
- package/lib/components/Preview/index.d.ts.map +1 -0
- package/lib/components/StyledTooltip/StyledTooltip.d.ts +12 -0
- package/lib/components/StyledTooltip/StyledTooltip.d.ts.map +1 -0
- package/lib/components/StyledTooltip/index.d.ts +2 -16
- package/lib/components/StyledTooltip/index.d.ts.map +1 -0
- package/lib/components/WaveForm/WaveForm.d.ts +16 -0
- package/lib/components/WaveForm/WaveForm.d.ts.map +1 -0
- package/lib/components/WaveForm/index.d.ts +2 -18
- package/lib/components/WaveForm/index.d.ts.map +1 -0
- package/lib/components/index.d.ts +7 -2
- package/lib/components/index.d.ts.map +1 -0
- package/lib/hooks/index.d.ts +5 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/useApp.d.ts +25 -0
- package/lib/hooks/useApp.d.ts.map +1 -0
- package/lib/hooks/useAvatar.d.ts +88 -0
- package/lib/hooks/useAvatar.d.ts.map +1 -0
- package/lib/hooks/useMicrophones.d.ts +8 -0
- package/lib/hooks/useMicrophones.d.ts.map +1 -0
- package/lib/hooks/useWebRTC.d.ts +22 -0
- package/lib/hooks/useWebRTC.d.ts.map +1 -0
- package/lib/index-legacy.d.ts +27 -0
- package/lib/index-legacy.d.ts.map +1 -0
- package/lib/index-legacy.esm.js +2 -0
- package/lib/index-legacy.esm.js.map +1 -0
- package/lib/index-legacy.js +2 -0
- package/lib/index-legacy.js.map +1 -0
- package/lib/index.css +1 -1
- package/lib/index.d.ts +14 -19
- package/lib/index.d.ts.map +1 -0
- package/lib/index.esm.js +2 -1
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -0
- package/lib/index.standalone.js +22599 -1
- package/lib/index.standalone.js.map +1 -0
- package/lib/index.standalone.min.js +11 -0
- package/lib/index.standalone.min.js.map +1 -0
- package/lib/index.umd.js +19928 -0
- package/lib/index.umd.js.map +1 -0
- package/lib/index.umd.min.js +11 -0
- package/lib/index.umd.min.js.map +1 -0
- package/lib/sdk-base-B2ct7PnW.js +7 -0
- package/lib/sdk-base-B2ct7PnW.js.map +1 -0
- package/lib/sdk-base-DLPLYGiY.js +7 -0
- package/lib/sdk-base-DLPLYGiY.js.map +1 -0
- package/lib/sdk-base.d.ts +69 -0
- package/lib/sdk-base.d.ts.map +1 -0
- package/lib/services/analytics.d.ts +41 -59
- package/lib/services/analytics.d.ts.map +1 -0
- package/lib/setupTests.d.ts +1 -0
- package/lib/setupTests.d.ts.map +1 -0
- package/lib/stores/hooks.d.ts +8 -0
- package/lib/stores/hooks.d.ts.map +1 -0
- package/lib/stores/index.d.ts +4 -3
- package/lib/stores/index.d.ts.map +1 -0
- package/lib/stores/middleware/featuresUpdateMiddleware.d.ts +1 -0
- package/lib/stores/middleware/featuresUpdateMiddleware.d.ts.map +1 -0
- package/lib/stores/slices/appSlice.d.ts +2 -2
- package/lib/stores/slices/appSlice.d.ts.map +1 -0
- package/lib/stores/slices/avatarSlice.d.ts +34 -3
- package/lib/stores/slices/avatarSlice.d.ts.map +1 -0
- package/lib/stores/store.d.ts +5 -4
- package/lib/stores/store.d.ts.map +1 -0
- package/lib/types/errors.d.ts +1 -0
- package/lib/types/errors.d.ts.map +1 -0
- package/lib/types/index.d.ts +34 -53
- package/lib/types/index.d.ts.map +1 -0
- package/lib/umd.d.ts +1 -0
- package/lib/umd.d.ts.map +1 -0
- package/lib/utils/index.d.ts +1 -4
- package/lib/utils/index.d.ts.map +1 -0
- package/package.json +23 -19
- package/lib/constants/events.d.ts +0 -49
- package/lib/constants/greenscreen.d.ts +0 -28
- package/lib/constants/index.d.ts +0 -6
- package/lib/constants/waveform.d.ts +0 -34
- package/lib/services/webrtc.d.ts +0 -26
- package/lib/stores/selectors.d.ts +0 -30
- package/lib/types/abstract-typing.d.ts +0 -35
- package/lib/types/analytics.d.ts +0 -95
- package/lib/utils/classnames.d.ts +0 -48
- package/lib/utils/debug.d.ts +0 -84
- package/lib/utils/domFactory.d.ts +0 -56
- package/lib/utils/greenscreen/GreenScreenProcessor.d.ts +0 -25
- package/lib/utils/greenscreen/index.d.ts +0 -1
- package/lib/utils/svg.d.ts +0 -30
package/README.md
CHANGED
|
@@ -15,38 +15,47 @@
|
|
|
15
15
|
|
|
16
16
|
**Key Capabilities:**
|
|
17
17
|
|
|
18
|
-
- 🎯 **Multiple Build Targets**: Support for ES modules and standalone bundles
|
|
18
|
+
- 🎯 **Multiple Build Targets**: Support for ES modules, CommonJS, UMD, and standalone bundles
|
|
19
|
+
- 🔄 **React 16-19 Compatible**: Full support from React 16.0 through React 19.x
|
|
20
|
+
- 🚪 **Dual Entry Points**: Modern (React 18+) and Legacy (React 16+) versions
|
|
19
21
|
- 📦 **Tree-Shakeable**: Only import what you need for optimal bundle sizes
|
|
20
22
|
- ⚡ **Zero External Dependencies** (Standalone): Drop in a script tag and go
|
|
21
|
-
- ✨ **Advanced Features**:
|
|
22
|
-
- 🔧 **TypeScript Support**:
|
|
23
|
-
-
|
|
24
|
-
- 🚀 **Production Ready**: Minified builds with type definitions
|
|
23
|
+
- ✨ **Advanced Features**: Auto-play, waveform visualization, background removal, inactive timeouts, and more
|
|
24
|
+
- 🔧 **TypeScript Support**: Full type definitions for a great developer experience
|
|
25
|
+
- 🚀 **Production Ready**: Minified builds with source maps for debugging
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
|
28
|
-
##
|
|
29
|
+
## 2. Quick Start
|
|
29
30
|
|
|
30
31
|
Get up and running in minutes. Follow these four steps to integrate the SDK into your project.
|
|
31
32
|
|
|
32
|
-
###
|
|
33
|
+
### 2.1 Choose Your Setup
|
|
33
34
|
|
|
34
35
|
Pick the integration method that matches your project:
|
|
35
36
|
|
|
36
|
-
| Build Type
|
|
37
|
-
|
|
|
38
|
-
| **
|
|
39
|
-
| **
|
|
37
|
+
| Build Type | Best For | Import Path | React Included? |
|
|
38
|
+
| --------------------------- | ----------------------------------- | -------------------------------------------- | --------------- |
|
|
39
|
+
| **React 18+** (Bundler) | Modern React apps with Vite/Webpack | `@touchcastllc/napster-companion-api` | No (peer dep) |
|
|
40
|
+
| **React 16/17** (Legacy) | React 16/17 apps with bundlers | `@touchcastllc/napster-companion-api/legacy` | No (peer dep) |
|
|
41
|
+
| **UMD** (Script Tag) | Browser, bring your own React 18+ | `lib/index.umd.min.js` | No |
|
|
42
|
+
| **Standalone** (Script Tag) | Browser, zero dependencies | `lib/index.standalone.min.js` | Yes |
|
|
43
|
+
| **Core** (Headless) | Custom UI, framework-agnostic | `@touchcastllc/napster-companion-api/core` | No |
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
**Quick Links:**
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
- [React 18+ Example](#react-18-hooks--cleanup)
|
|
48
|
+
- [React 16/17 Example](#react-1617-class-component--cleanup)
|
|
49
|
+
- [Vue 3 Example](#vue-3-beforeunmount)
|
|
50
|
+
- [Vue 2 Example](#vue-2-beforedestroy)
|
|
51
|
+
- [Angular Example](#angular-ngondestroy)
|
|
52
|
+
- [Vanilla JS Example](#vanilla-js--core-no-build-tools)
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
### 2.2 Installation & Import
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
#### For Bundler-Based Projects
|
|
48
57
|
|
|
49
|
-
Install the package
|
|
58
|
+
**Install the package:**
|
|
50
59
|
|
|
51
60
|
```bash
|
|
52
61
|
npm install @touchcastllc/napster-companion-api
|
|
@@ -54,150 +63,117 @@ npm install @touchcastllc/napster-companion-api
|
|
|
54
63
|
yarn add @touchcastllc/napster-companion-api
|
|
55
64
|
```
|
|
56
65
|
|
|
57
|
-
Install peer dependencies
|
|
66
|
+
**Install peer dependencies:**
|
|
58
67
|
|
|
59
68
|
```bash
|
|
60
|
-
|
|
69
|
+
# For React 18+
|
|
70
|
+
npm install react@^18 react-dom@^18 @reduxjs/toolkit@^2 react-redux@^9
|
|
71
|
+
|
|
72
|
+
# For React 16/17
|
|
73
|
+
npm install react@^16 react-dom@^16 @reduxjs/toolkit@^2 react-redux@^8
|
|
61
74
|
```
|
|
62
75
|
|
|
63
|
-
**
|
|
76
|
+
**Import the SDK:**
|
|
64
77
|
|
|
65
78
|
```typescript
|
|
79
|
+
// React 18+
|
|
66
80
|
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Also, import CSS Stylesheet (required):**
|
|
70
81
|
|
|
71
|
-
|
|
82
|
+
// React 16/17
|
|
83
|
+
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api/legacy";
|
|
84
|
+
```
|
|
72
85
|
|
|
73
|
-
**
|
|
86
|
+
**Import CSS (required):**
|
|
74
87
|
|
|
75
88
|
```typescript
|
|
76
|
-
import "@touchcastllc/napster-companion-api/
|
|
89
|
+
import "@touchcastllc/napster-companion-api/lib/index.css";
|
|
77
90
|
```
|
|
78
91
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
```js
|
|
82
|
-
require("@touchcastllc/napster-companion-api/styles");
|
|
83
|
-
```
|
|
92
|
+
#### For Browser Script Tags
|
|
84
93
|
|
|
85
|
-
**
|
|
94
|
+
**Option A: Standalone (Easiest - React Included)**
|
|
86
95
|
|
|
87
96
|
```html
|
|
88
|
-
<
|
|
89
|
-
rel="stylesheet"
|
|
90
|
-
href="https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.css"
|
|
91
|
-
/>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**CSS `@import`:**
|
|
95
|
-
|
|
96
|
-
```css
|
|
97
|
-
@import url("https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.css");
|
|
97
|
+
<script src="https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.standalone.min.js"></script>
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
#### 1.2.2 For Standalone Build Type
|
|
103
|
-
|
|
104
|
-
**Step 1: Import the SDK**
|
|
100
|
+
**Option B: UMD (Smaller - Bring Your Own React)**
|
|
105
101
|
|
|
106
102
|
```html
|
|
107
|
-
|
|
108
|
-
<script src="https://cdn.jsdelivr.net/npm
|
|
103
|
+
<script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>
|
|
104
|
+
<script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
105
|
+
<script src="https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.umd.min.js"></script>
|
|
109
106
|
```
|
|
110
107
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
### 1.3 Initialization
|
|
114
|
-
|
|
115
|
-
#### 1.3.1 For ESM Build Type
|
|
108
|
+
### 2.3 Initialization
|
|
116
109
|
|
|
117
110
|
Initialize the SDK by calling `NapsterCompanionApiSdk.init()` with your auth token and configuration options.
|
|
118
111
|
|
|
119
|
-
See the [1.4 Authentication](#14-authentication) section for details on generating auth tokens.
|
|
120
|
-
|
|
121
112
|
**Basic Example:**
|
|
122
113
|
|
|
123
114
|
```typescript
|
|
115
|
+
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";
|
|
116
|
+
|
|
124
117
|
const widget = await NapsterCompanionApiSdk.init("YOUR_AUTH_TOKEN", {
|
|
125
118
|
mountContainer: "#avatar-container",
|
|
119
|
+
position: "bottom-right",
|
|
120
|
+
features: {
|
|
121
|
+
waveform: { enabled: true },
|
|
122
|
+
backgroundRemoval: { enabled: false },
|
|
123
|
+
inactiveTimeout: { enabled: true, timeoutDuration: 60000 },
|
|
124
|
+
},
|
|
126
125
|
});
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
#### 1.3.2 For Standalone Build Type
|
|
130
126
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
127
|
+
// The returned widget object exposes control methods:
|
|
128
|
+
// widget.showAvatar() - Show the avatar
|
|
129
|
+
// widget.hideAvatar() - Hide the avatar
|
|
130
|
+
// widget.destroy() - Destroy and unmount the widget
|
|
131
|
+
// widget.updateStyles({...}) - Update widget styles dynamically
|
|
132
|
+
// widget.enableFeature("waveform") - Enable a specific feature
|
|
133
|
+
// widget.disableFeature("waveform") - Disable a specific feature
|
|
134
|
+
// widget.updateFeatureConfig("waveform", { enabled: false }) - Update feature configuration
|
|
135
|
+
// widget.setPosition("top-left") - Update widget position dynamically
|
|
136
|
+
// widget.sendQuestion("Your question here") - Send a question to the avatar
|
|
139
137
|
```
|
|
140
138
|
|
|
141
|
-
**
|
|
139
|
+
**What `init()` Returns:**
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
The `init()` method returns a widget instance with the following methods:
|
|
142
|
+
|
|
143
|
+
| Method | Description |
|
|
144
|
+
| ----------------------- | --------------------------------------------- |
|
|
145
|
+
| `showAvatar()` | Shows the avatar |
|
|
146
|
+
| `hideAvatar()` | Hides the avatar |
|
|
147
|
+
| `destroy()` | Destroys and unmounts the widget from the DOM |
|
|
148
|
+
| `updateStyles()` | Updates widget styles dynamically |
|
|
149
|
+
| `enableFeature()` | Enables a specific feature |
|
|
150
|
+
| `disableFeature()` | Disables a specific feature |
|
|
151
|
+
| `updateFeatureConfig()` | Updates feature configuration |
|
|
152
|
+
| `setPosition()` | Updates widget position dynamically |
|
|
153
|
+
| `sendQuestion()` | Sends a question to the avatar |
|
|
150
154
|
|
|
151
155
|
---
|
|
152
156
|
|
|
153
|
-
###
|
|
157
|
+
### 2.4 Authentication
|
|
154
158
|
|
|
155
159
|
The SDK requires an **auth token** to connect to the Napster Companion API.
|
|
156
160
|
|
|
157
161
|
**Important:** **Do not generate tokens in client-side code** — use a backend service to keep your API key secure.
|
|
158
162
|
|
|
159
|
-
#### Step 1: Generate Napster
|
|
163
|
+
#### Step 1: Generate Napster API Key
|
|
160
164
|
|
|
161
|
-
1. Visit the [Napster
|
|
165
|
+
1. Visit the [Napster API Keys page](https://app.cogcache.com/organization/keys)
|
|
162
166
|
2. Click **Generate API key** and enter your details
|
|
163
167
|
3. Store your API key securely on your backend server
|
|
164
168
|
|
|
165
169
|
#### Step 2: Create Connection & Get Token (Backend)
|
|
166
170
|
|
|
167
|
-
Create a backend API call to the Napster
|
|
168
|
-
|
|
169
|
-
**Request URL:**
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
POST https://companion-api.napster.ai/public/connections
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**Request Headers:**
|
|
176
|
-
|
|
177
|
-
| Header | Value |
|
|
178
|
-
| -------------- | ------------------- |
|
|
179
|
-
| `content-type` | `application/json` |
|
|
180
|
-
| `x-api-key` | `YOUR_API_KEY_HERE` |
|
|
181
|
-
|
|
182
|
-
**Request Body:**
|
|
171
|
+
Create a backend API call to the Napster API to generate auth tokens:
|
|
183
172
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
| `providerConfig.voiceId` | string | Voice identifier (e.g., `alloy`) |
|
|
189
|
-
| `providerConfig.settings.instructions` | string | Custom instructions for the avatar |
|
|
190
|
-
| `providerConfig.settings.turnDetection.threshold` | number | Voice detection threshold |
|
|
191
|
-
| `providerConfig.settings.turnDetection.prefix_padding_ms` | number | Padding before speech detection (ms) |
|
|
192
|
-
| `providerConfig.settings.turnDetection.silence_duration_ms` | number | Silence duration threshold (ms) |
|
|
193
|
-
| `providerConfig.settings.temperature` | number | Response temperature (0-1) |
|
|
194
|
-
| `providerConfig.useGreenVideo` | boolean | Enable green screen background |
|
|
195
|
-
| `disableIdleTimeout` | boolean | Disable idle timeout |
|
|
196
|
-
|
|
197
|
-
**Example Request:**
|
|
198
|
-
|
|
199
|
-
```bash
|
|
200
|
-
curl 'https://companion-api.napster.ai/public/connections' \
|
|
173
|
+
```sh
|
|
174
|
+
curl 'https://management-api.cogcache.com/public/connections' \
|
|
175
|
+
-H 'accept: */*' \
|
|
176
|
+
-H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' \
|
|
201
177
|
-H 'content-type: application/json' \
|
|
202
178
|
-H 'x-api-key: YOUR_API_KEY_HERE' \
|
|
203
179
|
--data-raw '{
|
|
@@ -215,8 +191,7 @@ curl 'https://companion-api.napster.ai/public/connections' \
|
|
|
215
191
|
"temperature": 0
|
|
216
192
|
},
|
|
217
193
|
"useGreenVideo": true
|
|
218
|
-
}
|
|
219
|
-
"disableIdleTimeout": false
|
|
194
|
+
}
|
|
220
195
|
}'
|
|
221
196
|
```
|
|
222
197
|
|
|
@@ -225,608 +200,835 @@ curl 'https://companion-api.napster.ai/public/connections' \
|
|
|
225
200
|
Fetch the token from your backend and pass it to the SDK:
|
|
226
201
|
|
|
227
202
|
```typescript
|
|
228
|
-
//
|
|
203
|
+
// Client-side code
|
|
229
204
|
async function initializeAvatar() {
|
|
230
205
|
// Fetch token from your backend
|
|
231
|
-
const response = await fetch("/
|
|
206
|
+
const response = await fetch("/api/get-napster-token", { method: "POST" });
|
|
232
207
|
const { token } = await response.json();
|
|
208
|
+
|
|
209
|
+
// Initialize SDK with the token
|
|
210
|
+
const widget = await NapsterCompanionApiSdk.init(token, {
|
|
211
|
+
mountContainer: "#avatar-container",
|
|
212
|
+
position: "bottom-right",
|
|
213
|
+
});
|
|
233
214
|
}
|
|
234
215
|
```
|
|
235
216
|
|
|
236
217
|
---
|
|
237
218
|
|
|
238
|
-
##
|
|
219
|
+
## 3. Feature Configuration
|
|
239
220
|
|
|
240
|
-
|
|
221
|
+
The SDK supports several configurable features. You can enable/disable them during initialization or update them dynamically at runtime.
|
|
241
222
|
|
|
242
|
-
###
|
|
223
|
+
### Available Features
|
|
243
224
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
225
|
+
| Feature | Description | Default Value |
|
|
226
|
+
| ------------------- | ------------------------------------------- | -------------------------------------------- |
|
|
227
|
+
| `waveform` | Display audio waveform visualization | `{ enabled: false }` |
|
|
228
|
+
| `backgroundRemoval` | Remove avatar background for transparency | `{ enabled: false }` |
|
|
229
|
+
| `inactiveTimeout` | Auto-hide avatar after period of inactivity | `{ enabled: false, timeoutDuration: 60000 }` |
|
|
230
|
+
| `disclaimer` | Show disclaimer text above the avatar | `{ enabled: false, text: "" }` |
|
|
248
231
|
|
|
249
|
-
|
|
250
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
251
|
-
const [instance, setInstance] = useState<NapsterCompanionApiInstance | null>(
|
|
252
|
-
null
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
useEffect(() => {
|
|
256
|
-
const initSDK = async () => {
|
|
257
|
-
if (!containerRef.current) return;
|
|
232
|
+
### Configuration During Initialization
|
|
258
233
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
234
|
+
```typescript
|
|
235
|
+
const widget = await NapsterCompanionApiSdk.init("YOUR_AUTH_TOKEN", {
|
|
236
|
+
mountContainer: "#avatar-container",
|
|
237
|
+
position: "bottom-right",
|
|
238
|
+
features: {
|
|
239
|
+
autoplay: { enabled: true },
|
|
240
|
+
waveform: { enabled: true },
|
|
241
|
+
backgroundRemoval: { enabled: false },
|
|
242
|
+
inactiveTimeout: {
|
|
243
|
+
enabled: true,
|
|
244
|
+
timeoutDuration: 60000, // 60 seconds
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
```
|
|
269
249
|
|
|
270
|
-
|
|
250
|
+
### Dynamic Feature Updates
|
|
271
251
|
|
|
272
|
-
|
|
273
|
-
instance?.destroy();
|
|
274
|
-
};
|
|
275
|
-
}, []);
|
|
252
|
+
Update feature configuration after initialization:
|
|
276
253
|
|
|
277
|
-
|
|
278
|
-
|
|
254
|
+
```typescript
|
|
255
|
+
// Enable/disable a single feature
|
|
256
|
+
widget.enableFeature("waveform");
|
|
257
|
+
widget.disableFeature("waveform");
|
|
258
|
+
|
|
259
|
+
// Update feature configuration
|
|
260
|
+
widget.updateFeatureConfig({
|
|
261
|
+
autoplay: { enabled: true },
|
|
262
|
+
inactiveTimeout: {
|
|
263
|
+
enabled: true,
|
|
264
|
+
timeoutDuration: 30000, // 30 seconds
|
|
265
|
+
},
|
|
266
|
+
});
|
|
279
267
|
```
|
|
280
268
|
|
|
281
|
-
|
|
269
|
+
---
|
|
282
270
|
|
|
283
|
-
|
|
284
|
-
<template>
|
|
285
|
-
<div ref="containerRef"></div>
|
|
286
|
-
</template>
|
|
271
|
+
## 4. API Reference
|
|
287
272
|
|
|
288
|
-
|
|
289
|
-
import { onMounted, onUnmounted, ref } from "vue";
|
|
290
|
-
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";
|
|
291
|
-
import type { NapsterCompanionApiInstance } from "@touchcastllc/napster-companion-api";
|
|
273
|
+
### Initialization
|
|
292
274
|
|
|
293
|
-
|
|
294
|
-
let instance: NapsterCompanionApiInstance | null = null;
|
|
275
|
+
#### `NapsterCompanionApiSdk.init(token, options)`
|
|
295
276
|
|
|
296
|
-
|
|
297
|
-
if (!containerRef.value) return;
|
|
298
|
-
|
|
299
|
-
instance = await NapsterCompanionApiSdk.init("YOUR_TOKEN", {
|
|
300
|
-
mountContainer: containerRef.value,
|
|
301
|
-
position: "bottom-right",
|
|
302
|
-
features: {
|
|
303
|
-
waveform: { enabled: true },
|
|
304
|
-
backgroundRemoval: { enabled: true },
|
|
305
|
-
},
|
|
306
|
-
});
|
|
307
|
-
});
|
|
277
|
+
Initialize and mount the avatar widget.
|
|
308
278
|
|
|
309
|
-
|
|
310
|
-
instance?.destroy();
|
|
311
|
-
});
|
|
312
|
-
</script>
|
|
313
|
-
```
|
|
279
|
+
**Parameters:**
|
|
314
280
|
|
|
315
|
-
|
|
281
|
+
| Parameter | Type | Required | Description |
|
|
282
|
+
| --------- | --------------------------- | -------- | ---------------------------- |
|
|
283
|
+
| `token` | `string` | Yes | Auth token from your backend |
|
|
284
|
+
| `options` | `NapsterCompanionApiConfig` | Yes | Configuration object |
|
|
285
|
+
|
|
286
|
+
**`NapsterCompanionApiConfig` Interface:**
|
|
316
287
|
|
|
317
288
|
```typescript
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
289
|
+
interface NapsterCompanionApiConfig {
|
|
290
|
+
mountContainer: string | HTMLElement; // CSS selector or DOM element
|
|
291
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
292
|
+
features?: {
|
|
293
|
+
autoplay?: { enabled: boolean };
|
|
294
|
+
waveform?: { enabled: boolean };
|
|
295
|
+
backgroundRemoval?: { enabled: boolean };
|
|
296
|
+
inactiveTimeout?: { enabled: boolean; timeoutDuration?: number };
|
|
297
|
+
};
|
|
298
|
+
styles?: {
|
|
299
|
+
width?: string;
|
|
300
|
+
height?: string;
|
|
301
|
+
zIndex?: number;
|
|
302
|
+
};
|
|
303
|
+
onReady?: () => void;
|
|
304
|
+
onError?: (error: Error) => void;
|
|
305
|
+
onData?: (data: EventMessage) => void;
|
|
306
|
+
onAvatarReady?: () => void;
|
|
307
|
+
onDestroy?: () => void;
|
|
308
|
+
}
|
|
309
|
+
```
|
|
329
310
|
|
|
330
|
-
|
|
331
|
-
selector: "app-companion",
|
|
332
|
-
template: "<div #containerRef></div>",
|
|
333
|
-
})
|
|
334
|
-
export class CompanionComponent implements OnInit, OnDestroy {
|
|
335
|
-
@ViewChild("containerRef") containerRef!: ElementRef<HTMLDivElement>;
|
|
336
|
-
private instance: NapsterCompanionApiInstance | null = null;
|
|
311
|
+
**Returns:** `Promise<NapsterCompanionApiInstance>`
|
|
337
312
|
|
|
338
|
-
|
|
339
|
-
this.instance = await NapsterCompanionApiSdk.init("YOUR_TOKEN", {
|
|
340
|
-
mountContainer: this.containerRef.nativeElement,
|
|
341
|
-
position: "bottom-right",
|
|
342
|
-
features: {
|
|
343
|
-
waveform: { enabled: true },
|
|
344
|
-
backgroundRemoval: { enabled: true },
|
|
345
|
-
},
|
|
346
|
-
});
|
|
347
|
-
}
|
|
313
|
+
**Example:**
|
|
348
314
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
315
|
+
```typescript
|
|
316
|
+
const widget = await NapsterCompanionApiSdk.init("YOUR_TOKEN", {
|
|
317
|
+
mountContainer: "#avatar-container",
|
|
318
|
+
position: "bottom-right",
|
|
319
|
+
});
|
|
353
320
|
```
|
|
354
321
|
|
|
355
|
-
|
|
322
|
+
---
|
|
356
323
|
|
|
357
|
-
|
|
358
|
-
<div id="sdk-container"></div>
|
|
359
|
-
<script src="https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.standalone.js"></script>
|
|
360
|
-
<script>
|
|
361
|
-
const sdk = window.napsterCompanionApiSDK;
|
|
362
|
-
|
|
363
|
-
sdk
|
|
364
|
-
.init("YOUR_TOKEN", {
|
|
365
|
-
mountContainer: "#sdk-container",
|
|
366
|
-
position: "bottom-right",
|
|
367
|
-
features: {
|
|
368
|
-
waveform: { enabled: true },
|
|
369
|
-
backgroundRemoval: { enabled: true },
|
|
370
|
-
},
|
|
371
|
-
})
|
|
372
|
-
.then(instance => {
|
|
373
|
-
// Control the avatar
|
|
374
|
-
instance.showAvatar();
|
|
324
|
+
### Widget Instance Methods
|
|
375
325
|
|
|
376
|
-
|
|
377
|
-
setTimeout(() => instance.hideAvatar(), 10000);
|
|
378
|
-
});
|
|
379
|
-
</script>
|
|
380
|
-
```
|
|
326
|
+
#### `widget.showAvatar()`
|
|
381
327
|
|
|
382
|
-
|
|
328
|
+
Shows the avatar widget.
|
|
383
329
|
|
|
384
330
|
```typescript
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
mountContainer: "#avatar-container",
|
|
388
|
-
position: "bottom-right",
|
|
331
|
+
widget.showAvatar();
|
|
332
|
+
```
|
|
389
333
|
|
|
390
|
-
|
|
391
|
-
backgroundRemoval: { enabled: true },
|
|
392
|
-
waveform: { enabled: true, color: "#00ff88" },
|
|
393
|
-
inactiveTimeout: { enabled: true, duration: 120000, countdown: 15 },
|
|
394
|
-
showSDKLoader: { enabled: true, bgColor: "#f0f0f0" },
|
|
395
|
-
},
|
|
334
|
+
---
|
|
396
335
|
|
|
397
|
-
|
|
398
|
-
borderRadius: "12px",
|
|
399
|
-
boxShadow: "0 4px 20px rgba(0,0,0,0.3)",
|
|
400
|
-
},
|
|
336
|
+
#### `widget.hideAvatar()`
|
|
401
337
|
|
|
402
|
-
|
|
403
|
-
onError: error => console.error("Error:", error),
|
|
404
|
-
onAvatarReady: ready => console.log("Avatar loaded:", ready),
|
|
405
|
-
});
|
|
338
|
+
Hides the avatar widget.
|
|
406
339
|
|
|
407
|
-
|
|
408
|
-
|
|
340
|
+
```typescript
|
|
341
|
+
widget.hideAvatar();
|
|
409
342
|
```
|
|
410
343
|
|
|
411
|
-
|
|
344
|
+
---
|
|
412
345
|
|
|
413
|
-
|
|
414
|
-
async function controlFeatures() {
|
|
415
|
-
const instance = await NapsterCompanionApiSdk.init("YOUR_TOKEN", {
|
|
416
|
-
mountContainer: "#avatar-container",
|
|
417
|
-
});
|
|
346
|
+
#### `widget.destroy()`
|
|
418
347
|
|
|
419
|
-
|
|
420
|
-
document.getElementById("toggle-waveform")?.addEventListener("click", () => {
|
|
421
|
-
if (instance.isFeatureEnabled("waveform")) {
|
|
422
|
-
instance.disableFeature("waveform");
|
|
423
|
-
} else {
|
|
424
|
-
instance.enableFeature("waveform");
|
|
425
|
-
}
|
|
426
|
-
});
|
|
348
|
+
Destroys and unmounts the widget from the DOM. Call this during cleanup.
|
|
427
349
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
instance.setPosition("top-left");
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
return instance;
|
|
434
|
-
}
|
|
350
|
+
```typescript
|
|
351
|
+
widget.destroy();
|
|
435
352
|
```
|
|
436
353
|
|
|
437
|
-
|
|
354
|
+
---
|
|
438
355
|
|
|
439
|
-
####
|
|
356
|
+
#### `widget.updateStyles(styles)`
|
|
440
357
|
|
|
441
|
-
|
|
442
|
-
const instance = await NapsterCompanionApiSdk.init(token, {
|
|
443
|
-
style: {
|
|
444
|
-
border: "2px solid #00ff00",
|
|
445
|
-
borderRadius: "8px",
|
|
446
|
-
boxShadow: "0 2px 10px rgba(0,0,0,0.2)",
|
|
447
|
-
background: "rgba(255,255,255,0.95)",
|
|
448
|
-
},
|
|
449
|
-
});
|
|
450
|
-
```
|
|
358
|
+
Update widget styling dynamically.
|
|
451
359
|
|
|
452
|
-
|
|
360
|
+
**Parameters:**
|
|
453
361
|
|
|
454
362
|
```typescript
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
```css
|
|
461
|
-
.my-custom-avatar {
|
|
462
|
-
border: 2px solid #007acc;
|
|
463
|
-
border-radius: 12px;
|
|
464
|
-
backdrop-filter: blur(10px);
|
|
363
|
+
interface React.CSSProperties {
|
|
364
|
+
width?: string;
|
|
365
|
+
height?: string;
|
|
366
|
+
zIndex?: number;
|
|
367
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
465
368
|
}
|
|
466
369
|
```
|
|
467
370
|
|
|
468
|
-
|
|
371
|
+
**Example:**
|
|
469
372
|
|
|
470
373
|
```typescript
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
374
|
+
widget.updateStyles({
|
|
375
|
+
width: "400px",
|
|
376
|
+
height: "600px",
|
|
377
|
+
zIndex: 1000,
|
|
378
|
+
position: "top-right",
|
|
476
379
|
});
|
|
477
380
|
```
|
|
478
381
|
|
|
479
382
|
---
|
|
480
383
|
|
|
481
|
-
|
|
384
|
+
#### `widget.enableFeature(featureName)`
|
|
482
385
|
|
|
483
|
-
|
|
386
|
+
Enable a specific feature.
|
|
484
387
|
|
|
485
|
-
|
|
388
|
+
**Parameters:**
|
|
486
389
|
|
|
487
|
-
|
|
390
|
+
- `featureName`: `"autoplay" | "waveform" | "backgroundRemoval" | "inactiveTimeout"`
|
|
488
391
|
|
|
489
|
-
**
|
|
392
|
+
**Example:**
|
|
490
393
|
|
|
491
394
|
```typescript
|
|
492
|
-
|
|
493
|
-
features: {
|
|
494
|
-
backgroundRemoval: { enabled: true },
|
|
495
|
-
},
|
|
496
|
-
});
|
|
395
|
+
widget.enableFeature("waveform");
|
|
497
396
|
```
|
|
498
397
|
|
|
499
|
-
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
#### `widget.disableFeature(featureName)`
|
|
401
|
+
|
|
402
|
+
Disable a specific feature.
|
|
403
|
+
|
|
404
|
+
**Parameters:**
|
|
500
405
|
|
|
501
|
-
|
|
406
|
+
- `featureName`: `"autoplay" | "waveform" | "backgroundRemoval" | "inactiveTimeout"`
|
|
407
|
+
|
|
408
|
+
**Example:**
|
|
502
409
|
|
|
503
410
|
```typescript
|
|
504
|
-
|
|
505
|
-
features: {
|
|
506
|
-
waveform: {
|
|
507
|
-
enabled: true,
|
|
508
|
-
color: "#00ff00", // Optional custom color
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
});
|
|
411
|
+
widget.disableFeature("autoplay");
|
|
512
412
|
```
|
|
513
413
|
|
|
514
|
-
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
#### `widget.updateFeatureConfig(config)`
|
|
515
417
|
|
|
516
|
-
|
|
418
|
+
Update multiple feature configurations at once.
|
|
419
|
+
|
|
420
|
+
**Parameters:**
|
|
517
421
|
|
|
518
422
|
```typescript
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
},
|
|
526
|
-
},
|
|
527
|
-
});
|
|
423
|
+
interface FeatureConfig {
|
|
424
|
+
autoplay?: { enabled: boolean };
|
|
425
|
+
waveform?: { enabled: boolean };
|
|
426
|
+
backgroundRemoval?: { enabled: boolean };
|
|
427
|
+
inactiveTimeout?: { enabled: boolean; timeoutDuration?: number };
|
|
428
|
+
}
|
|
528
429
|
```
|
|
529
430
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
Display a loading screen while the avatar assets are being loaded. Customize the background color to match your application's design.
|
|
431
|
+
**Example:**
|
|
533
432
|
|
|
534
433
|
```typescript
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
enabled: true,
|
|
539
|
-
bgColor: "#ffffff", // Optional background color
|
|
540
|
-
},
|
|
541
|
-
},
|
|
434
|
+
widget.updateFeatureConfig({
|
|
435
|
+
autoplay: { enabled: true },
|
|
436
|
+
inactiveTimeout: { enabled: true, timeoutDuration: 45000 },
|
|
542
437
|
});
|
|
543
438
|
```
|
|
544
439
|
|
|
545
|
-
|
|
440
|
+
---
|
|
546
441
|
|
|
547
|
-
|
|
442
|
+
### Type Definitions
|
|
548
443
|
|
|
549
444
|
```typescript
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
445
|
+
// Main initialization options
|
|
446
|
+
interface NapsterCompanionApiConfig {
|
|
447
|
+
mountContainer: string | HTMLElement;
|
|
448
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
449
|
+
features?: FeatureConfig;
|
|
450
|
+
styles?: React.CSSProperties;
|
|
451
|
+
onReady?: () => void;
|
|
452
|
+
onError?: (error: Error) => void;
|
|
453
|
+
onData?: (data: EventMessage) => void;
|
|
454
|
+
onAvatarReady?: () => void;
|
|
455
|
+
onDestroy?: () => void;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Feature configuration
|
|
459
|
+
interface FeatureConfig {
|
|
460
|
+
autoplay?: { enabled: boolean };
|
|
461
|
+
waveform?: { enabled: boolean };
|
|
462
|
+
backgroundRemoval?: { enabled: boolean };
|
|
463
|
+
inactiveTimeout?: { enabled: boolean; timeoutDuration?: number };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Widget instance returned by init()
|
|
467
|
+
interface NapsterCompanionApiInstance {
|
|
468
|
+
showAvatar(): void;
|
|
469
|
+
hideAvatar(): void;
|
|
470
|
+
destroy(): void;
|
|
471
|
+
updateStyles(styles: React.CSSProperties): void;
|
|
472
|
+
enableFeature(feature: string): void;
|
|
473
|
+
disableFeature(feature: string): void;
|
|
474
|
+
updateFeatureConfig(config: FeatureConfig): void;
|
|
475
|
+
}
|
|
562
476
|
```
|
|
563
477
|
|
|
564
|
-
|
|
478
|
+
---
|
|
565
479
|
|
|
566
|
-
|
|
567
|
-
- `"bottom-left"`
|
|
568
|
-
- `"bottom-center"`
|
|
569
|
-
- `"top-right"`
|
|
570
|
-
- `"top-left"`
|
|
571
|
-
- `"top-center"`
|
|
572
|
-
- `"center"`
|
|
480
|
+
## 5. Usage Examples
|
|
573
481
|
|
|
574
|
-
|
|
482
|
+
Framework-specific, copy-paste-ready snippets demonstrating proper lifecycle management.
|
|
575
483
|
|
|
576
|
-
|
|
484
|
+
### React 18+ (Hooks)
|
|
577
485
|
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
486
|
+
```tsx
|
|
487
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
488
|
+
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";
|
|
489
|
+
|
|
490
|
+
export function AvatarWidget() {
|
|
491
|
+
const containerRef = useRef(null);
|
|
492
|
+
const [widget, setWidget] = useState(null);
|
|
493
|
+
|
|
494
|
+
useEffect(() => {
|
|
495
|
+
let mounted = true;
|
|
496
|
+
|
|
497
|
+
async function initialize() {
|
|
498
|
+
try {
|
|
499
|
+
// Fetch token from your backend
|
|
500
|
+
const response = await fetch("/api/get-napster-token", {
|
|
501
|
+
method: "POST",
|
|
502
|
+
});
|
|
503
|
+
const { token } = await response.json();
|
|
504
|
+
|
|
505
|
+
// Initialize widget
|
|
506
|
+
const instance = await NapsterCompanionApiSdk.init(token, {
|
|
507
|
+
mountContainer: containerRef.current,
|
|
508
|
+
position: "bottom-right",
|
|
509
|
+
features: {
|
|
510
|
+
autoplay: { enabled: true },
|
|
511
|
+
waveform: { enabled: true },
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
if (mounted) {
|
|
516
|
+
setWidget(instance);
|
|
517
|
+
}
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.error("Failed to initialize avatar:", error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
initialize();
|
|
524
|
+
|
|
525
|
+
// Cleanup on unmount
|
|
526
|
+
return () => {
|
|
527
|
+
mounted = false;
|
|
528
|
+
widget?.destroy();
|
|
529
|
+
};
|
|
530
|
+
}, []);
|
|
531
|
+
|
|
532
|
+
return <div ref={containerRef} style={{ width: "100%", height: "100%" }} />;
|
|
533
|
+
}
|
|
593
534
|
```
|
|
594
535
|
|
|
595
536
|
---
|
|
596
537
|
|
|
597
|
-
|
|
538
|
+
### React 16/17 (Class Component)
|
|
598
539
|
|
|
599
|
-
|
|
540
|
+
```tsx
|
|
541
|
+
import React from "react";
|
|
542
|
+
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api/legacy";
|
|
543
|
+
|
|
544
|
+
export class AvatarWidget extends React.Component {
|
|
545
|
+
containerRef = React.createRef();
|
|
546
|
+
widget = null;
|
|
547
|
+
|
|
548
|
+
async componentDidMount() {
|
|
549
|
+
try {
|
|
550
|
+
// Fetch token from your backend
|
|
551
|
+
const response = await fetch("/api/get-napster-token", {
|
|
552
|
+
method: "POST",
|
|
553
|
+
});
|
|
554
|
+
const { token } = await response.json();
|
|
600
555
|
|
|
601
|
-
|
|
556
|
+
// Initialize widget
|
|
557
|
+
this.widget = await NapsterCompanionApiSdk.init(token, {
|
|
558
|
+
mountContainer: this.containerRef.current,
|
|
559
|
+
position: "bottom-right",
|
|
560
|
+
features: {
|
|
561
|
+
autoplay: { enabled: true },
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
} catch (error) {
|
|
565
|
+
console.error("Failed to initialize avatar:", error);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
602
568
|
|
|
603
|
-
|
|
569
|
+
componentWillUnmount() {
|
|
570
|
+
// Cleanup on unmount
|
|
571
|
+
this.widget?.destroy();
|
|
572
|
+
}
|
|
604
573
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
574
|
+
render() {
|
|
575
|
+
return (
|
|
576
|
+
<div ref={this.containerRef} style={{ width: "100%", height: "100%" }} />
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
610
580
|
```
|
|
611
581
|
|
|
612
|
-
|
|
582
|
+
---
|
|
613
583
|
|
|
614
|
-
|
|
584
|
+
### Vue 3
|
|
615
585
|
|
|
616
|
-
```
|
|
617
|
-
|
|
586
|
+
```html
|
|
587
|
+
<template>
|
|
588
|
+
<div ref="containerRef" style="width: 100%; height: 100%;"></div>
|
|
589
|
+
</template>
|
|
590
|
+
|
|
591
|
+
<script setup>
|
|
592
|
+
import { ref, onMounted, onBeforeUnmount } from "vue";
|
|
593
|
+
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";
|
|
594
|
+
import "@touchcastllc/napster-companion-api/lib/index.css";
|
|
595
|
+
|
|
596
|
+
const containerRef = ref(null);
|
|
597
|
+
let widget = null;
|
|
598
|
+
|
|
599
|
+
onMounted(async () => {
|
|
600
|
+
try {
|
|
601
|
+
// Fetch token from your backend
|
|
602
|
+
const response = await fetch("/api/get-napster-token", {
|
|
603
|
+
method: "POST",
|
|
604
|
+
});
|
|
605
|
+
const { token } = await response.json();
|
|
606
|
+
|
|
607
|
+
// Initialize widget
|
|
608
|
+
widget = await NapsterCompanionApiSdk.init(token, {
|
|
609
|
+
mountContainer: containerRef.value,
|
|
610
|
+
position: "bottom-right",
|
|
611
|
+
features: {
|
|
612
|
+
autoplay: { enabled: true },
|
|
613
|
+
waveform: { enabled: true },
|
|
614
|
+
},
|
|
615
|
+
});
|
|
616
|
+
} catch (error) {
|
|
617
|
+
console.error("Failed to initialize avatar:", error);
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
onBeforeUnmount(() => {
|
|
622
|
+
// Cleanup on unmount
|
|
623
|
+
widget?.destroy();
|
|
624
|
+
});
|
|
625
|
+
</script>
|
|
618
626
|
```
|
|
619
627
|
|
|
620
|
-
|
|
628
|
+
---
|
|
621
629
|
|
|
622
|
-
|
|
630
|
+
### Vue 2
|
|
623
631
|
|
|
624
|
-
```
|
|
625
|
-
|
|
632
|
+
```html
|
|
633
|
+
<template>
|
|
634
|
+
<div ref="container" style="width: 100%; height: 100%;"></div>
|
|
635
|
+
</template>
|
|
636
|
+
|
|
637
|
+
<script>
|
|
638
|
+
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";
|
|
639
|
+
import "@touchcastllc/napster-companion-api/lib/index.css";
|
|
640
|
+
|
|
641
|
+
export default {
|
|
642
|
+
name: "AvatarWidget",
|
|
643
|
+
data() {
|
|
644
|
+
return {
|
|
645
|
+
widget: null,
|
|
646
|
+
};
|
|
647
|
+
},
|
|
648
|
+
async mounted() {
|
|
649
|
+
try {
|
|
650
|
+
// Fetch token from your backend
|
|
651
|
+
const response = await fetch("/api/get-napster-token", {
|
|
652
|
+
method: "POST",
|
|
653
|
+
});
|
|
654
|
+
const { token } = await response.json();
|
|
655
|
+
|
|
656
|
+
// Initialize widget
|
|
657
|
+
this.widget = await NapsterCompanionApiSdk.init(token, {
|
|
658
|
+
mountContainer: this.$refs.container,
|
|
659
|
+
position: "bottom-right",
|
|
660
|
+
features: {
|
|
661
|
+
autoplay: { enabled: true },
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
} catch (error) {
|
|
665
|
+
console.error("Failed to initialize avatar:", error);
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
beforeDestroy() {
|
|
669
|
+
// Cleanup on unmount
|
|
670
|
+
this.widget?.destroy();
|
|
671
|
+
},
|
|
672
|
+
};
|
|
673
|
+
</script>
|
|
626
674
|
```
|
|
627
675
|
|
|
628
|
-
|
|
676
|
+
---
|
|
629
677
|
|
|
630
|
-
|
|
678
|
+
### Angular
|
|
631
679
|
|
|
632
680
|
```typescript
|
|
633
|
-
|
|
634
|
-
|
|
681
|
+
import {
|
|
682
|
+
Component,
|
|
683
|
+
OnInit,
|
|
684
|
+
OnDestroy,
|
|
685
|
+
ElementRef,
|
|
686
|
+
ViewChild,
|
|
687
|
+
} from "@angular/core";
|
|
688
|
+
import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";
|
|
689
|
+
|
|
690
|
+
@Component({
|
|
691
|
+
selector: "app-avatar-widget",
|
|
692
|
+
template: ` <div #container style="width: 100%; height: 100%;"></div> `,
|
|
693
|
+
styles: [
|
|
694
|
+
`
|
|
695
|
+
@import "@touchcastllc/napster-companion-api/lib/index.css";
|
|
696
|
+
`,
|
|
697
|
+
],
|
|
698
|
+
})
|
|
699
|
+
export class AvatarWidgetComponent implements OnInit, OnDestroy {
|
|
700
|
+
@ViewChild("container", { static: true }) container!: ElementRef;
|
|
701
|
+
private widget: any = null;
|
|
702
|
+
|
|
703
|
+
async ngOnInit() {
|
|
704
|
+
try {
|
|
705
|
+
// Fetch token from your backend
|
|
706
|
+
const response = await fetch("/api/get-napster-token", {
|
|
707
|
+
method: "POST",
|
|
708
|
+
});
|
|
709
|
+
const { token } = await response.json();
|
|
710
|
+
|
|
711
|
+
// Initialize widget
|
|
712
|
+
this.widget = await NapsterCompanionApiSdk.init(token, {
|
|
713
|
+
mountContainer: this.container.nativeElement,
|
|
714
|
+
position: "bottom-right",
|
|
715
|
+
features: {
|
|
716
|
+
autoplay: { enabled: true },
|
|
717
|
+
waveform: { enabled: true },
|
|
718
|
+
},
|
|
719
|
+
});
|
|
720
|
+
} catch (error) {
|
|
721
|
+
console.error("Failed to initialize avatar:", error);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
ngOnDestroy() {
|
|
726
|
+
// Cleanup on unmount
|
|
727
|
+
this.widget?.destroy();
|
|
728
|
+
}
|
|
635
729
|
}
|
|
636
730
|
```
|
|
637
731
|
|
|
638
|
-
|
|
732
|
+
---
|
|
639
733
|
|
|
640
|
-
|
|
734
|
+
### Vanilla JS / Core (No Build Tools)
|
|
641
735
|
|
|
642
|
-
```
|
|
643
|
-
|
|
736
|
+
```html
|
|
737
|
+
<!DOCTYPE html>
|
|
738
|
+
<html lang="en">
|
|
739
|
+
<head>
|
|
740
|
+
<meta charset="UTF-8" />
|
|
741
|
+
<title>Napster Avatar SDK</title>
|
|
742
|
+
</head>
|
|
743
|
+
<body>
|
|
744
|
+
<div id="avatar-container"></div>
|
|
745
|
+
|
|
746
|
+
<script src="https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.standalone.min.js"></script>
|
|
747
|
+
<script>
|
|
748
|
+
(async function () {
|
|
749
|
+
try {
|
|
750
|
+
// Fetch token from your backend
|
|
751
|
+
const response = await fetch("/api/get-napster-token", {
|
|
752
|
+
method: "POST",
|
|
753
|
+
});
|
|
754
|
+
const { token } = await response.json();
|
|
755
|
+
|
|
756
|
+
// Initialize widget
|
|
757
|
+
const widget = await window.CompanionApiSDK.init(token, {
|
|
758
|
+
mountContainer: "#avatar-container",
|
|
759
|
+
position: "bottom-right",
|
|
760
|
+
features: {
|
|
761
|
+
autoplay: { enabled: true },
|
|
762
|
+
waveform: { enabled: true },
|
|
763
|
+
},
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
// Widget controls
|
|
767
|
+
// widget.showAvatar();
|
|
768
|
+
// widget.hideAvatar();
|
|
769
|
+
// widget.destroy();
|
|
770
|
+
} catch (error) {
|
|
771
|
+
console.error("Failed to initialize avatar:", error);
|
|
772
|
+
}
|
|
773
|
+
})();
|
|
774
|
+
</script>
|
|
775
|
+
</body>
|
|
776
|
+
</html>
|
|
644
777
|
```
|
|
645
778
|
|
|
646
|
-
|
|
779
|
+
---
|
|
780
|
+
|
|
781
|
+
## 6. Styling & Customization
|
|
647
782
|
|
|
648
|
-
|
|
783
|
+
### CSS Import Path
|
|
649
784
|
|
|
650
|
-
|
|
785
|
+
Always import the SDK's CSS file to ensure proper styling:
|
|
786
|
+
|
|
787
|
+
**For Bundler Projects:**
|
|
651
788
|
|
|
652
789
|
```typescript
|
|
653
|
-
|
|
654
|
-
top: "50px",
|
|
655
|
-
left: "50px",
|
|
656
|
-
opacity: "0.8",
|
|
657
|
-
});
|
|
790
|
+
import "@touchcastllc/napster-companion-api/lib/index.css";
|
|
658
791
|
```
|
|
659
792
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
Change avatar position on screen.
|
|
793
|
+
**For Browser Script Tags:**
|
|
663
794
|
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
795
|
+
```html
|
|
796
|
+
<link
|
|
797
|
+
rel="stylesheet"
|
|
798
|
+
href="https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.css"
|
|
799
|
+
/>
|
|
669
800
|
```
|
|
670
801
|
|
|
671
|
-
|
|
802
|
+
### Custom Container Styling
|
|
672
803
|
|
|
673
|
-
|
|
804
|
+
You can customize the avatar container's size, position, and z-index:
|
|
674
805
|
|
|
675
806
|
```typescript
|
|
676
|
-
|
|
807
|
+
const widget = await NapsterCompanionApiSdk.init("YOUR_TOKEN", {
|
|
808
|
+
mountContainer: "#avatar-container",
|
|
809
|
+
position: "bottom-right",
|
|
810
|
+
styles: {
|
|
811
|
+
width: "400px",
|
|
812
|
+
height: "600px",
|
|
813
|
+
zIndex: 9999,
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// Update styles dynamically
|
|
818
|
+
widget.updateStyles({
|
|
819
|
+
width: "500px",
|
|
820
|
+
height: "700px",
|
|
821
|
+
position: "top-right",
|
|
822
|
+
});
|
|
677
823
|
```
|
|
678
824
|
|
|
679
|
-
|
|
825
|
+
---
|
|
680
826
|
|
|
681
|
-
|
|
827
|
+
## 7. Troubleshooting
|
|
682
828
|
|
|
683
|
-
|
|
829
|
+
Common integration errors and their quick resolutions.
|
|
684
830
|
|
|
685
|
-
|
|
686
|
-
instance.enableFeature("waveform");
|
|
687
|
-
instance.enableFeature("backgroundRemoval");
|
|
688
|
-
```
|
|
831
|
+
### React Version Mismatch
|
|
689
832
|
|
|
690
|
-
|
|
833
|
+
**Error:** `Error: Invalid hook call` or `React version mismatch`
|
|
691
834
|
|
|
692
|
-
|
|
835
|
+
**Cause:** Using the wrong SDK entry point for your React version.
|
|
693
836
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
837
|
+
**Solution:**
|
|
838
|
+
|
|
839
|
+
- For React 18+, use: `import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api";`
|
|
840
|
+
- For React 16/17, use: `import { NapsterCompanionApiSdk } from "@touchcastllc/napster-companion-api/legacy";`
|
|
841
|
+
|
|
842
|
+
```bash
|
|
843
|
+
# Check your React version
|
|
844
|
+
npm list react
|
|
697
845
|
```
|
|
698
846
|
|
|
699
|
-
|
|
847
|
+
---
|
|
700
848
|
|
|
701
|
-
|
|
849
|
+
### Missing CSS Styles
|
|
702
850
|
|
|
703
|
-
|
|
704
|
-
if (instance.isFeatureEnabled("waveform")) {
|
|
705
|
-
console.log("Waveform is enabled");
|
|
706
|
-
}
|
|
707
|
-
```
|
|
851
|
+
**Error:** Widget appears unstyled or invisible
|
|
708
852
|
|
|
709
|
-
|
|
853
|
+
**Cause:** CSS file not imported.
|
|
854
|
+
|
|
855
|
+
**Solution:**
|
|
856
|
+
|
|
857
|
+
Add the CSS import:
|
|
710
858
|
|
|
711
859
|
```typescript
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
mountContainer?: HTMLElement | string | null;
|
|
715
|
-
|
|
716
|
-
// Positioning
|
|
717
|
-
position?:
|
|
718
|
-
| "bottom-right"
|
|
719
|
-
| "bottom-left"
|
|
720
|
-
| "bottom-center"
|
|
721
|
-
| "top-right"
|
|
722
|
-
| "top-left"
|
|
723
|
-
| "top-center"
|
|
724
|
-
| "center";
|
|
725
|
-
|
|
726
|
-
// Styling
|
|
727
|
-
style?: StyleObject;
|
|
728
|
-
className?: string;
|
|
729
|
-
|
|
730
|
-
// Features
|
|
731
|
-
features?: {
|
|
732
|
-
backgroundRemoval?: { enabled: boolean };
|
|
733
|
-
waveform?: { enabled: boolean; color?: string };
|
|
734
|
-
inactiveTimeout?: {
|
|
735
|
-
enabled: boolean;
|
|
736
|
-
duration?: number; // max: 180000ms
|
|
737
|
-
countdown?: number; // max: 60s
|
|
738
|
-
};
|
|
739
|
-
disclaimer?: { enabled: boolean; text?: string };
|
|
740
|
-
showSDKLoader?: { enabled: boolean; bgColor?: string };
|
|
741
|
-
};
|
|
860
|
+
import "@touchcastllc/napster-companion-api/lib/index.css";
|
|
861
|
+
```
|
|
742
862
|
|
|
743
|
-
|
|
744
|
-
debug?: boolean;
|
|
863
|
+
For HTML script tags:
|
|
745
864
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
onInactivityStatusChange?: (isInactive: boolean) => void;
|
|
752
|
-
onDestroy?: () => void;
|
|
753
|
-
onFeaturesUpdate?: (features: FeatureConfig) => void;
|
|
754
|
-
}
|
|
865
|
+
```html
|
|
866
|
+
<link
|
|
867
|
+
rel="stylesheet"
|
|
868
|
+
href="https://cdn.jsdelivr.net/npm/@touchcastllc/napster-companion-api@latest/lib/index.css"
|
|
869
|
+
/>
|
|
755
870
|
```
|
|
756
871
|
|
|
757
872
|
---
|
|
758
873
|
|
|
759
|
-
|
|
874
|
+
### CORS Errors
|
|
875
|
+
|
|
876
|
+
**Error:** `Cross-Origin Request Blocked` when fetching token
|
|
760
877
|
|
|
761
|
-
|
|
878
|
+
**Cause:** Making token generation API calls from the browser.
|
|
762
879
|
|
|
763
|
-
**Problem: "Module not found" Error**
|
|
764
880
|
**Solution:**
|
|
765
881
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
882
|
+
**Never generate tokens in client-side code.** Create a backend endpoint:
|
|
883
|
+
|
|
884
|
+
```javascript
|
|
885
|
+
// Backend (Node.js/Express)
|
|
886
|
+
app.post("/api/get-napster-token", async (req, res) => {
|
|
887
|
+
const response = await fetch(
|
|
888
|
+
"https://management-api.cogcache.com/public/connections",
|
|
889
|
+
{
|
|
890
|
+
method: "POST",
|
|
891
|
+
headers: {
|
|
892
|
+
"Content-Type": "application/json",
|
|
893
|
+
"x-api-key": process.env.NAPSTER_API_KEY,
|
|
894
|
+
},
|
|
895
|
+
body: JSON.stringify({
|
|
896
|
+
companionId: process.env.COMPANION_ID,
|
|
897
|
+
providerConfig: {
|
|
898
|
+
/* your config */
|
|
899
|
+
},
|
|
900
|
+
}),
|
|
901
|
+
}
|
|
902
|
+
);
|
|
903
|
+
const data = await response.json();
|
|
904
|
+
res.json({ token: data.token });
|
|
905
|
+
});
|
|
906
|
+
```
|
|
769
907
|
|
|
770
|
-
|
|
771
|
-
|
|
908
|
+
Then fetch from your backend in the client:
|
|
909
|
+
|
|
910
|
+
```typescript
|
|
911
|
+
const response = await fetch("/api/get-napster-token", { method: "POST" });
|
|
912
|
+
const { token } = await response.json();
|
|
772
913
|
```
|
|
773
914
|
|
|
774
|
-
|
|
775
|
-
|
|
915
|
+
---
|
|
916
|
+
|
|
917
|
+
### Authentication / Token Issues
|
|
918
|
+
|
|
919
|
+
**Error:** `401 Unauthorized` or `Invalid token`
|
|
920
|
+
|
|
921
|
+
**Possible Causes:**
|
|
776
922
|
|
|
777
|
-
|
|
778
|
-
|
|
923
|
+
1. Token expired
|
|
924
|
+
2. Invalid API key
|
|
925
|
+
3. Incorrect companion ID
|
|
779
926
|
|
|
780
|
-
**Problem: Avatar appears unstyled**
|
|
781
927
|
**Solution:**
|
|
782
928
|
|
|
783
|
-
|
|
929
|
+
1. **Verify your API key** at [Napster API Keys page](https://app.cogcache.com/organization/keys)
|
|
930
|
+
2. **Check token expiration** - generate a fresh token
|
|
931
|
+
3. **Verify companion ID** matches your configuration
|
|
932
|
+
4. **Ensure backend endpoint** is working:
|
|
933
|
+
|
|
934
|
+
---
|
|
935
|
+
|
|
936
|
+
### Widget Not Mounting
|
|
937
|
+
|
|
938
|
+
**Error:** Container element not found or widget doesn't appear
|
|
939
|
+
|
|
940
|
+
**Possible Causes:**
|
|
941
|
+
|
|
942
|
+
1. Container element doesn't exist
|
|
943
|
+
2. Invalid CSS selector
|
|
944
|
+
3. Container rendered after initialization
|
|
784
945
|
|
|
785
|
-
**Problem:** `mountContainer` element doesn't exist
|
|
786
946
|
**Solution:**
|
|
787
947
|
|
|
788
948
|
```typescript
|
|
789
|
-
// Wait for DOM
|
|
790
|
-
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
949
|
+
// ✅ Good: Wait for DOM element
|
|
950
|
+
useEffect(() => {
|
|
951
|
+
const container = document.querySelector("#avatar-container");
|
|
952
|
+
if (container) {
|
|
953
|
+
NapsterCompanionApiSdk.init(token, {
|
|
954
|
+
mountContainer: container, // Use DOM element directly
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
}, []);
|
|
958
|
+
|
|
959
|
+
// ❌ Bad: Container may not exist yet
|
|
960
|
+
const widget = NapsterCompanionApiSdk.init(token, {
|
|
961
|
+
mountContainer: "#avatar-container",
|
|
794
962
|
});
|
|
795
963
|
```
|
|
796
964
|
|
|
797
|
-
|
|
965
|
+
---
|
|
798
966
|
|
|
799
|
-
|
|
967
|
+
### Module Not Found
|
|
800
968
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
969
|
+
**Error:** `Cannot find module '@touchcastllc/napster-companion-api'`
|
|
970
|
+
|
|
971
|
+
**Cause:** Package not installed.
|
|
972
|
+
|
|
973
|
+
**Solution:**
|
|
974
|
+
|
|
975
|
+
```bash
|
|
976
|
+
npm install @touchcastllc/napster-companion-api
|
|
977
|
+
# or
|
|
978
|
+
yarn add @touchcastllc/napster-companion-api
|
|
807
979
|
```
|
|
808
980
|
|
|
809
|
-
|
|
981
|
+
---
|
|
810
982
|
|
|
811
|
-
|
|
983
|
+
### Peer Dependency Warnings
|
|
812
984
|
|
|
813
|
-
|
|
814
|
-
- Import only needed features
|
|
815
|
-
- Enable tree-shaking in bundler
|
|
985
|
+
**Warning:** `ERESOLVE unable to resolve dependency tree`
|
|
816
986
|
|
|
817
|
-
|
|
987
|
+
**Cause:** Missing or incompatible peer dependencies.
|
|
818
988
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
989
|
+
**Solution:**
|
|
990
|
+
|
|
991
|
+
Install all required peer dependencies:
|
|
992
|
+
|
|
993
|
+
```bash
|
|
994
|
+
# For React 18+
|
|
995
|
+
npm install react@^18 react-dom@^18 @reduxjs/toolkit@^2 react-redux@^9
|
|
996
|
+
|
|
997
|
+
# For React 16/17
|
|
998
|
+
npm install react@^16 react-dom@^16 @reduxjs/toolkit@^2 react-redux@^8
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
---
|
|
1002
|
+
|
|
1003
|
+
### TypeScript Errors
|
|
1004
|
+
|
|
1005
|
+
**Error:** `Cannot find type definitions`
|
|
1006
|
+
|
|
1007
|
+
**Cause:** TypeScript can't locate type definitions.
|
|
1008
|
+
|
|
1009
|
+
**Solution:**
|
|
1010
|
+
|
|
1011
|
+
The SDK includes TypeScript definitions. Ensure your `tsconfig.json` includes:
|
|
1012
|
+
|
|
1013
|
+
```json
|
|
1014
|
+
{
|
|
1015
|
+
"compilerOptions": {
|
|
1016
|
+
"moduleResolution": "node",
|
|
1017
|
+
"esModuleInterop": true,
|
|
1018
|
+
"skipLibCheck": true
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
826
1021
|
```
|
|
827
1022
|
|
|
828
1023
|
---
|
|
829
1024
|
|
|
830
|
-
##
|
|
1025
|
+
## 📚 Additional Resources
|
|
1026
|
+
|
|
1027
|
+
- **API Keys**: [Generate API Keys](https://app.cogcache.com/organization/keys)
|
|
1028
|
+
- **npm**: [@touchcastllc/napster-companion-api](https://www.npmjs.com/package/@touchcastllc/napster-companion-api)
|
|
1029
|
+
|
|
1030
|
+
---
|
|
1031
|
+
|
|
1032
|
+
## 📝 License
|
|
831
1033
|
|
|
832
|
-
|
|
1034
|
+
MIT License. See [LICENSE](LICENSE) file for details.
|