@hivegpt/hiveai-angular 0.0.610 → 0.0.612

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.
@@ -1609,6 +1609,11 @@ class ChatDrawerComponent {
1609
1609
  this.displayAvatarUrl = 'https://www.jotform.com/uploads/mehmetkarakasli/form_files/1564593667676a8e85f23758.86945537_icon.png';
1610
1610
  /** When true, chat auto-scrolls to bottom when new content is added. Default: false (no auto scroll). */
1611
1611
  this.autoScrollOnNewMessage = false;
1612
+ /** Tracks whether the user has manually scrolled up, suppressing auto-scroll until they scroll back to bottom or send a new message. */
1613
+ this.userHasScrolled = false;
1614
+ /** Guards against our own programmatic scrolls triggering the userHasScrolled detection. */
1615
+ this.isProgrammaticScroll = false;
1616
+ this.scrollListenerTimer = null;
1612
1617
  /** Connections list from host (e.g. from store selectConnectionsList). Each item has userId. When set, used for Connect/Request Sent/Disconnect button state. */
1613
1618
  this.connectionsList = [];
1614
1619
  /** Pending sent request user IDs from host (e.g. from store selectPendingConnectionsSentList). When set, used for Request Sent state. */
@@ -3352,6 +3357,8 @@ class ChatDrawerComponent {
3352
3357
  if (!this.input || this.loading) {
3353
3358
  return;
3354
3359
  }
3360
+ // Reset auto-scroll suppression when user sends a new message
3361
+ this.userHasScrolled = false;
3355
3362
  this.chatLog.push({
3356
3363
  type: 'user',
3357
3364
  message: this.processMessageForDisplay(this.input),
@@ -3375,6 +3382,8 @@ class ChatDrawerComponent {
3375
3382
  if (!inputMsg || this.loading) {
3376
3383
  return;
3377
3384
  }
3385
+ // Reset auto-scroll suppression when user sends a new message
3386
+ this.userHasScrolled = false;
3378
3387
  try {
3379
3388
  chat.relatedListItems = [];
3380
3389
  this.cdr.detectChanges();
@@ -3432,7 +3441,8 @@ class ChatDrawerComponent {
3432
3441
  reader.read().then(({ done, value }) => {
3433
3442
  const lastItem = this.chatLog[this.chatLog.length - 1];
3434
3443
  if (done) {
3435
- lastItem.message = this.processMessageForDisplay(lastItem.message);
3444
+ // Final pass with the complete raw text (aiResponse) for a clean render
3445
+ lastItem.message = this.processMessageForDisplay(this.aiResponse);
3436
3446
  this.chatLog.pop();
3437
3447
  this.chatLog.push(lastItem);
3438
3448
  if (allSuggestions?.length) {
@@ -3461,7 +3471,7 @@ class ChatDrawerComponent {
3461
3471
  allSuggestions.push(match?.replace(/<\/?sug>/g, ''));
3462
3472
  });
3463
3473
  }
3464
- lastItem.message = this.aiResponse;
3474
+ lastItem.message = this.processMessageForDisplay(this.aiResponse);
3465
3475
  this.cdr.markForCheck();
3466
3476
  }
3467
3477
  else {
@@ -3748,30 +3758,41 @@ class ChatDrawerComponent {
3748
3758
  }
3749
3759
  this.cdr.detectChanges();
3750
3760
  }
3751
- scrollToBottom(force = false) {
3761
+ scrollToBottom(force = false, smooth = true) {
3752
3762
  if (!force && !this.autoScrollOnNewMessage) {
3753
3763
  return;
3754
3764
  }
3765
+ if (this.userHasScrolled) {
3766
+ return;
3767
+ }
3755
3768
  if (!this.chatMain?.nativeElement)
3756
3769
  return;
3757
- let counter = 0;
3758
- const interval = setInterval(() => {
3759
- this.chatMain.nativeElement.scrollTop =
3760
- this.chatMain.nativeElement.scrollHeight;
3761
- if (counter++ > 5)
3762
- clearInterval(interval);
3763
- }, 5);
3770
+ const el = this.chatMain.nativeElement;
3771
+ this.isProgrammaticScroll = true;
3772
+ el.scrollTo({
3773
+ top: el.scrollHeight,
3774
+ behavior: smooth ? 'smooth' : 'auto',
3775
+ });
3776
+ // Release the guard after the smooth scroll settles
3777
+ setTimeout(() => { this.isProgrammaticScroll = false; }, smooth ? 400 : 50);
3764
3778
  }
3765
3779
  /** Scrolls the chat container so the top of the AI message with the given id is visible. */
3766
3780
  scrollToAiMessage(messageId) {
3781
+ if (this.userHasScrolled)
3782
+ return;
3767
3783
  if (!this.chatMain?.nativeElement)
3768
3784
  return;
3769
3785
  const container = this.chatMain.nativeElement;
3770
3786
  const el = container.querySelector(`[data-msg-id="${messageId}"]`);
3771
3787
  if (!el)
3772
3788
  return;
3773
- const offset = el.getBoundingClientRect().top - container.getBoundingClientRect().top;
3774
- container.scrollTop = container.scrollTop + offset - 8;
3789
+ const targetTop = el.getBoundingClientRect().top - container.getBoundingClientRect().top;
3790
+ this.isProgrammaticScroll = true;
3791
+ container.scrollTo({
3792
+ top: container.scrollTop + targetTop - 8,
3793
+ behavior: 'smooth',
3794
+ });
3795
+ setTimeout(() => { this.isProgrammaticScroll = false; }, 400);
3775
3796
  }
3776
3797
  focusOnTextarea() {
3777
3798
  setTimeout(() => {
@@ -3787,6 +3808,21 @@ class ChatDrawerComponent {
3787
3808
  }
3788
3809
  }
3789
3810
  ngAfterViewInit() {
3811
+ // Detect user manual scroll to suppress auto-scroll (debounced, ignores programmatic scrolls)
3812
+ if (this.chatMain?.nativeElement) {
3813
+ this.chatMain.nativeElement.addEventListener('scroll', () => {
3814
+ if (this.isProgrammaticScroll)
3815
+ return;
3816
+ clearTimeout(this.scrollListenerTimer);
3817
+ this.scrollListenerTimer = setTimeout(() => {
3818
+ const el = this.chatMain?.nativeElement;
3819
+ if (!el)
3820
+ return;
3821
+ const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
3822
+ this.userHasScrolled = !atBottom;
3823
+ }, 100);
3824
+ });
3825
+ }
3790
3826
  // Check if the drawer is initially open and apply overflow hidden to body if so
3791
3827
  if (this.drawer.opened) {
3792
3828
  this.setBodyOverflow();
@@ -4640,7 +4676,9 @@ class ChatDrawerComponent {
4640
4676
  return;
4641
4677
  const isNewAiMessage = !this.chatLog.find((p) => p._id == messageId);
4642
4678
  const currentChatMessage = this.upsertAiChatMessage(messageId);
4643
- currentChatMessage.message = `${currentChatMessage.message || ''}${payload?.text || ''}`;
4679
+ // Accumulate raw text and render markdown in real-time
4680
+ currentChatMessage.rawMessage = `${currentChatMessage.rawMessage || ''}${payload?.text || ''}`;
4681
+ currentChatMessage.message = this.processMessageForDisplay(currentChatMessage.rawMessage);
4644
4682
  if (isNewAiMessage) {
4645
4683
  // First chunk: hide Thinking indicator and scroll to the message start
4646
4684
  this.isChatingWithAi = false;
@@ -4649,7 +4687,7 @@ class ChatDrawerComponent {
4649
4687
  }
4650
4688
  else {
4651
4689
  this.cdr.markForCheck();
4652
- setTimeout(() => this.scrollToBottom(true), 0);
4690
+ setTimeout(() => this.scrollToBottom(true, false), 0);
4653
4691
  }
4654
4692
  break;
4655
4693
  }
@@ -4660,12 +4698,14 @@ class ChatDrawerComponent {
4660
4698
  break;
4661
4699
  }
4662
4700
  const currentChatMessage = this.upsertAiChatMessage(messageId);
4663
- const finalAnswer = payload?.text ?? currentChatMessage.message ?? '';
4701
+ const finalAnswer = payload?.text ?? currentChatMessage.rawMessage ?? currentChatMessage.message ?? '';
4664
4702
  const hasCardResponse = this.applyToolResultCardMessage(currentChatMessage, messageId, finalAnswer, payload?.tool_results);
4665
4703
  if (!hasCardResponse) {
4666
4704
  currentChatMessage.type = 'ai';
4667
4705
  currentChatMessage.message = this.processMessageForDisplay(finalAnswer);
4668
4706
  }
4707
+ // Clean up raw accumulator
4708
+ delete currentChatMessage.rawMessage;
4669
4709
  if (Array.isArray(payload?.suggestions)) {
4670
4710
  currentChatMessage.relatedListItems = payload.suggestions;
4671
4711
  }
@@ -4680,7 +4720,12 @@ class ChatDrawerComponent {
4680
4720
  this.showFeedBackIconsIndex = this.chatLog.length - 1;
4681
4721
  this.activeAskMessageId = '';
4682
4722
  this.isChatingWithAi = false;
4683
- this.scrollToBottom();
4723
+ // For card responses (tool results), don't scroll at all —
4724
+ // user is already viewing the AI text from the streaming phase,
4725
+ // cards render below and the user can scroll down to see them.
4726
+ if (!hasCardResponse) {
4727
+ this.scrollToBottom();
4728
+ }
4684
4729
  this.focusOnTextarea();
4685
4730
  this.cdr.markForCheck();
4686
4731
  break;
@@ -4797,6 +4842,7 @@ class ChatDrawerComponent {
4797
4842
  return Object.keys(obj).map((key) => ({ key, value: obj[key] }));
4798
4843
  }
4799
4844
  startNewConversation() {
4845
+ this.userHasScrolled = false;
4800
4846
  this.conversationKey = this.conversationService.getKey(this.botId, true, this.eventId);
4801
4847
  this.chatLog = [this.chatLog[0]];
4802
4848
  this.isChatingWithAi = false;