ttp-agent-sdk 2.3.2 → 2.3.3

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.
@@ -0,0 +1,1167 @@
1
+ <!-- demo-v2.html -->
2
+
3
+ <!DOCTYPE html>
4
+
5
+ <html lang="en">
6
+
7
+ <head>
8
+
9
+ <meta charset="UTF-8">
10
+
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
+
13
+ <title>VoiceSDK v2 Demo - Multi-Codec Support</title>
14
+
15
+ <style>
16
+
17
+ * {
18
+
19
+ margin: 0;
20
+
21
+ padding: 0;
22
+
23
+ box-sizing: border-box;
24
+
25
+ }
26
+
27
+
28
+
29
+ body {
30
+
31
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
32
+
33
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
34
+
35
+ min-height: 100vh;
36
+
37
+ display: flex;
38
+
39
+ justify-content: center;
40
+
41
+ align-items: center;
42
+
43
+ padding: 20px;
44
+
45
+ }
46
+
47
+
48
+
49
+ .container {
50
+
51
+ background: white;
52
+
53
+ border-radius: 20px;
54
+
55
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
56
+
57
+ max-width: 800px;
58
+
59
+ width: 100%;
60
+
61
+ padding: 40px;
62
+
63
+ }
64
+
65
+
66
+
67
+ h1 {
68
+
69
+ color: #333;
70
+
71
+ margin-bottom: 10px;
72
+
73
+ font-size: 32px;
74
+
75
+ }
76
+
77
+
78
+
79
+ .version {
80
+
81
+ color: #667eea;
82
+
83
+ font-size: 14px;
84
+
85
+ margin-bottom: 30px;
86
+
87
+ }
88
+
89
+
90
+
91
+ .config-section {
92
+
93
+ background: #f8f9fa;
94
+
95
+ padding: 20px;
96
+
97
+ border-radius: 10px;
98
+
99
+ margin-bottom: 20px;
100
+
101
+ }
102
+
103
+
104
+
105
+ .config-group {
106
+
107
+ margin-bottom: 15px;
108
+
109
+ }
110
+
111
+
112
+
113
+ .config-group label {
114
+
115
+ display: block;
116
+
117
+ font-weight: 600;
118
+
119
+ margin-bottom: 5px;
120
+
121
+ color: #555;
122
+
123
+ font-size: 14px;
124
+
125
+ }
126
+
127
+
128
+
129
+ .config-group input,
130
+
131
+ .config-group select {
132
+
133
+ width: 100%;
134
+
135
+ padding: 10px;
136
+
137
+ border: 2px solid #ddd;
138
+
139
+ border-radius: 8px;
140
+
141
+ font-size: 14px;
142
+
143
+ transition: border-color 0.3s;
144
+
145
+ }
146
+
147
+
148
+
149
+ .config-group input:focus,
150
+
151
+ .config-group select:focus {
152
+
153
+ outline: none;
154
+
155
+ border-color: #667eea;
156
+
157
+ }
158
+
159
+
160
+
161
+ .config-row {
162
+
163
+ display: grid;
164
+
165
+ grid-template-columns: 1fr 1fr;
166
+
167
+ gap: 15px;
168
+
169
+ }
170
+
171
+
172
+
173
+ .status {
174
+
175
+ background: #f8f9fa;
176
+
177
+ padding: 15px;
178
+
179
+ border-radius: 10px;
180
+
181
+ margin-bottom: 20px;
182
+
183
+ font-family: 'Courier New', monospace;
184
+
185
+ font-size: 13px;
186
+
187
+ }
188
+
189
+
190
+
191
+ .status-item {
192
+
193
+ display: flex;
194
+
195
+ justify-content: space-between;
196
+
197
+ margin-bottom: 8px;
198
+
199
+ padding-bottom: 8px;
200
+
201
+ border-bottom: 1px solid #e0e0e0;
202
+
203
+ }
204
+
205
+
206
+
207
+ .status-item:last-child {
208
+
209
+ margin-bottom: 0;
210
+
211
+ padding-bottom: 0;
212
+
213
+ border-bottom: none;
214
+
215
+ }
216
+
217
+
218
+
219
+ .status-label {
220
+
221
+ color: #666;
222
+
223
+ }
224
+
225
+
226
+
227
+ .status-value {
228
+
229
+ font-weight: bold;
230
+
231
+ color: #333;
232
+
233
+ }
234
+
235
+
236
+
237
+ .status-value.connected {
238
+
239
+ color: #10b981;
240
+
241
+ }
242
+
243
+
244
+
245
+ .status-value.disconnected {
246
+
247
+ color: #ef4444;
248
+
249
+ }
250
+
251
+
252
+
253
+ .status-value.active {
254
+
255
+ color: #3b82f6;
256
+
257
+ }
258
+
259
+
260
+
261
+ .controls {
262
+
263
+ display: grid;
264
+
265
+ grid-template-columns: repeat(2, 1fr);
266
+
267
+ gap: 15px;
268
+
269
+ margin-bottom: 20px;
270
+
271
+ }
272
+
273
+
274
+
275
+ button {
276
+
277
+ padding: 15px 30px;
278
+
279
+ border: none;
280
+
281
+ border-radius: 10px;
282
+
283
+ font-size: 16px;
284
+
285
+ font-weight: 600;
286
+
287
+ cursor: pointer;
288
+
289
+ transition: all 0.3s;
290
+
291
+ display: flex;
292
+
293
+ align-items: center;
294
+
295
+ justify-content: center;
296
+
297
+ gap: 10px;
298
+
299
+ }
300
+
301
+
302
+
303
+ button:disabled {
304
+
305
+ opacity: 0.5;
306
+
307
+ cursor: not-allowed;
308
+
309
+ }
310
+
311
+
312
+
313
+ .btn-connect {
314
+
315
+ background: #10b981;
316
+
317
+ color: white;
318
+
319
+ }
320
+
321
+
322
+
323
+ .btn-connect:hover:not(:disabled) {
324
+
325
+ background: #059669;
326
+
327
+ transform: translateY(-2px);
328
+
329
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
330
+
331
+ }
332
+
333
+
334
+
335
+ .btn-disconnect {
336
+
337
+ background: #ef4444;
338
+
339
+ color: white;
340
+
341
+ }
342
+
343
+
344
+
345
+ .btn-disconnect:hover:not(:disabled) {
346
+
347
+ background: #dc2626;
348
+
349
+ transform: translateY(-2px);
350
+
351
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
352
+
353
+ }
354
+
355
+
356
+
357
+ .btn-record {
358
+
359
+ background: #3b82f6;
360
+
361
+ color: white;
362
+
363
+ }
364
+
365
+
366
+
367
+ .btn-record:hover:not(:disabled) {
368
+
369
+ background: #2563eb;
370
+
371
+ transform: translateY(-2px);
372
+
373
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
374
+
375
+ }
376
+
377
+
378
+
379
+ .btn-record.recording {
380
+
381
+ background: #f59e0b;
382
+
383
+ animation: pulse 2s infinite;
384
+
385
+ }
386
+
387
+
388
+
389
+ @keyframes pulse {
390
+
391
+ 0%, 100% { opacity: 1; }
392
+
393
+ 50% { opacity: 0.7; }
394
+
395
+ }
396
+
397
+
398
+
399
+ .btn-stop {
400
+
401
+ background: #8b5cf6;
402
+
403
+ color: white;
404
+
405
+ }
406
+
407
+
408
+
409
+ .btn-stop:hover:not(:disabled) {
410
+
411
+ background: #7c3aed;
412
+
413
+ transform: translateY(-2px);
414
+
415
+ box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
416
+
417
+ }
418
+
419
+
420
+
421
+ .logs {
422
+
423
+ background: #1f2937;
424
+
425
+ color: #10b981;
426
+
427
+ padding: 20px;
428
+
429
+ border-radius: 10px;
430
+
431
+ max-height: 300px;
432
+
433
+ overflow-y: auto;
434
+
435
+ font-family: 'Courier New', monospace;
436
+
437
+ font-size: 13px;
438
+
439
+ }
440
+
441
+
442
+
443
+ .log-entry {
444
+
445
+ margin-bottom: 5px;
446
+
447
+ line-height: 1.4;
448
+
449
+ }
450
+
451
+
452
+
453
+ .log-entry.error {
454
+
455
+ color: #ef4444;
456
+
457
+ }
458
+
459
+
460
+
461
+ .log-entry.success {
462
+
463
+ color: #10b981;
464
+
465
+ }
466
+
467
+
468
+
469
+ .log-entry.info {
470
+
471
+ color: #3b82f6;
472
+
473
+ }
474
+
475
+
476
+
477
+ .log-entry.warning {
478
+
479
+ color: #f59e0b;
480
+
481
+ }
482
+
483
+
484
+
485
+ .format-info {
486
+
487
+ background: #eff6ff;
488
+
489
+ border-left: 4px solid #3b82f6;
490
+
491
+ padding: 15px;
492
+
493
+ border-radius: 8px;
494
+
495
+ margin-top: 15px;
496
+
497
+ }
498
+
499
+
500
+
501
+ .format-info h3 {
502
+
503
+ color: #1e40af;
504
+
505
+ margin-bottom: 10px;
506
+
507
+ font-size: 16px;
508
+
509
+ }
510
+
511
+
512
+
513
+ .format-info pre {
514
+
515
+ background: white;
516
+
517
+ padding: 10px;
518
+
519
+ border-radius: 5px;
520
+
521
+ overflow-x: auto;
522
+
523
+ font-size: 12px;
524
+
525
+ }
526
+
527
+ </style>
528
+
529
+ </head>
530
+
531
+ <body>
532
+
533
+ <div class="container">
534
+
535
+ <h1>🎙️ VoiceSDK v2 Demo</h1>
536
+
537
+ <p class="version">Multi-Codec Speech-to-Speech SDK</p>
538
+
539
+
540
+
541
+ <!-- Configuration -->
542
+
543
+ <div class="config-section">
544
+
545
+ <h2 style="margin-bottom: 15px; font-size: 18px;">⚙️ Configuration</h2>
546
+
547
+
548
+
549
+ <div class="config-group">
550
+
551
+ <label>WebSocket URL</label>
552
+
553
+ <input type="text" id="websocketUrl" value="wss://speech.talktopc.com/ws/conv">
554
+
555
+ </div>
556
+
557
+
558
+
559
+ <div class="config-row">
560
+
561
+ <div class="config-group">
562
+
563
+ <label>Agent ID</label>
564
+
565
+ <input type="text" id="agentId" value="agent_5a2b984c1" placeholder="agent-123">
566
+
567
+ </div>
568
+
569
+ <div class="config-group">
570
+
571
+ <label>App ID</label>
572
+
573
+ <input type="text" id="appId" value="app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC" placeholder="app-456">
574
+
575
+ </div>
576
+
577
+ </div>
578
+
579
+
580
+
581
+ <div class="config-row">
582
+
583
+ <div class="config-group">
584
+
585
+ <label>Output Container</label>
586
+
587
+ <select id="outputContainer">
588
+
589
+ <option value="raw">Raw (No container)</option>
590
+
591
+ <option value="wav" selected>WAV (With header)</option>
592
+
593
+ </select>
594
+
595
+ </div>
596
+
597
+ <div class="config-group">
598
+
599
+ <label>Output Encoding</label>
600
+
601
+ <select id="outputEncoding">
602
+
603
+ <option value="pcm" selected>PCM (Uncompressed)</option>
604
+
605
+ <option value="pcmu">PCMU (μ-law, 8-bit)</option>
606
+
607
+ <option value="pcma">PCMA (A-law, 8-bit)</option>
608
+
609
+ </select>
610
+
611
+ </div>
612
+
613
+ </div>
614
+
615
+
616
+
617
+ <div class="config-row">
618
+
619
+ <div class="config-group">
620
+
621
+ <label>Output Sample Rate</label>
622
+
623
+ <select id="outputSampleRate">
624
+
625
+ <option value="8000">8 kHz (Telephony)</option>
626
+
627
+ <option value="16000" selected>16 kHz (Voice)</option>
628
+
629
+ <option value="22050">22.05 kHz</option>
630
+
631
+ <option value="24000">24 kHz</option>
632
+
633
+ <option value="44100">44.1 kHz (CD Quality)</option>
634
+
635
+ <option value="48000">48 kHz (Pro)</option>
636
+
637
+ </select>
638
+
639
+ </div>
640
+
641
+ <div class="config-group">
642
+
643
+ <label>Output Bit Depth</label>
644
+
645
+ <select id="outputBitDepth">
646
+
647
+ <option value="8">8-bit</option>
648
+
649
+ <option value="16" selected>16-bit</option>
650
+
651
+ <option value="24">24-bit</option>
652
+
653
+ </select>
654
+
655
+ </div>
656
+
657
+ </div>
658
+
659
+ </div>
660
+
661
+
662
+
663
+ <!-- Status -->
664
+
665
+ <div class="status">
666
+
667
+ <div class="status-item">
668
+
669
+ <span class="status-label">Connection:</span>
670
+
671
+ <span class="status-value disconnected" id="statusConnection">Disconnected</span>
672
+
673
+ </div>
674
+
675
+ <div class="status-item">
676
+
677
+ <span class="status-label">Recording:</span>
678
+
679
+ <span class="status-value" id="statusRecording">Idle</span>
680
+
681
+ </div>
682
+
683
+ <div class="status-item">
684
+
685
+ <span class="status-label">Playback:</span>
686
+
687
+ <span class="status-value" id="statusPlayback">Idle</span>
688
+
689
+ </div>
690
+
691
+ <div class="status-item">
692
+
693
+ <span class="status-label">Negotiated Format:</span>
694
+
695
+ <span class="status-value" id="statusFormat">Not negotiated</span>
696
+
697
+ </div>
698
+
699
+ </div>
700
+
701
+
702
+
703
+ <!-- Controls -->
704
+
705
+ <div class="controls">
706
+
707
+ <button class="btn-connect" id="btnConnect">
708
+
709
+ 🔌 Connect
710
+
711
+ </button>
712
+
713
+ <button class="btn-disconnect" id="btnDisconnect" disabled>
714
+
715
+ 🔌 Disconnect
716
+
717
+ </button>
718
+
719
+ <button class="btn-record" id="btnRecord" disabled>
720
+
721
+ 🎤 Start Recording
722
+
723
+ </button>
724
+
725
+ <button class="btn-stop" id="btnStop" disabled>
726
+
727
+ 🛑 Stop Recording
728
+
729
+ </button>
730
+
731
+ </div>
732
+
733
+
734
+
735
+ <!-- Format Info (shown after negotiation) -->
736
+
737
+ <div class="format-info" id="formatInfo" style="display: none;">
738
+
739
+ <h3>📊 Negotiated Audio Format</h3>
740
+
741
+ <pre id="formatDetails"></pre>
742
+
743
+ </div>
744
+
745
+
746
+
747
+ <!-- Logs -->
748
+
749
+ <h3 style="margin-bottom: 10px; font-size: 18px;">📝 Event Log</h3>
750
+
751
+ <div class="logs" id="logs">
752
+
753
+ <div class="log-entry info">Waiting to connect...</div>
754
+
755
+ </div>
756
+
757
+ </div>
758
+
759
+
760
+
761
+ <!-- Load SDK -->
762
+
763
+ <!-- Load SDK as UMD module -->
764
+ <script src="../dist/agent-widget.js"></script>
765
+
766
+ <script>
767
+
768
+ // Get VoiceSDK_v2 from global TTPAgentSDK (UMD export)
769
+
770
+ const VoiceSDK_v2 = window.TTPAgentSDK?.VoiceSDK_v2;
771
+
772
+ if (!VoiceSDK_v2) {
773
+
774
+ console.error('VoiceSDK_v2 not found in TTPAgentSDK. Available exports:', Object.keys(window.TTPAgentSDK || {}));
775
+
776
+ alert('Error: VoiceSDK_v2 not found. Please check the console for details.');
777
+
778
+ }
779
+
780
+
781
+
782
+ // DOM elements
783
+
784
+ const btnConnect = document.getElementById('btnConnect');
785
+
786
+ const btnDisconnect = document.getElementById('btnDisconnect');
787
+
788
+ const btnRecord = document.getElementById('btnRecord');
789
+
790
+ const btnStop = document.getElementById('btnStop');
791
+
792
+
793
+
794
+ const statusConnection = document.getElementById('statusConnection');
795
+
796
+ const statusRecording = document.getElementById('statusRecording');
797
+
798
+ const statusPlayback = document.getElementById('statusPlayback');
799
+
800
+ const statusFormat = document.getElementById('statusFormat');
801
+
802
+
803
+
804
+ const logsContainer = document.getElementById('logs');
805
+
806
+ const formatInfo = document.getElementById('formatInfo');
807
+
808
+ const formatDetails = document.getElementById('formatDetails');
809
+
810
+
811
+
812
+ // SDK instance
813
+
814
+ let sdk = null;
815
+
816
+
817
+
818
+ // Logger
819
+
820
+ function log(message, type = 'info') {
821
+
822
+ const entry = document.createElement('div');
823
+
824
+ entry.className = `log-entry ${type}`;
825
+
826
+ const timestamp = new Date().toLocaleTimeString();
827
+
828
+ entry.textContent = `[${timestamp}] ${message}`;
829
+
830
+ logsContainer.appendChild(entry);
831
+
832
+ logsContainer.scrollTop = logsContainer.scrollHeight;
833
+
834
+ console.log(`[${type.toUpperCase()}] ${message}`);
835
+
836
+ }
837
+
838
+
839
+
840
+ // Connect button
841
+
842
+ btnConnect.addEventListener('click', async () => {
843
+
844
+ try {
845
+
846
+ // Get config from form
847
+
848
+ const config = {
849
+
850
+ websocketUrl: document.getElementById('websocketUrl').value,
851
+
852
+ agentId: document.getElementById('agentId').value,
853
+
854
+ appId: document.getElementById('appId').value,
855
+
856
+
857
+
858
+ // Output format
859
+
860
+ outputContainer: document.getElementById('outputContainer').value,
861
+
862
+ outputEncoding: document.getElementById('outputEncoding').value,
863
+
864
+ outputSampleRate: parseInt(document.getElementById('outputSampleRate').value),
865
+
866
+ outputBitDepth: parseInt(document.getElementById('outputBitDepth').value),
867
+
868
+ outputChannels: 1,
869
+
870
+
871
+
872
+ // Input format
873
+
874
+ sampleRate: 16000,
875
+
876
+ channels: 1,
877
+
878
+ bitDepth: 16,
879
+
880
+
881
+
882
+ // Disable auto-reconnect for demo (prevents infinite retry loop on connection failure)
883
+
884
+ autoReconnect: false
885
+
886
+ };
887
+
888
+
889
+
890
+ log('Initializing SDK v2...', 'info');
891
+
892
+
893
+
894
+ // Create SDK instance
895
+
896
+ sdk = new VoiceSDK_v2(config);
897
+
898
+
899
+
900
+ // Setup event listeners
901
+
902
+ setupSDKEvents();
903
+
904
+
905
+
906
+ log(`Connecting to ${config.websocketUrl}...`, 'info');
907
+
908
+
909
+
910
+ // Connect
911
+
912
+ const connected = await sdk.connect();
913
+
914
+
915
+
916
+ if (!connected) {
917
+
918
+ log('Failed to connect', 'error');
919
+
920
+ }
921
+
922
+
923
+
924
+ } catch (error) {
925
+
926
+ log(`Error: ${error.message}`, 'error');
927
+
928
+ console.error(error);
929
+
930
+ }
931
+
932
+ });
933
+
934
+
935
+
936
+ // Disconnect button
937
+
938
+ btnDisconnect.addEventListener('click', () => {
939
+
940
+ if (sdk) {
941
+
942
+ sdk.disconnect();
943
+
944
+ log('Disconnecting...', 'info');
945
+
946
+ }
947
+
948
+ });
949
+
950
+
951
+
952
+ // Record button
953
+
954
+ btnRecord.addEventListener('click', async () => {
955
+
956
+ try {
957
+
958
+ log('Starting recording...', 'info');
959
+
960
+ await sdk.startRecording();
961
+
962
+ } catch (error) {
963
+
964
+ log(`Error starting recording: ${error.message}`, 'error');
965
+
966
+ }
967
+
968
+ });
969
+
970
+
971
+
972
+ // Stop button
973
+
974
+ btnStop.addEventListener('click', async () => {
975
+
976
+ try {
977
+
978
+ log('Stopping recording...', 'info');
979
+
980
+ await sdk.stopRecording();
981
+
982
+ } catch (error) {
983
+
984
+ log(`Error stopping recording: ${error.message}`, 'error');
985
+
986
+ }
987
+
988
+ });
989
+
990
+
991
+
992
+ // Setup SDK event listeners
993
+
994
+ function setupSDKEvents() {
995
+
996
+ sdk.on('connected', () => {
997
+
998
+ log('✅ Connected to server', 'success');
999
+
1000
+ statusConnection.textContent = 'Connected';
1001
+
1002
+ statusConnection.className = 'status-value connected';
1003
+
1004
+
1005
+
1006
+ btnConnect.disabled = true;
1007
+
1008
+ btnDisconnect.disabled = false;
1009
+
1010
+ btnRecord.disabled = false;
1011
+
1012
+ });
1013
+
1014
+
1015
+
1016
+ sdk.on('disconnected', () => {
1017
+
1018
+ log('❌ Disconnected from server', 'warning');
1019
+
1020
+ statusConnection.textContent = 'Disconnected';
1021
+
1022
+ statusConnection.className = 'status-value disconnected';
1023
+
1024
+
1025
+
1026
+ btnConnect.disabled = false;
1027
+
1028
+ btnDisconnect.disabled = true;
1029
+
1030
+ btnRecord.disabled = true;
1031
+
1032
+ btnStop.disabled = true;
1033
+
1034
+
1035
+
1036
+ formatInfo.style.display = 'none';
1037
+
1038
+ });
1039
+
1040
+
1041
+
1042
+ sdk.on('formatNegotiated', (format) => {
1043
+
1044
+ log(`✅ Format negotiated: ${format.container}/${format.encoding} @ ${format.sampleRate}Hz`, 'success');
1045
+
1046
+
1047
+
1048
+ statusFormat.textContent = `${format.container}/${format.encoding} ${format.sampleRate}Hz ${format.bitDepth}bit`;
1049
+
1050
+
1051
+
1052
+ // Show format info
1053
+
1054
+ formatInfo.style.display = 'block';
1055
+
1056
+ formatDetails.textContent = JSON.stringify(format, null, 2);
1057
+
1058
+ });
1059
+
1060
+
1061
+
1062
+ sdk.on('recordingStarted', () => {
1063
+
1064
+ log('🎤 Recording started', 'success');
1065
+
1066
+ statusRecording.textContent = 'Recording';
1067
+
1068
+ statusRecording.className = 'status-value active';
1069
+
1070
+
1071
+
1072
+ btnRecord.disabled = true;
1073
+
1074
+ btnRecord.classList.add('recording');
1075
+
1076
+ btnStop.disabled = false;
1077
+
1078
+ });
1079
+
1080
+
1081
+
1082
+ sdk.on('recordingStopped', () => {
1083
+
1084
+ log('🛑 Recording stopped', 'info');
1085
+
1086
+ statusRecording.textContent = 'Idle';
1087
+
1088
+ statusRecording.className = 'status-value';
1089
+
1090
+
1091
+
1092
+ btnRecord.disabled = false;
1093
+
1094
+ btnRecord.classList.remove('recording');
1095
+
1096
+ btnStop.disabled = true;
1097
+
1098
+ });
1099
+
1100
+
1101
+
1102
+ sdk.on('playbackStarted', () => {
1103
+
1104
+ log('🔊 Playback started', 'info');
1105
+
1106
+ statusPlayback.textContent = 'Playing';
1107
+
1108
+ statusPlayback.className = 'status-value active';
1109
+
1110
+ });
1111
+
1112
+
1113
+
1114
+ sdk.on('playbackStopped', () => {
1115
+
1116
+ log('🔇 Playback stopped', 'info');
1117
+
1118
+ statusPlayback.textContent = 'Idle';
1119
+
1120
+ statusPlayback.className = 'status-value';
1121
+
1122
+ });
1123
+
1124
+
1125
+
1126
+ sdk.on('message', (message) => {
1127
+
1128
+ log(`📨 Message: ${message.t}`, 'info');
1129
+
1130
+ if (message.text) {
1131
+
1132
+ log(` "${message.text}"`, 'info');
1133
+
1134
+ }
1135
+
1136
+ });
1137
+
1138
+
1139
+
1140
+ sdk.on('error', (error) => {
1141
+
1142
+ log(`❌ Error: ${error.message}`, 'error');
1143
+
1144
+ console.error(error);
1145
+
1146
+ });
1147
+
1148
+ }
1149
+
1150
+
1151
+
1152
+ // Make SDK available in console for debugging
1153
+
1154
+ window.sdk = sdk;
1155
+
1156
+ window.VoiceSDK_v2 = VoiceSDK_v2;
1157
+
1158
+
1159
+
1160
+ log('Demo ready! Configure and click "Connect"', 'success');
1161
+
1162
+ </script>
1163
+
1164
+ </body>
1165
+
1166
+ </html>
1167
+