@hivegpt/hiveai-angular 0.0.607 → 0.0.609

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.
@@ -153,6 +153,11 @@ class AudioAnalyzerService {
153
153
  this.audioLevelsSubject.next([]);
154
154
  this.isUserSpeakingSubject.next(false);
155
155
  }
156
+ resumeContextIfSuspended() {
157
+ if (!this.audioContext || this.audioContext.state !== 'suspended')
158
+ return;
159
+ this.audioContext.resume().catch((err) => console.warn('[HiveGpt Voice] AudioContext resume failed', err));
160
+ }
156
161
  analyze() {
157
162
  if (!this.isRunning || !this.analyserNode || !this.dataArray) {
158
163
  return;
@@ -418,6 +423,8 @@ class VoiceAgentService {
418
423
  this.lifecycleToken = 0;
419
424
  this.subscriptions = new Subscription();
420
425
  this.pcEventCleanup = [];
426
+ this.pageLifecycleCleanup = [];
427
+ this.wasInActiveCallBeforeHide = false;
421
428
  this.callState$ = this.callStateSubject.asObservable();
422
429
  this.statusText$ = this.statusTextSubject.asObservable();
423
430
  this.duration$ = this.durationSubject.asObservable();
@@ -427,6 +434,13 @@ class VoiceAgentService {
427
434
  this.userTranscript$ = this.userTranscriptSubject.asObservable();
428
435
  this.botTranscript$ = this.botTranscriptSubject.asObservable();
429
436
  this.subscriptions.add(this.audioAnalyzer.audioLevels$.pipe(throttleTime(50)).subscribe((levels) => this.ngZone.run(() => this.audioLevelsSubject.next(levels))));
437
+ this.registerPageLifecycleListeners();
438
+ }
439
+ ngOnDestroy() {
440
+ this.pageLifecycleCleanup.forEach((fn) => fn());
441
+ this.pageLifecycleCleanup = [];
442
+ this.subscriptions.unsubscribe();
443
+ void this.cleanupPipecatClient();
430
444
  }
431
445
  /** Reset to idle (e.g. when modal re-opens so user can click Start Call). */
432
446
  resetToIdle() {
@@ -447,6 +461,7 @@ class VoiceAgentService {
447
461
  this.durationSubject.next('00:00');
448
462
  this.statusTextSubject.next('');
449
463
  this.callStateSubject.next('idle');
464
+ this.wasInActiveCallBeforeHide = false;
450
465
  await this.cleanupPipecatClient();
451
466
  }
452
467
  async connect(apiUrl, token, botId, conversationId, apiKey, eventToken, eventId, eventUrl, domainAuthority, usersApiUrl) {
@@ -678,6 +693,7 @@ class VoiceAgentService {
678
693
  this.callStartTime = 0;
679
694
  this.audioAnalyzer.stop();
680
695
  this.stopBotAudio();
696
+ this.wasInActiveCallBeforeHide = false;
681
697
  await this.cleanupPipecatClient();
682
698
  this.callStateSubject.next('ended');
683
699
  this.statusTextSubject.next('Call Ended');
@@ -729,6 +745,53 @@ class VoiceAgentService {
729
745
  this.durationInterval = null;
730
746
  }
731
747
  }
748
+ registerPageLifecycleListeners() {
749
+ if (!isPlatformBrowser(this.platformId) || typeof window === 'undefined' || typeof document === 'undefined') {
750
+ return;
751
+ }
752
+ const onVisibilityChange = () => {
753
+ if (document.hidden) {
754
+ this.wasInActiveCallBeforeHide = this.isCallActiveState(this.callStateSubject.value);
755
+ return;
756
+ }
757
+ if (this.wasInActiveCallBeforeHide) {
758
+ this.wasInActiveCallBeforeHide = false;
759
+ this.recoverAfterPageRestore();
760
+ }
761
+ };
762
+ const onPageShow = (event) => {
763
+ if (event.persisted && this.isCallActiveState(this.callStateSubject.value)) {
764
+ this.recoverAfterPageRestore();
765
+ }
766
+ };
767
+ document.addEventListener('visibilitychange', onVisibilityChange);
768
+ window.addEventListener('pageshow', onPageShow);
769
+ this.pageLifecycleCleanup.push(() => document.removeEventListener('visibilitychange', onVisibilityChange), () => window.removeEventListener('pageshow', onPageShow));
770
+ }
771
+ recoverAfterPageRestore() {
772
+ this.ngZone.run(() => {
773
+ if (!this.pcClient || !this.isCallActiveState(this.callStateSubject.value))
774
+ return;
775
+ // Older cached TS program state may not include this helper yet; call defensively.
776
+ this.audioAnalyzer.resumeContextIfSuspended?.();
777
+ this.startLocalMicAnalyzer();
778
+ this.resumeBotAudioPlayback();
779
+ });
780
+ }
781
+ resumeBotAudioPlayback() {
782
+ const botTrack = this.pcClient?.tracks()
783
+ ?.bot?.audio;
784
+ if (botTrack) {
785
+ this.setupBotAudioTrack(botTrack);
786
+ return;
787
+ }
788
+ if (this.botAudioElement) {
789
+ this.botAudioElement.play().catch((err) => console.warn('[HiveGpt Voice] Bot audio resume blocked', err));
790
+ }
791
+ }
792
+ isCallActiveState(state) {
793
+ return state === 'connecting' || state === 'connected' || state === 'listening' || state === 'talking';
794
+ }
732
795
  }
733
796
  VoiceAgentService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: VoiceAgentService, deps: [{ token: AudioAnalyzerService }, { token: PlatformTokenRefreshService }, { token: i0.NgZone }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
734
797
  VoiceAgentService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: VoiceAgentService, providedIn: 'root' });
@@ -3674,10 +3737,12 @@ class ChatDrawerComponent {
3674
3737
  }
3675
3738
  this.cdr.detectChanges();
3676
3739
  }
3677
- scrollToBottom() {
3678
- if (!this.autoScrollOnNewMessage) {
3740
+ scrollToBottom(force = false) {
3741
+ if (!force && !this.autoScrollOnNewMessage) {
3679
3742
  return;
3680
3743
  }
3744
+ if (!this.chatMain?.nativeElement)
3745
+ return;
3681
3746
  let counter = 0;
3682
3747
  const interval = setInterval(() => {
3683
3748
  this.chatMain.nativeElement.scrollTop =
@@ -3688,7 +3753,7 @@ class ChatDrawerComponent {
3688
3753
  }
3689
3754
  /** Scrolls the chat container so the top of the AI message with the given id is visible. */
3690
3755
  scrollToAiMessage(messageId) {
3691
- if (!this.autoScrollOnNewMessage || !this.chatMain?.nativeElement)
3756
+ if (!this.chatMain?.nativeElement)
3692
3757
  return;
3693
3758
  const container = this.chatMain.nativeElement;
3694
3759
  const el = container.querySelector(`[data-msg-id="${messageId}"]`);
@@ -4572,8 +4637,8 @@ class ChatDrawerComponent {
4572
4637
  setTimeout(() => this.scrollToAiMessage(messageId), 30);
4573
4638
  }
4574
4639
  else {
4575
- this.scrollToBottom();
4576
4640
  this.cdr.markForCheck();
4641
+ setTimeout(() => this.scrollToBottom(true), 0);
4577
4642
  }
4578
4643
  break;
4579
4644
  }