@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.
- package/dist/worklets/audio-worklet-processor.js +27 -27
- package/dist/worklets/playback-worklet.js +36 -36
- package/package.json +50 -50
- package/README.md +0 -331
|
@@ -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.
|
|
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
|