seawolf2357 commited on
Commit
96c7d3a
ยท
verified ยท
1 Parent(s): a7626a8

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +112 -2
index.html CHANGED
@@ -276,6 +276,38 @@
276
  background-color: var(--secondary-color);
277
  border-radius: 50%;
278
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  </style>
280
  </head>
281
 
@@ -296,6 +328,12 @@
296
  <div id="status-dot" class="status-dot disconnected"></div>
297
  <span id="status-text">์—ฐ๊ฒฐ ๋Œ€๊ธฐ ์ค‘</span>
298
  </div>
 
 
 
 
 
 
299
  </div>
300
  <div class="chat-container">
301
  <div class="chat-messages" id="chat-messages"></div>
@@ -317,6 +355,8 @@
317
  let audioLevel = 0;
318
  let animationFrame;
319
  let audioContext, analyser, audioSource;
 
 
320
 
321
  function updateStatus(state) {
322
  statusDot.className = 'status-dot ' + state;
@@ -328,6 +368,7 @@
328
  statusText.textContent = '์—ฐ๊ฒฐ ๋Œ€๊ธฐ ์ค‘';
329
  }
330
  }
 
331
  function updateButtonState() {
332
  const button = document.getElementById('start-button');
333
  if (peerConnection && (peerConnection.connectionState === 'connecting' || peerConnection.connectionState === 'new')) {
@@ -357,6 +398,7 @@
357
  updateStatus('disconnected');
358
  }
359
  }
 
360
  function setupAudioVisualization(stream) {
361
  audioContext = new (window.AudioContext || window.webkitAudioContext)();
362
  analyser = audioContext.createAnalyser();
@@ -364,7 +406,7 @@
364
  audioSource.connect(analyser);
365
  analyser.fftSize = 256;
366
  const bufferLength = analyser.frequencyBinCount;
367
- const dataArray = new Uint8Array(bufferLength);
368
 
369
  const visualizerBars = document.querySelectorAll('.visualizer-bar');
370
  const barCount = visualizerBars.length;
@@ -389,11 +431,24 @@
389
  visualizerBars[i].style.transform = `scaleY(${scaleY})`;
390
  }
391
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  animationFrame = requestAnimationFrame(updateAudioLevel);
393
  }
394
 
395
  updateAudioLevel();
396
  }
 
397
  function showError(message) {
398
  const toast = document.getElementById('error-toast');
399
  toast.textContent = message;
@@ -404,6 +459,7 @@
404
  toast.style.display = 'none';
405
  }, 5000);
406
  }
 
407
  async function setupWebRTC() {
408
  const config = __RTC_CONFIGURATION__;
409
  peerConnection = new RTCPeerConnection(config);
@@ -417,6 +473,7 @@
417
  toast.style.display = 'none';
418
  }, 5000);
419
  }, 5000);
 
420
  try {
421
  const stream = await navigator.mediaDevices.getUserMedia({
422
  audio: true
@@ -462,6 +519,7 @@
462
  }
463
  updateButtonState();
464
  });
 
465
  webrtc_id = Math.random().toString(36).substring(7);
466
  const response = await fetch('/webrtc/offer', {
467
  method: 'POST',
@@ -472,6 +530,7 @@
472
  webrtc_id: webrtc_id
473
  })
474
  });
 
475
  const serverResponse = await response.json();
476
  if (serverResponse.status === 'failed') {
477
  showError(serverResponse.meta.error === 'concurrency_limit_reached'
@@ -480,12 +539,42 @@
480
  stop();
481
  return;
482
  }
 
483
  await peerConnection.setRemoteDescription(serverResponse);
484
- const eventSource = new EventSource('/outputs?webrtc_id=' + webrtc_id);
 
 
 
 
 
 
 
 
 
485
  eventSource.addEventListener("output", (event) => {
486
  const eventJson = JSON.parse(event.data);
487
  addMessage("assistant", eventJson.content);
488
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  } catch (err) {
490
  clearTimeout(timeoutId);
491
  console.error('Error setting up WebRTC:', err);
@@ -493,6 +582,7 @@
493
  stop();
494
  }
495
  }
 
496
  function addMessage(role, content) {
497
  const messageDiv = document.createElement('div');
498
  messageDiv.classList.add('message', role);
@@ -500,7 +590,13 @@
500
  chatMessages.appendChild(messageDiv);
501
  chatMessages.scrollTop = chatMessages.scrollHeight;
502
  }
 
503
  function stop() {
 
 
 
 
 
504
  if (animationFrame) {
505
  cancelAnimationFrame(animationFrame);
506
  }
@@ -529,6 +625,7 @@
529
  updateButtonState();
530
  audioLevel = 0;
531
  }
 
532
  startButton.addEventListener('click', () => {
533
  console.log('clicked');
534
  console.log(peerConnection, peerConnection?.connectionState);
@@ -539,6 +636,19 @@
539
  stop();
540
  }
541
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  </script>
543
  </body>
544
 
 
276
  background-color: var(--secondary-color);
277
  border-radius: 50%;
278
  }
279
+ /* ์ถ”๊ฐ€๋œ ์Šคํƒ€์ผ */
280
+ .header-controls {
281
+ display: flex;
282
+ justify-content: center;
283
+ align-items: center;
284
+ margin-top: 10px;
285
+ padding: 0 10px;
286
+ }
287
+
288
+ .conversation-status {
289
+ font-size: 14px;
290
+ color: #aaa;
291
+ padding: 5px 10px;
292
+ border-radius: 8px;
293
+ background-color: rgba(0, 0, 0, 0.2);
294
+ }
295
+
296
+ .status-box {
297
+ padding: 8px 12px;
298
+ background-color: var(--card-bg);
299
+ border-radius: 8px;
300
+ margin-top: 10px;
301
+ font-size: 14px;
302
+ border: 1px solid var(--border-color);
303
+ color: #aaa;
304
+ }
305
+
306
+ .status-box.error {
307
+ background-color: rgba(244, 67, 54, 0.1);
308
+ border-color: rgba(244, 67, 54, 0.5);
309
+ color: #f44336;
310
+ }
311
  </style>
312
  </head>
313
 
 
328
  <div id="status-dot" class="status-dot disconnected"></div>
329
  <span id="status-text">์—ฐ๊ฒฐ ๋Œ€๊ธฐ ์ค‘</span>
330
  </div>
331
+ <!-- ์ƒํƒœ ํ‘œ์‹œ UI -->
332
+ <div class="header-controls">
333
+ <div class="conversation-status" id="conversation-status">
334
+ ๋Œ€ํ™”๋ฅผ ์‹œ์ž‘ํ•˜์„ธ์š”
335
+ </div>
336
+ </div>
337
  </div>
338
  <div class="chat-container">
339
  <div class="chat-messages" id="chat-messages"></div>
 
355
  let audioLevel = 0;
356
  let animationFrame;
357
  let audioContext, analyser, audioSource;
358
+ let dataArray;
359
+ let eventSource;
360
 
361
  function updateStatus(state) {
362
  statusDot.className = 'status-dot ' + state;
 
368
  statusText.textContent = '์—ฐ๊ฒฐ ๋Œ€๊ธฐ ์ค‘';
369
  }
370
  }
371
+
372
  function updateButtonState() {
373
  const button = document.getElementById('start-button');
374
  if (peerConnection && (peerConnection.connectionState === 'connecting' || peerConnection.connectionState === 'new')) {
 
398
  updateStatus('disconnected');
399
  }
400
  }
401
+
402
  function setupAudioVisualization(stream) {
403
  audioContext = new (window.AudioContext || window.webkitAudioContext)();
404
  analyser = audioContext.createAnalyser();
 
406
  audioSource.connect(analyser);
407
  analyser.fftSize = 256;
408
  const bufferLength = analyser.frequencyBinCount;
409
+ dataArray = new Uint8Array(bufferLength);
410
 
411
  const visualizerBars = document.querySelectorAll('.visualizer-bar');
412
  const barCount = visualizerBars.length;
 
431
  visualizerBars[i].style.transform = `scaleY(${scaleY})`;
432
  }
433
 
434
+ // ์Œ์„ฑ ํ™œ๋™ ๊ฐ์ง€๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€ ์ฝ”๋“œ
435
+ let totalSum = 0;
436
+ for (let i = 0; i < dataArray.length; i++) {
437
+ totalSum += dataArray[i];
438
+ }
439
+ const totalAverage = totalSum / dataArray.length / 255;
440
+
441
+ // ์ž„๊ณ„๊ฐ’ ์ด์ƒ์ด๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ๋งํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ
442
+ if (totalAverage > 0.1) {
443
+ handleUserSpeaking();
444
+ }
445
+
446
  animationFrame = requestAnimationFrame(updateAudioLevel);
447
  }
448
 
449
  updateAudioLevel();
450
  }
451
+
452
  function showError(message) {
453
  const toast = document.getElementById('error-toast');
454
  toast.textContent = message;
 
459
  toast.style.display = 'none';
460
  }, 5000);
461
  }
462
+
463
  async function setupWebRTC() {
464
  const config = __RTC_CONFIGURATION__;
465
  peerConnection = new RTCPeerConnection(config);
 
473
  toast.style.display = 'none';
474
  }, 5000);
475
  }, 5000);
476
+
477
  try {
478
  const stream = await navigator.mediaDevices.getUserMedia({
479
  audio: true
 
519
  }
520
  updateButtonState();
521
  });
522
+
523
  webrtc_id = Math.random().toString(36).substring(7);
524
  const response = await fetch('/webrtc/offer', {
525
  method: 'POST',
 
530
  webrtc_id: webrtc_id
531
  })
532
  });
533
+
534
  const serverResponse = await response.json();
535
  if (serverResponse.status === 'failed') {
536
  showError(serverResponse.meta.error === 'concurrency_limit_reached'
 
539
  stop();
540
  return;
541
  }
542
+
543
  await peerConnection.setRemoteDescription(serverResponse);
544
+
545
+ // ๊ธฐ์กด EventSource ์ •๋ฆฌ
546
+ if (eventSource) {
547
+ eventSource.close();
548
+ }
549
+
550
+ // ์ƒˆ EventSource ์ƒ์„ฑ
551
+ eventSource = new EventSource('/outputs?webrtc_id=' + webrtc_id);
552
+
553
+ // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์„ค์ •
554
  eventSource.addEventListener("output", (event) => {
555
  const eventJson = JSON.parse(event.data);
556
  addMessage("assistant", eventJson.content);
557
  });
558
+
559
+ eventSource.addEventListener("status", (event) => {
560
+ const eventJson = JSON.parse(event.data);
561
+ document.getElementById('conversation-status').textContent = eventJson.status;
562
+ });
563
+
564
+ eventSource.addEventListener("error", (event) => {
565
+ try {
566
+ const eventJson = JSON.parse(event.data);
567
+ showError(eventJson.message);
568
+ } catch (e) {
569
+ // EventSource์˜ ๊ธฐ๋ณธ ์˜ค๋ฅ˜ ์ด๋ฒคํŠธ์ผ ์ˆ˜ ์žˆ์Œ
570
+ if (eventSource.readyState === EventSource.CLOSED) {
571
+ console.log("EventSource ์—ฐ๊ฒฐ์ด ๋‹ซํ˜”์Šต๋‹ˆ๋‹ค.");
572
+ } else {
573
+ console.error("EventSource ์˜ค๋ฅ˜:", e);
574
+ }
575
+ }
576
+ });
577
+
578
  } catch (err) {
579
  clearTimeout(timeoutId);
580
  console.error('Error setting up WebRTC:', err);
 
582
  stop();
583
  }
584
  }
585
+
586
  function addMessage(role, content) {
587
  const messageDiv = document.createElement('div');
588
  messageDiv.classList.add('message', role);
 
590
  chatMessages.appendChild(messageDiv);
591
  chatMessages.scrollTop = chatMessages.scrollHeight;
592
  }
593
+
594
  function stop() {
595
+ if (eventSource) {
596
+ eventSource.close();
597
+ eventSource = null;
598
+ }
599
+
600
  if (animationFrame) {
601
  cancelAnimationFrame(animationFrame);
602
  }
 
625
  updateButtonState();
626
  audioLevel = 0;
627
  }
628
+
629
  startButton.addEventListener('click', () => {
630
  console.log('clicked');
631
  console.log(peerConnection, peerConnection?.connectionState);
 
636
  stop();
637
  }
638
  });
639
+
640
+ // ์ธํ„ฐ๋Ÿฝ์…˜ ์ฒ˜๋ฆฌ - ์‚ฌ์šฉ์ž๊ฐ€ ๋งํ•˜๊ธฐ ์‹œ์ž‘ํ•  ๋•Œ AI ์˜ค๋””์˜ค ์ค‘๋‹จ
641
+ let userSpeaking = false;
642
+ let speakingTimeout;
643
+
644
+ function handleUserSpeaking() {
645
+ userSpeaking = true;
646
+ clearTimeout(speakingTimeout);
647
+
648
+ speakingTimeout = setTimeout(() => {
649
+ userSpeaking = false;
650
+ }, 1000); // 1์ดˆ ๋™์•ˆ ์˜ค๋””์˜ค๊ฐ€ ์—†์œผ๋ฉด ๋งํ•˜๊ธฐ ์ค‘๋‹จ์œผ๋กœ ๊ฐ„์ฃผ
651
+ }
652
  </script>
653
  </body>
654