ttp-agent-sdk 2.1.12 → 2.2.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 +2 -1
- package/dist/agent-widget.js +1 -1
- package/dist/agent-widget.js.map +1 -1
- package/dist/examples/test-agent-app.html +779 -38
- package/dist/examples/test.html +16 -0
- package/dist/index.html +1 -1
- package/examples/test-agent-app.html +779 -38
- package/examples/test.html +16 -0
- package/package.json +1 -1
|
@@ -84,6 +84,117 @@
|
|
|
84
84
|
font-size: 12px;
|
|
85
85
|
overflow-x: auto;
|
|
86
86
|
margin: 20px 0;
|
|
87
|
+
white-space: pre;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.code-block pre {
|
|
91
|
+
margin: 0;
|
|
92
|
+
font-family: inherit;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.code-block code {
|
|
96
|
+
font-family: inherit;
|
|
97
|
+
white-space: pre;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.tabs {
|
|
101
|
+
display: flex;
|
|
102
|
+
gap: 10px;
|
|
103
|
+
margin-bottom: 15px;
|
|
104
|
+
border-bottom: 2px solid #E5E7EB;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.tab {
|
|
108
|
+
background: none;
|
|
109
|
+
border: none;
|
|
110
|
+
padding: 12px 24px;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
font-size: 14px;
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
color: #6B7280;
|
|
115
|
+
border-bottom: 3px solid transparent;
|
|
116
|
+
transition: all 0.2s;
|
|
117
|
+
margin-bottom: -2px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.tab:hover {
|
|
121
|
+
color: #4F46E5;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.tab.active {
|
|
125
|
+
color: #4F46E5;
|
|
126
|
+
border-bottom-color: #4F46E5;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.tab-content {
|
|
130
|
+
display: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.tab-content.active {
|
|
134
|
+
display: block;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.controls-panel {
|
|
138
|
+
background: white;
|
|
139
|
+
border: 2px solid #E5E7EB;
|
|
140
|
+
border-radius: 8px;
|
|
141
|
+
padding: 20px;
|
|
142
|
+
margin: 20px 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.control-row {
|
|
146
|
+
display: grid;
|
|
147
|
+
grid-template-columns: 150px 150px 1fr auto;
|
|
148
|
+
gap: 12px;
|
|
149
|
+
align-items: center;
|
|
150
|
+
margin-bottom: 12px;
|
|
151
|
+
padding: 12px;
|
|
152
|
+
background: #F9FAFB;
|
|
153
|
+
border-radius: 6px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.control-row label {
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
color: #374151;
|
|
159
|
+
font-size: 13px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.control-row select,
|
|
163
|
+
.control-row input[type="text"],
|
|
164
|
+
.control-row input[type="color"] {
|
|
165
|
+
padding: 8px 12px;
|
|
166
|
+
border: 1px solid #D1D5DB;
|
|
167
|
+
border-radius: 4px;
|
|
168
|
+
font-size: 14px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.control-row input[type="color"] {
|
|
172
|
+
width: 60px;
|
|
173
|
+
height: 40px;
|
|
174
|
+
cursor: pointer;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.control-row button {
|
|
178
|
+
padding: 8px 16px;
|
|
179
|
+
background: #3B82F6;
|
|
180
|
+
color: white;
|
|
181
|
+
border: none;
|
|
182
|
+
border-radius: 4px;
|
|
183
|
+
cursor: pointer;
|
|
184
|
+
font-size: 13px;
|
|
185
|
+
margin: 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.control-row button:hover {
|
|
189
|
+
background: #2563EB;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.control-row button.remove {
|
|
193
|
+
background: #EF4444;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.control-row button.remove:hover {
|
|
197
|
+
background: #DC2626;
|
|
87
198
|
}
|
|
88
199
|
</style>
|
|
89
200
|
</head>
|
|
@@ -105,7 +216,7 @@
|
|
|
105
216
|
<ol style="margin: 10px 0 0 20px; line-height: 1.8;">
|
|
106
217
|
<li>Look for the floating voice button in the bottom-right corner</li>
|
|
107
218
|
<li>Click the button to open the widget</li>
|
|
108
|
-
<li>Click the
|
|
219
|
+
<li>Click the voice button to start (will request microphone permission)</li>
|
|
109
220
|
<li>Widget will connect directly using agent ID + app ID</li>
|
|
110
221
|
<li>Check the status updates below</li>
|
|
111
222
|
</ol>
|
|
@@ -122,28 +233,245 @@
|
|
|
122
233
|
</div>
|
|
123
234
|
|
|
124
235
|
<h2>Implementation Code:</h2>
|
|
125
|
-
<div class="
|
|
126
|
-
|
|
236
|
+
<div class="tabs">
|
|
237
|
+
<button class="tab active" onclick="switchTab('simple')">Simple</button>
|
|
238
|
+
<button class="tab" onclick="switchTab('advanced')">All Options</button>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div id="simple-tab" class="tab-content active">
|
|
242
|
+
<div class="code-block"><pre><code>const widget = new TTPAgentSDK.AgentWidget({
|
|
243
|
+
agentId: 'agent_87c4a55a1',
|
|
244
|
+
appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
|
|
245
|
+
|
|
246
|
+
primaryColor: '#10B981',
|
|
247
|
+
position: 'bottom-right',
|
|
248
|
+
|
|
249
|
+
// Icon configuration
|
|
250
|
+
icon: {
|
|
251
|
+
type: 'custom',
|
|
252
|
+
customImage: 'https://talktopc.com/logo192.png',
|
|
253
|
+
size: 'medium'
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// Floating button (main button) colors - WHITE
|
|
257
|
+
button: {
|
|
258
|
+
backgroundColor: '#FFFFFF', // White floating button
|
|
259
|
+
hoverColor: '#E5E7EB' // Gray on hover
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
// Header (top of panel) colors - LIGHT PURPLE
|
|
263
|
+
header: {
|
|
264
|
+
title: 'TTP Support',
|
|
265
|
+
backgroundColor: '#A78BFA', // Light purple header
|
|
266
|
+
textColor: '#FFFFFF'
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
// Mic button (inside panel) colors - GRAY
|
|
270
|
+
panel: {
|
|
271
|
+
micButtonColor: '#E5E7EB', // Gray mic button
|
|
272
|
+
micButtonActiveColor: '#EF4444' // Red when active
|
|
273
|
+
}
|
|
274
|
+
});</code></pre></div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<h2>🎨 Interactive Customization</h2>
|
|
278
|
+
<div class="controls-panel">
|
|
279
|
+
<div style="margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid #E5E7EB;">
|
|
280
|
+
<strong style="color: #374151;">Customize widget colors and properties in real-time:</strong>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<div id="control-rows">
|
|
284
|
+
<!-- Control rows will be added dynamically -->
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
<button id="add-control-btn" style="margin-top: 12px; padding: 10px 20px; background: #10B981;">
|
|
288
|
+
+ Add Customization
|
|
289
|
+
</button>
|
|
290
|
+
|
|
291
|
+
<div style="margin-top: 16px; padding-top: 16px; border-top: 1px solid #E5E7EB;">
|
|
292
|
+
<button id="reset-defaults-btn" style="background: #6B7280;">
|
|
293
|
+
Reset to Defaults
|
|
294
|
+
</button>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<h2>Implementation Code:</h2>
|
|
299
|
+
<div class="tabs">
|
|
300
|
+
<button class="tab active" onclick="switchTab('simple')">Simple</button>
|
|
301
|
+
<button class="tab" onclick="switchTab('advanced')">All Options</button>
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<div id="advanced-tab" class="tab-content">
|
|
305
|
+
<div class="code-block"><pre><code>const widget = new TTPAgentSDK.AgentWidget({
|
|
306
|
+
// Required
|
|
127
307
|
agentId: 'agent_87c4a55a1',
|
|
128
308
|
appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
|
|
129
309
|
|
|
130
|
-
//
|
|
131
|
-
getSessionUrl: async ({ agentId, appId, variables }) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
310
|
+
// Optional: getSessionUrl - only needed for signed links or custom URL logic
|
|
311
|
+
// getSessionUrl: async ({ agentId, appId, variables }) => {
|
|
312
|
+
// const response = await fetch('https://your-backend.com/api/signed-url', {
|
|
313
|
+
// method: 'POST',
|
|
314
|
+
// headers: { 'Content-Type': 'application/json' },
|
|
315
|
+
// body: JSON.stringify({ agentId, appId, variables })
|
|
316
|
+
// });
|
|
317
|
+
// const data = await response.json();
|
|
318
|
+
// return data.signedUrl;
|
|
319
|
+
// },
|
|
320
|
+
|
|
321
|
+
// Simple styling (backward compatible)
|
|
322
|
+
primaryColor: '#10B981',
|
|
323
|
+
position: 'bottom-right', // or use advanced position object below
|
|
324
|
+
|
|
325
|
+
// Icon Configuration
|
|
326
|
+
icon: {
|
|
327
|
+
type: 'custom', // 'microphone', 'custom', 'emoji', 'text'
|
|
328
|
+
customImage: 'https://talktopc.com/logo192.png', // For type: 'custom'
|
|
329
|
+
size: 'medium' // 'small', 'medium', 'large', 'xl'
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
// Button Configuration - Floating Button (Main Button) - WHITE
|
|
333
|
+
button: {
|
|
334
|
+
size: 'medium', // 'small', 'medium', 'large', 'xl'
|
|
335
|
+
shape: 'circle', // 'circle', 'square', 'rounded'
|
|
336
|
+
backgroundColor: '#FFFFFF', // Floating button background color (WHITE)
|
|
337
|
+
hoverColor: '#E5E7EB', // Floating button hover color (gray)
|
|
338
|
+
shadow: true,
|
|
339
|
+
shadowColor: 'rgba(0,0,0,0.15)'
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
// Header Configuration - Top of Panel - LIGHT PURPLE
|
|
343
|
+
header: {
|
|
344
|
+
title: 'TTP Support', // Custom title for the widget header
|
|
345
|
+
showTitle: true, // Show/hide title
|
|
346
|
+
backgroundColor: '#A78BFA', // Header background color (LIGHT PURPLE)
|
|
347
|
+
textColor: '#FFFFFF', // Header text color
|
|
348
|
+
showCloseButton: true
|
|
135
349
|
},
|
|
136
350
|
|
|
351
|
+
// Panel Configuration - Includes Mic Button Colors - GRAY
|
|
352
|
+
panel: {
|
|
353
|
+
width: 350,
|
|
354
|
+
height: 500,
|
|
355
|
+
borderRadius: 12,
|
|
356
|
+
backgroundColor: 'rgba(255,255,255,0.95)', // Panel background
|
|
357
|
+
backdropFilter: null,
|
|
358
|
+
border: '1px solid rgba(0,0,0,0.1)',
|
|
359
|
+
micButtonColor: '#E5E7EB', // Mic button (inside panel) background color (GRAY)
|
|
360
|
+
micButtonActiveColor: '#EF4444' // Mic button color when active/recording (RED)
|
|
361
|
+
},
|
|
362
|
+
// icon: {
|
|
363
|
+
// type: 'microphone',
|
|
364
|
+
// emoji: '🎤', // For type: 'emoji'
|
|
365
|
+
// text: 'AI', // For type: 'text'
|
|
366
|
+
// size: 'medium'
|
|
367
|
+
// },
|
|
368
|
+
|
|
369
|
+
// Advanced Positioning
|
|
370
|
+
// position: {
|
|
371
|
+
// vertical: 'bottom', // 'top', 'bottom', 'center'
|
|
372
|
+
// horizontal: 'right', // 'left', 'right', 'center'
|
|
373
|
+
// offset: { x: 20, y: 20 } // Custom offset in pixels
|
|
374
|
+
// },
|
|
375
|
+
|
|
376
|
+
// Button Configuration
|
|
377
|
+
// button: {
|
|
378
|
+
// size: 'medium', // 'small', 'medium', 'large', 'xl'
|
|
379
|
+
// shape: 'circle', // 'circle', 'square', 'rounded'
|
|
380
|
+
// primaryColor: '#10B981',
|
|
381
|
+
// hoverColor: '#059669',
|
|
382
|
+
// activeColor: '#EF4444',
|
|
383
|
+
// shadow: true,
|
|
384
|
+
// shadowColor: 'rgba(0,0,0,0.15)'
|
|
385
|
+
// },
|
|
386
|
+
|
|
387
|
+
// Panel Configuration
|
|
388
|
+
// panel: {
|
|
389
|
+
// width: 350,
|
|
390
|
+
// height: 500,
|
|
391
|
+
// borderRadius: 12,
|
|
392
|
+
// backgroundColor: 'rgba(255,255,255,0.95)',
|
|
393
|
+
// backdropFilter: 'blur(10px)', // Glass morphism effect
|
|
394
|
+
// border: '1px solid rgba(0,0,0,0.1)'
|
|
395
|
+
// },
|
|
396
|
+
|
|
397
|
+
// Header Configuration
|
|
398
|
+
// header: {
|
|
399
|
+
// title: 'Voice Assistant',
|
|
400
|
+
// showTitle: true,
|
|
401
|
+
// backgroundColor: null, // Uses button primaryColor if null
|
|
402
|
+
// textColor: '#FFFFFF',
|
|
403
|
+
// showCloseButton: true
|
|
404
|
+
// },
|
|
405
|
+
|
|
406
|
+
// Messages Configuration
|
|
407
|
+
// messages: {
|
|
408
|
+
// userBackgroundColor: '#E5E7EB',
|
|
409
|
+
// agentBackgroundColor: '#F3F4F6',
|
|
410
|
+
// systemBackgroundColor: '#DCFCE7',
|
|
411
|
+
// errorBackgroundColor: '#FEE2E2',
|
|
412
|
+
// textColor: '#1F2937',
|
|
413
|
+
// fontSize: '14px',
|
|
414
|
+
// borderRadius: 8
|
|
415
|
+
// },
|
|
416
|
+
|
|
417
|
+
// Animation Configuration
|
|
418
|
+
// animation: {
|
|
419
|
+
// enableHover: true,
|
|
420
|
+
// enablePulse: true,
|
|
421
|
+
// enableSlide: true,
|
|
422
|
+
// duration: 0.3
|
|
423
|
+
// },
|
|
424
|
+
|
|
425
|
+
// Behavior Configuration
|
|
426
|
+
// behavior: {
|
|
427
|
+
// autoOpen: false, // Automatically open panel on load
|
|
428
|
+
// autoConnect: false, // Automatically connect on load
|
|
429
|
+
// showWelcomeMessage: true,
|
|
430
|
+
// welcomeMessage: 'Hello! How can I help you today?'
|
|
431
|
+
// },
|
|
432
|
+
|
|
433
|
+
// Accessibility Configuration
|
|
434
|
+
// accessibility: {
|
|
435
|
+
// ariaLabel: 'Voice Assistant',
|
|
436
|
+
// ariaDescription: 'Click to open voice assistant',
|
|
437
|
+
// keyboardNavigation: true // ESC key to close
|
|
438
|
+
// },
|
|
439
|
+
|
|
440
|
+
// Custom CSS
|
|
441
|
+
// customStyles: `
|
|
442
|
+
// #agent-widget {
|
|
443
|
+
// /* Your custom CSS here */
|
|
444
|
+
// }
|
|
445
|
+
// `,
|
|
446
|
+
|
|
447
|
+
// Variables for your agent/backend
|
|
137
448
|
variables: {
|
|
138
449
|
testMode: true,
|
|
139
450
|
userName: 'Test User',
|
|
140
451
|
page: 'test-agent-app.html'
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
primaryColor: '#10B981',
|
|
144
|
-
position: 'bottom-right'
|
|
145
|
-
});
|
|
452
|
+
}
|
|
453
|
+
});</code></pre></div>
|
|
146
454
|
</div>
|
|
455
|
+
|
|
456
|
+
<script>
|
|
457
|
+
function switchTab(tabName) {
|
|
458
|
+
// Hide all tab contents
|
|
459
|
+
document.querySelectorAll('.tab-content').forEach(content => {
|
|
460
|
+
content.classList.remove('active');
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Remove active class from all tabs
|
|
464
|
+
document.querySelectorAll('.tab').forEach(tab => {
|
|
465
|
+
tab.classList.remove('active');
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Show selected tab content
|
|
469
|
+
document.getElementById(tabName + '-tab').classList.add('active');
|
|
470
|
+
|
|
471
|
+
// Add active class to clicked tab
|
|
472
|
+
event.target.classList.add('active');
|
|
473
|
+
}
|
|
474
|
+
</script>
|
|
147
475
|
|
|
148
476
|
<h2>Manual Tests:</h2>
|
|
149
477
|
<button onclick="testMicButton()">Test Mic Button Click</button>
|
|
@@ -185,6 +513,368 @@ const widget = new TTPAgentSDK.AgentWidget({
|
|
|
185
513
|
statusDiv.className = 'status ' + type;
|
|
186
514
|
}
|
|
187
515
|
|
|
516
|
+
// ============================================
|
|
517
|
+
// CUSTOMIZATION CONTROLS - Declare early
|
|
518
|
+
// ============================================
|
|
519
|
+
// Initialize variable first - MUST be declared before any functions that use it
|
|
520
|
+
let controlRowCount = 0;
|
|
521
|
+
|
|
522
|
+
const componentOptions = {
|
|
523
|
+
'button': {
|
|
524
|
+
label: 'Floating Button',
|
|
525
|
+
properties: {
|
|
526
|
+
'backgroundColor': { type: 'color', label: 'Background Color' },
|
|
527
|
+
'hoverColor': { type: 'color', label: 'Hover Color' },
|
|
528
|
+
'size': { type: 'select', label: 'Size', options: ['small', 'medium', 'large', 'xl'] },
|
|
529
|
+
'shape': { type: 'select', label: 'Shape', options: ['circle', 'square', 'rounded'] }
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
'header': {
|
|
533
|
+
label: 'Header',
|
|
534
|
+
properties: {
|
|
535
|
+
'backgroundColor': { type: 'color', label: 'Background Color' },
|
|
536
|
+
'textColor': { type: 'color', label: 'Text Color' },
|
|
537
|
+
'title': { type: 'text', label: 'Title' }
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
'panel': {
|
|
541
|
+
label: 'Mic Button',
|
|
542
|
+
properties: {
|
|
543
|
+
'micButtonColor': { type: 'color', label: 'Background Color' },
|
|
544
|
+
'micButtonActiveColor': { type: 'color', label: 'Active Color (Recording)' }
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
'micButtonHint': {
|
|
548
|
+
label: 'Mic Button Hint',
|
|
549
|
+
properties: {
|
|
550
|
+
'micButtonHint.text': { type: 'text', label: 'Hint Text (empty to hide)' },
|
|
551
|
+
'micButtonHint.color': { type: 'color', label: 'Text Color' },
|
|
552
|
+
'micButtonHint.fontSize': { type: 'text', label: 'Font Size (e.g., 12px)' }
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
'direction': {
|
|
556
|
+
label: 'Text Direction',
|
|
557
|
+
properties: {
|
|
558
|
+
'direction': { type: 'select', label: 'Direction', options: ['ltr', 'rtl'] }
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
function initializeCustomizationControls() {
|
|
564
|
+
// Add one initial row
|
|
565
|
+
addControlRow();
|
|
566
|
+
|
|
567
|
+
// Set up button event listeners
|
|
568
|
+
const addBtn = document.getElementById('add-control-btn');
|
|
569
|
+
const resetBtn = document.getElementById('reset-defaults-btn');
|
|
570
|
+
if (addBtn) {
|
|
571
|
+
addBtn.addEventListener('click', addControlRow);
|
|
572
|
+
}
|
|
573
|
+
if (resetBtn) {
|
|
574
|
+
resetBtn.addEventListener('click', resetToDefaults);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function addControlRow() {
|
|
579
|
+
controlRowCount++;
|
|
580
|
+
const controlRows = document.getElementById('control-rows');
|
|
581
|
+
const row = document.createElement('div');
|
|
582
|
+
row.className = 'control-row';
|
|
583
|
+
row.id = `control-row-${controlRowCount}`;
|
|
584
|
+
|
|
585
|
+
// Component selector
|
|
586
|
+
const componentSelect = document.createElement('select');
|
|
587
|
+
componentSelect.id = `component-${controlRowCount}`;
|
|
588
|
+
componentSelect.innerHTML = '<option value="">Select Component</option>';
|
|
589
|
+
Object.keys(componentOptions).forEach(key => {
|
|
590
|
+
const option = document.createElement('option');
|
|
591
|
+
option.value = key;
|
|
592
|
+
option.textContent = componentOptions[key].label;
|
|
593
|
+
componentSelect.appendChild(option);
|
|
594
|
+
});
|
|
595
|
+
componentSelect.onchange = () => updatePropertySelect(controlRowCount);
|
|
596
|
+
|
|
597
|
+
// Property selector (will be populated based on component)
|
|
598
|
+
const propertySelect = document.createElement('select');
|
|
599
|
+
propertySelect.id = `property-${controlRowCount}`;
|
|
600
|
+
propertySelect.innerHTML = '<option value="">Select Property</option>';
|
|
601
|
+
|
|
602
|
+
// Value input (dynamically changes based on property type)
|
|
603
|
+
const valueContainer = document.createElement('div');
|
|
604
|
+
valueContainer.id = `value-container-${controlRowCount}`;
|
|
605
|
+
valueContainer.style.display = 'none';
|
|
606
|
+
|
|
607
|
+
// Action buttons
|
|
608
|
+
const actionContainer = document.createElement('div');
|
|
609
|
+
actionContainer.style.display = 'flex';
|
|
610
|
+
actionContainer.style.gap = '8px';
|
|
611
|
+
|
|
612
|
+
const applyBtn = document.createElement('button');
|
|
613
|
+
applyBtn.textContent = 'Apply';
|
|
614
|
+
applyBtn.onclick = () => applyChange(controlRowCount);
|
|
615
|
+
|
|
616
|
+
const removeBtn = document.createElement('button');
|
|
617
|
+
removeBtn.textContent = 'Remove';
|
|
618
|
+
removeBtn.className = 'remove';
|
|
619
|
+
removeBtn.onclick = () => removeControlRow(controlRowCount);
|
|
620
|
+
|
|
621
|
+
actionContainer.appendChild(applyBtn);
|
|
622
|
+
actionContainer.appendChild(removeBtn);
|
|
623
|
+
|
|
624
|
+
row.appendChild(componentSelect);
|
|
625
|
+
row.appendChild(propertySelect);
|
|
626
|
+
row.appendChild(valueContainer);
|
|
627
|
+
row.appendChild(actionContainer);
|
|
628
|
+
|
|
629
|
+
controlRows.appendChild(row);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function updatePropertySelect(rowId) {
|
|
633
|
+
const componentSelect = document.getElementById(`component-${rowId}`);
|
|
634
|
+
const propertySelect = document.getElementById(`property-${rowId}`);
|
|
635
|
+
const valueContainer = document.getElementById(`value-container-${rowId}`);
|
|
636
|
+
|
|
637
|
+
const component = componentSelect.value;
|
|
638
|
+
if (!component) {
|
|
639
|
+
propertySelect.innerHTML = '<option value="">Select Property</option>';
|
|
640
|
+
valueContainer.style.display = 'none';
|
|
641
|
+
valueContainer.innerHTML = '';
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Populate properties
|
|
646
|
+
propertySelect.innerHTML = '<option value="">Select Property</option>';
|
|
647
|
+
const properties = componentOptions[component].properties;
|
|
648
|
+
Object.keys(properties).forEach(key => {
|
|
649
|
+
const option = document.createElement('option');
|
|
650
|
+
option.value = key;
|
|
651
|
+
option.textContent = properties[key].label;
|
|
652
|
+
propertySelect.appendChild(option);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
propertySelect.onchange = () => updateValueInput(rowId);
|
|
656
|
+
valueContainer.style.display = 'none';
|
|
657
|
+
valueContainer.innerHTML = '';
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function updateValueInput(rowId) {
|
|
661
|
+
const componentSelect = document.getElementById(`component-${rowId}`);
|
|
662
|
+
const propertySelect = document.getElementById(`property-${rowId}`);
|
|
663
|
+
const valueContainer = document.getElementById(`value-container-${rowId}`);
|
|
664
|
+
|
|
665
|
+
const component = componentSelect.value;
|
|
666
|
+
const property = propertySelect.value;
|
|
667
|
+
|
|
668
|
+
if (!component || !property) {
|
|
669
|
+
valueContainer.style.display = 'none';
|
|
670
|
+
valueContainer.innerHTML = '';
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const propConfig = componentOptions[component].properties[property];
|
|
675
|
+
valueContainer.innerHTML = '';
|
|
676
|
+
|
|
677
|
+
if (propConfig.type === 'color') {
|
|
678
|
+
const input = document.createElement('input');
|
|
679
|
+
input.type = 'color';
|
|
680
|
+
input.id = `value-${rowId}`;
|
|
681
|
+
// Set current value from widget
|
|
682
|
+
const currentValue = getCurrentValue(component, property);
|
|
683
|
+
if (currentValue) input.value = rgbToHex(currentValue) || currentValue;
|
|
684
|
+
valueContainer.appendChild(input);
|
|
685
|
+
} else if (propConfig.type === 'select') {
|
|
686
|
+
const select = document.createElement('select');
|
|
687
|
+
select.id = `value-${rowId}`;
|
|
688
|
+
propConfig.options.forEach(opt => {
|
|
689
|
+
const option = document.createElement('option');
|
|
690
|
+
option.value = opt;
|
|
691
|
+
option.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);
|
|
692
|
+
select.appendChild(option);
|
|
693
|
+
});
|
|
694
|
+
// Set current value
|
|
695
|
+
const currentValue = getCurrentValue(component, property);
|
|
696
|
+
if (currentValue !== null && currentValue !== undefined) {
|
|
697
|
+
select.value = currentValue.toString();
|
|
698
|
+
}
|
|
699
|
+
valueContainer.appendChild(select);
|
|
700
|
+
} else if (propConfig.type === 'text') {
|
|
701
|
+
const input = document.createElement('input');
|
|
702
|
+
input.type = 'text';
|
|
703
|
+
input.id = `value-${rowId}`;
|
|
704
|
+
input.placeholder = 'Enter value';
|
|
705
|
+
// Set current value
|
|
706
|
+
const currentValue = getCurrentValue(component, property);
|
|
707
|
+
if (currentValue !== null && currentValue !== undefined) {
|
|
708
|
+
input.value = currentValue;
|
|
709
|
+
}
|
|
710
|
+
valueContainer.appendChild(input);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
valueContainer.style.display = 'block';
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function getCurrentValue(component, property) {
|
|
717
|
+
if (!window.testWidget) return null;
|
|
718
|
+
const config = window.testWidget.config;
|
|
719
|
+
|
|
720
|
+
if (component === 'button' && config.button) {
|
|
721
|
+
return config.button[property];
|
|
722
|
+
} else if (component === 'header' && config.header) {
|
|
723
|
+
return config.header[property];
|
|
724
|
+
} else if (component === 'panel' && config.panel) {
|
|
725
|
+
return config.panel[property];
|
|
726
|
+
} else if (component === 'micButtonHint' && config.panel?.micButtonHint) {
|
|
727
|
+
// Handle nested micButtonHint properties
|
|
728
|
+
const hintKey = property.split('.')[1]; // e.g., 'text', 'color', 'fontSize'
|
|
729
|
+
const value = config.panel.micButtonHint[hintKey];
|
|
730
|
+
return value;
|
|
731
|
+
} else if (component === 'direction') {
|
|
732
|
+
return config.direction;
|
|
733
|
+
}
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function applyChange(rowId) {
|
|
738
|
+
const componentSelect = document.getElementById(`component-${rowId}`);
|
|
739
|
+
const propertySelect = document.getElementById(`property-${rowId}`);
|
|
740
|
+
const valueInput = document.getElementById(`value-${rowId}`);
|
|
741
|
+
|
|
742
|
+
const component = componentSelect.value;
|
|
743
|
+
const property = propertySelect.value;
|
|
744
|
+
let value = valueInput.value;
|
|
745
|
+
|
|
746
|
+
if (!component || !property) {
|
|
747
|
+
alert('Please select component and property');
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Allow empty value for text fields (to hide hint)
|
|
752
|
+
if (value === undefined || value === null) {
|
|
753
|
+
value = '';
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (!window.testWidget) {
|
|
757
|
+
alert('Widget not initialized');
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Handle boolean values (only for non-empty values)
|
|
762
|
+
if (value && value !== '') {
|
|
763
|
+
if (value === 'true') value = true;
|
|
764
|
+
if (value === 'false') value = false;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Prepare update config
|
|
768
|
+
const updateConfig = {};
|
|
769
|
+
if (component === 'button') {
|
|
770
|
+
updateConfig.button = { [property]: value };
|
|
771
|
+
} else if (component === 'header') {
|
|
772
|
+
updateConfig.header = { [property]: value };
|
|
773
|
+
} else if (component === 'panel') {
|
|
774
|
+
updateConfig.panel = { [property]: value };
|
|
775
|
+
} else if (component === 'micButtonHint') {
|
|
776
|
+
// Handle nested micButtonHint properties
|
|
777
|
+
const hintKey = property.split('.')[1]; // e.g., 'text', 'color', 'fontSize', 'show'
|
|
778
|
+
if (!updateConfig.panel) updateConfig.panel = {};
|
|
779
|
+
if (!updateConfig.panel.micButtonHint) updateConfig.panel.micButtonHint = {};
|
|
780
|
+
updateConfig.panel.micButtonHint[hintKey] = value;
|
|
781
|
+
} else if (component === 'direction') {
|
|
782
|
+
updateConfig.direction = value;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Apply update
|
|
786
|
+
console.log(`Applying change: ${component}.${property} = ${value}`);
|
|
787
|
+
window.testWidget.updateConfig(updateConfig);
|
|
788
|
+
|
|
789
|
+
// Update the displayed code blocks
|
|
790
|
+
updateCodeDisplay();
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function removeControlRow(rowId) {
|
|
794
|
+
const row = document.getElementById(`control-row-${rowId}`);
|
|
795
|
+
if (row) row.remove();
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function resetToDefaults() {
|
|
799
|
+
if (!window.testWidget || !window.defaultConfig) {
|
|
800
|
+
alert('Widget or defaults not available');
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (confirm('Reset widget to default configuration?')) {
|
|
805
|
+
window.testWidget.updateConfig(window.defaultConfig);
|
|
806
|
+
updateCodeDisplay();
|
|
807
|
+
|
|
808
|
+
// Clear all control rows
|
|
809
|
+
const controlRows = document.getElementById('control-rows');
|
|
810
|
+
controlRows.innerHTML = '';
|
|
811
|
+
controlRowCount = 0;
|
|
812
|
+
addControlRow();
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function updateCodeDisplay() {
|
|
817
|
+
if (!window.testWidget) return;
|
|
818
|
+
const config = window.testWidget.config;
|
|
819
|
+
|
|
820
|
+
// Helper to safely get position value
|
|
821
|
+
function getPositionValue() {
|
|
822
|
+
if (!config.position) return 'bottom-right';
|
|
823
|
+
if (typeof config.position === 'string') return config.position;
|
|
824
|
+
// If it's an object, extract the string representation
|
|
825
|
+
return `${config.position.vertical || 'bottom'}-${config.position.horizontal || 'right'}`;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Update simple tab
|
|
829
|
+
const simpleCode = document.querySelector('#simple-tab code');
|
|
830
|
+
if (simpleCode) {
|
|
831
|
+
const position = getPositionValue();
|
|
832
|
+
simpleCode.textContent = `const widget = new TTPAgentSDK.AgentWidget({
|
|
833
|
+
agentId: 'agent_87c4a55a1',
|
|
834
|
+
appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
|
|
835
|
+
|
|
836
|
+
primaryColor: '${config.primaryColor || '#10B981'}',
|
|
837
|
+
position: '${position}',
|
|
838
|
+
|
|
839
|
+
icon: {
|
|
840
|
+
type: '${config.icon?.type || 'custom'}',
|
|
841
|
+
customImage: '${config.icon?.customImage || 'https://talktopc.com/logo192.png'}',
|
|
842
|
+
size: '${config.icon?.size || 'medium'}'
|
|
843
|
+
},
|
|
844
|
+
|
|
845
|
+
button: {
|
|
846
|
+
backgroundColor: '${config.button?.backgroundColor || '#FFFFFF'}',
|
|
847
|
+
hoverColor: '${config.button?.hoverColor || '#E5E7EB'}'
|
|
848
|
+
},
|
|
849
|
+
|
|
850
|
+
header: {
|
|
851
|
+
title: '${(config.header?.title || 'TTP Support').replace(/'/g, "\\'")}',
|
|
852
|
+
backgroundColor: '${config.header?.backgroundColor || '#A78BFA'}',
|
|
853
|
+
textColor: '${config.header?.textColor || '#FFFFFF'}'
|
|
854
|
+
},
|
|
855
|
+
|
|
856
|
+
panel: {
|
|
857
|
+
micButtonColor: '${config.panel?.micButtonColor || '#E5E7EB'}',
|
|
858
|
+
micButtonActiveColor: '${config.panel?.micButtonActiveColor || '#EF4444'}'
|
|
859
|
+
}
|
|
860
|
+
});`;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function rgbToHex(rgb) {
|
|
865
|
+
if (!rgb) return null;
|
|
866
|
+
if (rgb.startsWith('#')) return rgb;
|
|
867
|
+
// Try to parse rgb/rgba format
|
|
868
|
+
const match = rgb.match(/\d+/g);
|
|
869
|
+
if (match && match.length >= 3) {
|
|
870
|
+
return '#' + match.slice(0, 3).map(x => {
|
|
871
|
+
const hex = parseInt(x).toString(16);
|
|
872
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
873
|
+
}).join('');
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
|
|
188
878
|
// Initialize the widget
|
|
189
879
|
try {
|
|
190
880
|
console.log('Initializing widget with agent ID + app ID...');
|
|
@@ -200,34 +890,16 @@ const widget = new TTPAgentSDK.AgentWidget({
|
|
|
200
890
|
}
|
|
201
891
|
|
|
202
892
|
// Create a new AgentWidget instance with agent ID + app ID
|
|
893
|
+
// getSessionUrl is now OPTIONAL - widget will auto-construct URL from agentId/appId
|
|
203
894
|
const widget = new TTPAgentSDK.AgentWidget({
|
|
204
895
|
agentId: 'agent_87c4a55a1',
|
|
205
896
|
appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
|
|
206
897
|
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
console.log('App ID:', appId);
|
|
211
|
-
console.log('Variables:', variables);
|
|
212
|
-
|
|
213
|
-
updateStatus('Creating direct session URL...', 'info');
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
// Direct WebSocket URL construction with demo flag
|
|
217
|
-
const wsUrl = `wss://speech.talktopc.com/ws/conv?agentId=${agentId}&appId=${appId}&demo=true`;
|
|
218
|
-
|
|
219
|
-
console.log('Generated direct session URL:', wsUrl);
|
|
220
|
-
updateStatus('Direct session URL created ✓', 'success');
|
|
221
|
-
|
|
222
|
-
return wsUrl;
|
|
223
|
-
} catch (error) {
|
|
224
|
-
console.error('Failed to create direct session URL:', error);
|
|
225
|
-
updateStatus('Failed to create direct session URL ✗', 'error');
|
|
226
|
-
throw error;
|
|
227
|
-
}
|
|
228
|
-
},
|
|
898
|
+
// getSessionUrl is OPTIONAL - omitted here, so widget auto-constructs the WebSocket URL
|
|
899
|
+
// If you need signed links or custom URL logic, you can provide it:
|
|
900
|
+
// getSessionUrl: async ({ agentId, appId, variables }) => { ... }
|
|
229
901
|
|
|
230
|
-
// Pass
|
|
902
|
+
// Optional: Pass variables for your agent/backend
|
|
231
903
|
variables: {
|
|
232
904
|
testMode: true,
|
|
233
905
|
userName: 'Test User',
|
|
@@ -235,14 +907,82 @@ const widget = new TTPAgentSDK.AgentWidget({
|
|
|
235
907
|
connectionType: 'direct'
|
|
236
908
|
},
|
|
237
909
|
|
|
238
|
-
// Customize appearance
|
|
910
|
+
// Customize appearance (using simple config - backward compatible)
|
|
239
911
|
primaryColor: '#10B981',
|
|
240
|
-
position: 'bottom-right'
|
|
912
|
+
position: 'bottom-right',
|
|
913
|
+
|
|
914
|
+
// Set TTP icon
|
|
915
|
+
icon: {
|
|
916
|
+
type: 'custom',
|
|
917
|
+
customImage: 'https://talktopc.com/logo192.png',
|
|
918
|
+
size: 'medium'
|
|
919
|
+
},
|
|
920
|
+
|
|
921
|
+
// Button colors - Floating button (main button) - WHITE
|
|
922
|
+
button: {
|
|
923
|
+
backgroundColor: '#FFFFFF', // White background for floating button
|
|
924
|
+
hoverColor: '#E5E7EB' // Gray on hover
|
|
925
|
+
},
|
|
926
|
+
|
|
927
|
+
// Header colors - Top of panel - LIGHT PURPLE
|
|
928
|
+
header: {
|
|
929
|
+
title: 'TTP Support',
|
|
930
|
+
backgroundColor: '#A78BFA', // Light purple header background
|
|
931
|
+
textColor: '#FFFFFF' // White text
|
|
932
|
+
},
|
|
933
|
+
|
|
934
|
+
// Panel colors - Mic button inside panel - GRAY
|
|
935
|
+
panel: {
|
|
936
|
+
micButtonColor: '#E5E7EB', // Gray mic button
|
|
937
|
+
micButtonActiveColor: '#EF4444' // Red when active/recording
|
|
938
|
+
},
|
|
939
|
+
|
|
940
|
+
// Enhanced features are now available but using defaults
|
|
941
|
+
// You can optionally add:
|
|
942
|
+
// button: { size: 'large', shape: 'circle' },
|
|
943
|
+
// panel: { width: 400, height: 600 },
|
|
944
|
+
// header: { title: 'My Voice Assistant' },
|
|
945
|
+
// animation: { enableHover: true, enablePulse: true }
|
|
241
946
|
});
|
|
242
947
|
|
|
243
948
|
// Store widget reference for testing (like the working implementation)
|
|
244
949
|
window.testWidget = widget;
|
|
245
950
|
|
|
951
|
+
// Store default config for reset functionality (matches initial widget config)
|
|
952
|
+
window.defaultConfig = JSON.parse(JSON.stringify({
|
|
953
|
+
primaryColor: '#10B981',
|
|
954
|
+
position: 'bottom-right',
|
|
955
|
+
direction: 'ltr',
|
|
956
|
+
icon: {
|
|
957
|
+
type: 'custom',
|
|
958
|
+
customImage: 'https://talktopc.com/logo192.png',
|
|
959
|
+
size: 'medium'
|
|
960
|
+
},
|
|
961
|
+
button: {
|
|
962
|
+
backgroundColor: '#FFFFFF',
|
|
963
|
+
hoverColor: '#E5E7EB',
|
|
964
|
+
size: 'medium',
|
|
965
|
+
shape: 'circle'
|
|
966
|
+
},
|
|
967
|
+
header: {
|
|
968
|
+
title: 'TTP Support',
|
|
969
|
+
backgroundColor: '#A78BFA',
|
|
970
|
+
textColor: '#FFFFFF'
|
|
971
|
+
},
|
|
972
|
+
panel: {
|
|
973
|
+
micButtonColor: '#E5E7EB',
|
|
974
|
+
micButtonActiveColor: '#EF4444',
|
|
975
|
+
micButtonHint: {
|
|
976
|
+
text: 'Click the button to start voice conversation',
|
|
977
|
+
color: '#6B7280',
|
|
978
|
+
fontSize: '12px'
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}));
|
|
982
|
+
|
|
983
|
+
// Initialize customization controls
|
|
984
|
+
initializeCustomizationControls();
|
|
985
|
+
|
|
246
986
|
console.log('Widget initialized successfully with agent ID + app ID!');
|
|
247
987
|
console.log('Widget instance:', widget);
|
|
248
988
|
console.log('Test methods available at window.testWidget');
|
|
@@ -294,6 +1034,7 @@ const widget = new TTPAgentSDK.AgentWidget({
|
|
|
294
1034
|
alert('Microphone access DENIED! Please allow microphone in browser settings.');
|
|
295
1035
|
}
|
|
296
1036
|
}
|
|
1037
|
+
|
|
297
1038
|
</script>
|
|
298
1039
|
</body>
|
|
299
1040
|
</html>
|