@twoimpulse/langbox-voice-widget 0.1.1 → 0.1.2-dev.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.
@@ -1,27 +1,27 @@
1
- class PCMProcessor extends AudioWorkletProcessor {
2
- constructor() {
3
- super();
4
- }
5
-
6
- process(inputs, outputs, parameters) {
7
- const input = inputs[0];
8
- if (input.length > 0) {
9
- const float32Buffer = input[0];
10
- const int16Buffer = this.convertFloat32ToInt16(float32Buffer);
11
- this.port.postMessage(int16Buffer);
12
- }
13
- return true;
14
- }
15
-
16
- convertFloat32ToInt16(float32Array) {
17
- const int16Array = new Int16Array(float32Array.length);
18
- for (let i = 0; i < float32Array.length; i++) {
19
- let val = Math.floor(float32Array[i] * 0x7fff);
20
- val = Math.max(-0x8000, Math.min(0x7fff, val));
21
- int16Array[i] = val;
22
- }
23
- return int16Array;
24
- }
25
- }
26
-
27
- registerProcessor("audio-worklet-processor", PCMProcessor);
1
+ class PCMProcessor extends AudioWorkletProcessor {
2
+ constructor() {
3
+ super();
4
+ }
5
+
6
+ process(inputs, outputs, parameters) {
7
+ const input = inputs[0];
8
+ if (input.length > 0) {
9
+ const float32Buffer = input[0];
10
+ const int16Buffer = this.convertFloat32ToInt16(float32Buffer);
11
+ this.port.postMessage(int16Buffer);
12
+ }
13
+ return true;
14
+ }
15
+
16
+ convertFloat32ToInt16(float32Array) {
17
+ const int16Array = new Int16Array(float32Array.length);
18
+ for (let i = 0; i < float32Array.length; i++) {
19
+ let val = Math.floor(float32Array[i] * 0x7fff);
20
+ val = Math.max(-0x8000, Math.min(0x7fff, val));
21
+ int16Array[i] = val;
22
+ }
23
+ return int16Array;
24
+ }
25
+ }
26
+
27
+ registerProcessor("audio-worklet-processor", PCMProcessor);
@@ -1,36 +1,36 @@
1
- class PlaybackWorklet extends AudioWorkletProcessor {
2
- constructor() {
3
- super();
4
- this.port.onmessage = this.handleMessage.bind(this);
5
- this.buffer = [];
6
- }
7
-
8
- handleMessage(event) {
9
- if (event.data === null) {
10
- this.buffer = [];
11
- return;
12
- }
13
- this.buffer.push(...event.data);
14
- }
15
-
16
- process(inputs, outputs, parameters) {
17
- const output = outputs[0];
18
- const channel = output[0];
19
-
20
- if (this.buffer.length > channel.length) {
21
- const toProcess = this.buffer.splice(0, channel.length);
22
- channel.set(toProcess.map((v) => v / 32768));
23
- } else {
24
- channel.set(this.buffer.map((v) => v / 32768));
25
- this.buffer = [];
26
- }
27
-
28
- if (this.buffer.length === 0) {
29
- this.port.postMessage({ empty: true });
30
- }
31
-
32
- return true;
33
- }
34
- }
35
-
36
- registerProcessor("playback-worklet", PlaybackWorklet);
1
+ class PlaybackWorklet extends AudioWorkletProcessor {
2
+ constructor() {
3
+ super();
4
+ this.port.onmessage = this.handleMessage.bind(this);
5
+ this.buffer = [];
6
+ }
7
+
8
+ handleMessage(event) {
9
+ if (event.data === null) {
10
+ this.buffer = [];
11
+ return;
12
+ }
13
+ this.buffer.push(...event.data);
14
+ }
15
+
16
+ process(inputs, outputs, parameters) {
17
+ const output = outputs[0];
18
+ const channel = output[0];
19
+
20
+ if (this.buffer.length > channel.length) {
21
+ const toProcess = this.buffer.splice(0, channel.length);
22
+ channel.set(toProcess.map((v) => v / 32768));
23
+ } else {
24
+ channel.set(this.buffer.map((v) => v / 32768));
25
+ this.buffer = [];
26
+ }
27
+
28
+ if (this.buffer.length === 0) {
29
+ this.port.postMessage({ empty: true });
30
+ }
31
+
32
+ return true;
33
+ }
34
+ }
35
+
36
+ registerProcessor("playback-worklet", PlaybackWorklet);
package/package.json CHANGED
@@ -1,50 +1,50 @@
1
- {
2
- "name": "@twoimpulse/langbox-voice-widget",
3
- "version": "0.1.1",
4
- "type": "module",
5
- "description": "Embeddable voice agent widget for Langbox",
6
- "main": "dist/langbox-widget.js",
7
- "files": [
8
- "dist"
9
- ],
10
- "scripts": {
11
- "dev": "vite",
12
- "build": "tsc --noEmit && vite build",
13
- "preview": "vite preview",
14
- "lint": "eslint src/",
15
- "lint:fix": "eslint src/ --fix",
16
- "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
17
- "typecheck": "tsc --noEmit"
18
- },
19
- "keywords": [
20
- "langbox",
21
- "voice-agent",
22
- "widget",
23
- "embeddable",
24
- "web-component"
25
- ],
26
- "author": "TwoImpulse",
27
- "license": "MIT",
28
- "devDependencies": {
29
- "@types/react": "^19.2.7",
30
- "@types/react-dom": "^19.2.3",
31
- "@typescript-eslint/eslint-plugin": "^8.49.0",
32
- "@typescript-eslint/parser": "^8.49.0",
33
- "@vitejs/plugin-react": "^4.4.1",
34
- "eslint": "^9.39.1",
35
- "eslint-config-prettier": "^10.1.8",
36
- "eslint-plugin-react": "^7.37.5",
37
- "eslint-plugin-react-hooks": "^7.0.1",
38
- "prettier": "^3.5.3",
39
- "sass": "^1.87.0",
40
- "terser": "^5.44.1",
41
- "typescript": "^5.9.3",
42
- "vite": "^6.3.4",
43
- "vite-plugin-css-injected-by-js": "^3.5.2"
44
- },
45
- "dependencies": {
46
- "openai": "^4.77.0",
47
- "react": "^19.1.0",
48
- "react-dom": "^19.1.0"
49
- }
50
- }
1
+ {
2
+ "name": "@twoimpulse/langbox-voice-widget",
3
+ "version": "0.1.2-dev.0",
4
+ "type": "module",
5
+ "description": "Embeddable voice agent widget for Langbox",
6
+ "main": "dist/langbox-widget.js",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "dev": "vite",
12
+ "build": "tsc --noEmit && vite build",
13
+ "preview": "vite preview",
14
+ "lint": "eslint src/",
15
+ "lint:fix": "eslint src/ --fix",
16
+ "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "keywords": [
20
+ "langbox",
21
+ "voice-agent",
22
+ "widget",
23
+ "embeddable",
24
+ "web-component"
25
+ ],
26
+ "author": "TwoImpulse",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "@types/react": "^19.2.7",
30
+ "@types/react-dom": "^19.2.3",
31
+ "@typescript-eslint/eslint-plugin": "^8.49.0",
32
+ "@typescript-eslint/parser": "^8.49.0",
33
+ "@vitejs/plugin-react": "^4.4.1",
34
+ "eslint": "^9.39.1",
35
+ "eslint-config-prettier": "^10.1.8",
36
+ "eslint-plugin-react": "^7.37.5",
37
+ "eslint-plugin-react-hooks": "^7.0.1",
38
+ "prettier": "^3.5.3",
39
+ "sass": "^1.87.0",
40
+ "terser": "^5.44.1",
41
+ "typescript": "^5.9.3",
42
+ "vite": "^6.3.4",
43
+ "vite-plugin-css-injected-by-js": "^3.5.2"
44
+ },
45
+ "dependencies": {
46
+ "openai": "^4.77.0",
47
+ "react": "^19.1.0",
48
+ "react-dom": "^19.1.0"
49
+ }
50
+ }
package/README.md DELETED
@@ -1,331 +0,0 @@
1
- # Langbox Voice Widget
2
-
3
- An embeddable voice agent widget that allows you to integrate Langbox AI voice agents into any website.
4
-
5
- ## Technology Stack Decision
6
-
7
- ### Why Vite + React instead of Angular?
8
-
9
- This widget intentionally uses **Vite + React** instead of Angular (which powers the main Langbox platform). Here's why:
10
-
11
- #### 1. Bundle Size
12
-
13
- | Framework | Minimum Bundle Size |
14
- |-----------|-------------------|
15
- | Angular | ~130KB+ (gzipped) |
16
- | React | ~45KB (gzipped) |
17
- | This Widget | ~30KB (gzipped) |
18
-
19
- For an embeddable widget that will be loaded on third-party websites, every kilobyte matters. A smaller bundle means:
20
- - Faster initial load times
21
- - Lower bandwidth consumption
22
- - Better user experience, especially on slower connections
23
-
24
- #### 2. Loading Performance
25
-
26
- Angular requires bootstrapping an entire application framework, which adds significant overhead:
27
- - Zone.js for change detection
28
- - Dependency injection system
29
- - Module compilation
30
-
31
- React with a simple component tree loads almost instantly, making the widget feel more responsive.
32
-
33
- #### 3. Web Components Compatibility
34
-
35
- This widget uses the **Web Components** standard (Custom Elements) to ensure:
36
- - Framework-agnostic integration
37
- - Shadow DOM isolation (styles don't leak or conflict)
38
- - Works with any website regardless of their tech stack
39
-
40
- While Angular does support building Web Components via Angular Elements, the resulting bundle is significantly larger.
41
-
42
- #### 4. Third-Party Integration
43
-
44
- Websites that embed this widget may already be using:
45
- - React
46
- - Vue
47
- - Angular
48
- - Vanilla JavaScript
49
- - jQuery
50
- - Or any other framework
51
-
52
- A lightweight, self-contained widget avoids:
53
- - Version conflicts
54
- - Framework interference
55
- - CSS style pollution (thanks to Shadow DOM)
56
-
57
- #### 5. Development Speed
58
-
59
- Vite offers:
60
- - Instant hot module replacement (HMR)
61
- - Fast cold starts
62
- - Optimized production builds
63
- - Simple configuration
64
-
65
- This makes iteration and development significantly faster compared to the Angular CLI.
66
-
67
- ## Usage
68
-
69
- ### Embedding the Widget
70
-
71
- Add the following code to your HTML page:
72
-
73
- ```html
74
- <langbox-agent agent-id="your-agent-id"></langbox-agent>
75
- <script src="https://langbox-widget.surge.sh/langbox-widget.js" async></script>
76
- ```
77
-
78
- ### Configuration Options
79
-
80
- | Attribute | Required | Description |
81
- |-----------|----------|-------------|
82
- | `agent-id` | Yes | The unique identifier of your Langbox voice agent |
83
- | `api-url` | No | Custom API URL (defaults to `https://api.langbox.ai`) |
84
-
85
- ### Example
86
-
87
- ```html
88
- <!DOCTYPE html>
89
- <html>
90
- <head>
91
- <title>My Website</title>
92
- </head>
93
- <body>
94
- <h1>Welcome to my website</h1>
95
-
96
- <!-- Langbox Voice Widget -->
97
- <langbox-agent agent-id="agent_abc123xyz"></langbox-agent>
98
- <script src="https://langbox-widget.surge.sh/langbox-widget.js" async></script>
99
- </body>
100
- </html>
101
- ```
102
-
103
- ## Development
104
-
105
- ### Prerequisites
106
-
107
- - Node.js 18+
108
- - npm or yarn
109
-
110
- ### Setup
111
-
112
- ```bash
113
- # Install dependencies
114
- npm install
115
-
116
- # Start development server
117
- npm run dev
118
-
119
- # Build for production
120
- npm run build
121
- ```
122
-
123
- ### Project Structure
124
-
125
- ```
126
- langbox-widget/
127
- ├── src/
128
- │ ├── index.jsx # Web Component entry point
129
- │ ├── components/
130
- │ │ └── VoiceAgentWidget.jsx # Main widget component
131
- │ └── styles/
132
- │ └── widget.scss # Widget styles
133
- ├── dist/ # Production build output
134
- ├── index.html # Development demo page
135
- ├── vite.config.js # Vite configuration
136
- └── package.json
137
- ```
138
-
139
- ## Deployment
140
-
141
- This section explains how to deploy the widget to a CDN so that customers can embed it on their websites using a simple script tag, similar to how ElevenLabs provides their widget.
142
-
143
- ### Build for Production
144
-
145
- First, build the widget for production:
146
-
147
- ```bash
148
- npm run build
149
- ```
150
-
151
- This generates `dist/langbox-widget.js` - a single, minified JavaScript file containing the entire widget (including styles, which are injected via JS).
152
-
153
- ### Publishing to CDN
154
-
155
- #### Option 1: NPM + unpkg (Recommended for Open Source)
156
-
157
- This is the simplest approach and how ElevenLabs distributes their widget.
158
-
159
- 1. **Configure npm for publishing** (first time only):
160
- ```bash
161
- npm login
162
- ```
163
-
164
- 2. **Publish to npm**:
165
- ```bash
166
- # For first publication
167
- npm publish --access public
168
-
169
- # For updates
170
- npm version patch # or minor/major
171
- npm publish
172
- ```
173
-
174
- 3. **Access via unpkg CDN**:
175
- ```
176
- https://unpkg.com/@langbox/voice-widget/dist/langbox-widget.js
177
- ```
178
-
179
- For a specific version:
180
- ```
181
- https://unpkg.com/@langbox/voice-widget@0.1.0/dist/langbox-widget.js
182
- ```
183
-
184
- For the latest version (with cache):
185
- ```
186
- https://unpkg.com/@langbox/voice-widget@latest/dist/langbox-widget.js
187
- ```
188
-
189
- 4. **Customers embed with**:
190
- ```html
191
- <langbox-agent agent-id="their-agent-id"></langbox-agent>
192
- <script src="https://unpkg.com/@langbox/voice-widget" async></script>
193
- ```
194
-
195
- #### Option 2: jsDelivr CDN
196
-
197
- jsDelivr automatically mirrors npm packages and provides better global CDN coverage:
198
-
199
- ```
200
- https://cdn.jsdelivr.net/npm/@langbox/voice-widget/dist/langbox-widget.js
201
- ```
202
-
203
- Benefits:
204
- - Automatic minification
205
- - Better caching
206
- - Multi-CDN infrastructure
207
- - China network support
208
-
209
- #### Option 3: Surge.sh (Current Setup)
210
-
211
- Surge.sh provides free static hosting with a simple CLI:
212
-
213
- 1. **Install Surge CLI** (first time only):
214
- ```bash
215
- npm install -g surge
216
- ```
217
-
218
- 2. **Build and deploy**:
219
- ```bash
220
- npm run build
221
- cd dist
222
- surge . langbox-widget.surge.sh
223
- ```
224
-
225
- 3. **Customers embed with**:
226
- ```html
227
- <langbox-agent agent-id="their-agent-id"></langbox-agent>
228
- <script src="https://langbox-widget.surge.sh/langbox-widget.js" async></script>
229
- ```
230
-
231
- Benefits:
232
- - Free hosting
233
- - Simple CLI deployment
234
- - Automatic SSL
235
- - No configuration needed
236
-
237
- #### Option 4: Azure Blob Storage + CDN
238
-
239
- 1. **Create storage account and container**:
240
- ```bash
241
- az storage account create --name langboxcdn --resource-group langbox
242
- az storage container create --name widget --account-name langboxcdn --public-access blob
243
- ```
244
-
245
- 2. **Upload the widget**:
246
- ```bash
247
- az storage blob upload \
248
- --account-name langboxcdn \
249
- --container-name widget \
250
- --name langbox-widget.js \
251
- --file dist/langbox-widget.js \
252
- --content-type "application/javascript"
253
- ```
254
-
255
- 3. **Enable Azure CDN** on the storage account.
256
-
257
- ### Versioning Strategy
258
-
259
- For production use, we recommend:
260
-
261
- 1. **Use semantic versioning**: `major.minor.patch`
262
- 2. **Provide a "latest" endpoint** for customers who want auto-updates
263
- 3. **Provide versioned endpoints** for customers who want stability
264
-
265
- Example URL structure:
266
- ```
267
- https://cdn.langbox.ai/widget/v1/langbox-widget.js # Latest v1.x.x
268
- https://cdn.langbox.ai/widget/v1.0.0/langbox-widget.js # Specific version
269
- https://cdn.langbox.ai/widget/latest/langbox-widget.js # Always latest
270
- ```
271
-
272
- ### CI/CD Pipeline
273
-
274
- Automate deployments with GitHub Actions:
275
-
276
- ```yaml
277
- # .github/workflows/deploy.yml
278
- name: Deploy Widget
279
-
280
- on:
281
- release:
282
- types: [published]
283
-
284
- jobs:
285
- deploy:
286
- runs-on: ubuntu-latest
287
- steps:
288
- - uses: actions/checkout@v4
289
-
290
- - uses: actions/setup-node@v4
291
- with:
292
- node-version: '18'
293
- registry-url: 'https://registry.npmjs.org'
294
-
295
- - run: npm ci
296
- - run: npm run build
297
-
298
- - run: npm publish --access public
299
- env:
300
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
301
- ```
302
-
303
- ### Testing the Deployment
304
-
305
- After deployment, test the widget on a sample HTML page:
306
-
307
- ```html
308
- <!DOCTYPE html>
309
- <html>
310
- <head>
311
- <title>Widget Test</title>
312
- </head>
313
- <body>
314
- <h1>Testing Langbox Widget</h1>
315
-
316
- <langbox-agent agent-id="test-agent-id"></langbox-agent>
317
- <script src="YOUR_CDN_URL/langbox-widget.js" async></script>
318
-
319
- <script>
320
- // Verify the custom element is registered
321
- customElements.whenDefined('langbox-agent').then(() => {
322
- console.log('Langbox widget loaded successfully!');
323
- });
324
- </script>
325
- </body>
326
- </html>
327
- ```
328
-
329
- ## License
330
-
331
- MIT