@hivegpt/hiveai-angular 0.0.611 → 0.0.613

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.
@@ -1644,6 +1644,8 @@ class ChatDrawerComponent {
1644
1644
  /** Guards against our own programmatic scrolls triggering the userHasScrolled detection. */
1645
1645
  this.isProgrammaticScroll = false;
1646
1646
  this.scrollListenerTimer = null;
1647
+ /** Throttle timer for streaming chunk scroll — prevents firing on every chunk. */
1648
+ this.chunkScrollRAF = null;
1647
1649
  /** Connections list from host (e.g. from store selectConnectionsList). Each item has userId. When set, used for Connect/Request Sent/Disconnect button state. */
1648
1650
  this.connectionsList = [];
1649
1651
  /** Pending sent request user IDs from host (e.g. from store selectPendingConnectionsSentList). When set, used for Request Sent state. */
@@ -3426,7 +3428,8 @@ class ChatDrawerComponent {
3426
3428
  var _a, _b, _c, _d;
3427
3429
  const lastItem = this.chatLog[this.chatLog.length - 1];
3428
3430
  if (done) {
3429
- lastItem.message = this.processMessageForDisplay(lastItem.message);
3431
+ // Final pass with the complete raw text (aiResponse) for a clean render
3432
+ lastItem.message = this.processMessageForDisplay(this.aiResponse);
3430
3433
  this.chatLog.pop();
3431
3434
  this.chatLog.push(lastItem);
3432
3435
  if (allSuggestions === null || allSuggestions === void 0 ? void 0 : allSuggestions.length) {
@@ -3455,7 +3458,7 @@ class ChatDrawerComponent {
3455
3458
  allSuggestions.push(match === null || match === void 0 ? void 0 : match.replace(/<\/?sug>/g, ''));
3456
3459
  });
3457
3460
  }
3458
- lastItem.message = this.aiResponse;
3461
+ lastItem.message = this.processMessageForDisplay(this.aiResponse);
3459
3462
  this.cdr.markForCheck();
3460
3463
  }
3461
3464
  else {
@@ -3743,7 +3746,7 @@ class ChatDrawerComponent {
3743
3746
  }
3744
3747
  this.cdr.detectChanges();
3745
3748
  }
3746
- scrollToBottom(force = false, smooth = true) {
3749
+ scrollToBottom(force = false) {
3747
3750
  var _a;
3748
3751
  if (!force && !this.autoScrollOnNewMessage) {
3749
3752
  return;
@@ -3757,10 +3760,37 @@ class ChatDrawerComponent {
3757
3760
  this.isProgrammaticScroll = true;
3758
3761
  el.scrollTo({
3759
3762
  top: el.scrollHeight,
3760
- behavior: smooth ? 'smooth' : 'auto',
3763
+ behavior: 'smooth',
3764
+ });
3765
+ setTimeout(() => { this.isProgrammaticScroll = false; }, 400);
3766
+ }
3767
+ /**
3768
+ * Lightweight scroll-to-bottom used during streaming chunks.
3769
+ * Coalesces via requestAnimationFrame so we get at most one smooth
3770
+ * nudge per paint frame, no matter how fast chunks arrive.
3771
+ */
3772
+ scrollToBottomStreaming() {
3773
+ if (this.userHasScrolled)
3774
+ return;
3775
+ if (this.chunkScrollRAF !== null)
3776
+ return; // already queued
3777
+ this.chunkScrollRAF = requestAnimationFrame(() => {
3778
+ var _a;
3779
+ this.chunkScrollRAF = null;
3780
+ if (this.userHasScrolled)
3781
+ return;
3782
+ const el = (_a = this.chatMain) === null || _a === void 0 ? void 0 : _a.nativeElement;
3783
+ if (!el)
3784
+ return;
3785
+ // Small gap check: only scroll if we're within a reasonable distance from the bottom
3786
+ const gap = el.scrollHeight - el.scrollTop - el.clientHeight;
3787
+ if (gap <= 0)
3788
+ return; // already at bottom
3789
+ this.isProgrammaticScroll = true;
3790
+ // Use a quick smooth nudge rather than instant jump
3791
+ el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });
3792
+ setTimeout(() => { this.isProgrammaticScroll = false; }, 300);
3761
3793
  });
3762
- // Release the guard after the smooth scroll settles
3763
- setTimeout(() => { this.isProgrammaticScroll = false; }, smooth ? 400 : 50);
3764
3794
  }
3765
3795
  /** Scrolls the chat container so the top of the AI message with the given id is visible. */
3766
3796
  scrollToAiMessage(messageId) {
@@ -4650,7 +4680,7 @@ class ChatDrawerComponent {
4650
4680
  return false;
4651
4681
  }
4652
4682
  handleAskWebSocketMessage(rawMessage) {
4653
- var _a, _b, _c, _d, _e;
4683
+ var _a, _b, _c, _d, _e, _f;
4654
4684
  if (!rawMessage)
4655
4685
  return;
4656
4686
  let parsed = null;
@@ -4672,7 +4702,9 @@ class ChatDrawerComponent {
4672
4702
  return;
4673
4703
  const isNewAiMessage = !this.chatLog.find((p) => p._id == messageId);
4674
4704
  const currentChatMessage = this.upsertAiChatMessage(messageId);
4675
- currentChatMessage.message = `${currentChatMessage.message || ''}${(payload === null || payload === void 0 ? void 0 : payload.text) || ''}`;
4705
+ // Accumulate raw text and render markdown in real-time
4706
+ currentChatMessage.rawMessage = `${currentChatMessage.rawMessage || ''}${(payload === null || payload === void 0 ? void 0 : payload.text) || ''}`;
4707
+ currentChatMessage.message = this.processMessageForDisplay(currentChatMessage.rawMessage);
4676
4708
  if (isNewAiMessage) {
4677
4709
  // First chunk: hide Thinking indicator and scroll to the message start
4678
4710
  this.isChatingWithAi = false;
@@ -4681,7 +4713,7 @@ class ChatDrawerComponent {
4681
4713
  }
4682
4714
  else {
4683
4715
  this.cdr.markForCheck();
4684
- setTimeout(() => this.scrollToBottom(true, false), 0);
4716
+ this.scrollToBottomStreaming();
4685
4717
  }
4686
4718
  break;
4687
4719
  }
@@ -4692,19 +4724,21 @@ class ChatDrawerComponent {
4692
4724
  break;
4693
4725
  }
4694
4726
  const currentChatMessage = this.upsertAiChatMessage(messageId);
4695
- const finalAnswer = (_c = (_b = payload === null || payload === void 0 ? void 0 : payload.text) !== null && _b !== void 0 ? _b : currentChatMessage.message) !== null && _c !== void 0 ? _c : '';
4727
+ const finalAnswer = (_d = (_c = (_b = payload === null || payload === void 0 ? void 0 : payload.text) !== null && _b !== void 0 ? _b : currentChatMessage.rawMessage) !== null && _c !== void 0 ? _c : currentChatMessage.message) !== null && _d !== void 0 ? _d : '';
4696
4728
  const hasCardResponse = this.applyToolResultCardMessage(currentChatMessage, messageId, finalAnswer, payload === null || payload === void 0 ? void 0 : payload.tool_results);
4697
4729
  if (!hasCardResponse) {
4698
4730
  currentChatMessage.type = 'ai';
4699
4731
  currentChatMessage.message = this.processMessageForDisplay(finalAnswer);
4700
4732
  }
4733
+ // Clean up raw accumulator
4734
+ delete currentChatMessage.rawMessage;
4701
4735
  if (Array.isArray(payload === null || payload === void 0 ? void 0 : payload.suggestions)) {
4702
4736
  currentChatMessage.relatedListItems = payload.suggestions;
4703
4737
  }
4704
4738
  if (Array.isArray(payload === null || payload === void 0 ? void 0 : payload.web_results)) {
4705
4739
  currentChatMessage.sourcesList = payload.web_results;
4706
- currentChatMessage.displayedSources = (_d = payload.web_results) === null || _d === void 0 ? void 0 : _d.slice(0, 3);
4707
- currentChatMessage.remainingSources = (_e = payload.web_results) === null || _e === void 0 ? void 0 : _e.slice(3);
4740
+ currentChatMessage.displayedSources = (_e = payload.web_results) === null || _e === void 0 ? void 0 : _e.slice(0, 3);
4741
+ currentChatMessage.remainingSources = (_f = payload.web_results) === null || _f === void 0 ? void 0 : _f.slice(3);
4708
4742
  }
4709
4743
  if (payload === null || payload === void 0 ? void 0 : payload.tool_results) {
4710
4744
  currentChatMessage.toolResults = payload.tool_results;
@@ -4712,13 +4746,10 @@ class ChatDrawerComponent {
4712
4746
  this.showFeedBackIconsIndex = this.chatLog.length - 1;
4713
4747
  this.activeAskMessageId = '';
4714
4748
  this.isChatingWithAi = false;
4715
- // For card responses (tool results), scroll to the message text bubble
4716
- // instead of the very bottom avoids cards pushing the view too far down
4717
- if (hasCardResponse && messageId) {
4718
- this.cdr.markForCheck();
4719
- setTimeout(() => this.scrollToAiMessage(messageId), 30);
4720
- }
4721
- else {
4749
+ // For card responses (tool results), don't scroll at all
4750
+ // user is already viewing the AI text from the streaming phase,
4751
+ // cards render below and the user can scroll down to see them.
4752
+ if (!hasCardResponse) {
4722
4753
  this.scrollToBottom();
4723
4754
  }
4724
4755
  this.focusOnTextarea();