@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.
- package/esm2020/lib/components/chat-drawer/chat-drawer.component.mjs +47 -17
- package/fesm2015/hivegpt-hiveai-angular.mjs +50 -19
- package/fesm2015/hivegpt-hiveai-angular.mjs.map +1 -1
- package/fesm2020/hivegpt-hiveai-angular.mjs +46 -16
- package/fesm2020/hivegpt-hiveai-angular.mjs.map +1 -1
- package/lib/components/chat-drawer/chat-drawer.component.d.ts +9 -1
- package/lib/components/chat-drawer/chat-drawer.component.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1614,6 +1614,8 @@ class ChatDrawerComponent {
|
|
|
1614
1614
|
/** Guards against our own programmatic scrolls triggering the userHasScrolled detection. */
|
|
1615
1615
|
this.isProgrammaticScroll = false;
|
|
1616
1616
|
this.scrollListenerTimer = null;
|
|
1617
|
+
/** Throttle timer for streaming chunk scroll — prevents firing on every chunk. */
|
|
1618
|
+
this.chunkScrollRAF = null;
|
|
1617
1619
|
/** Connections list from host (e.g. from store selectConnectionsList). Each item has userId. When set, used for Connect/Request Sent/Disconnect button state. */
|
|
1618
1620
|
this.connectionsList = [];
|
|
1619
1621
|
/** Pending sent request user IDs from host (e.g. from store selectPendingConnectionsSentList). When set, used for Request Sent state. */
|
|
@@ -3441,7 +3443,8 @@ class ChatDrawerComponent {
|
|
|
3441
3443
|
reader.read().then(({ done, value }) => {
|
|
3442
3444
|
const lastItem = this.chatLog[this.chatLog.length - 1];
|
|
3443
3445
|
if (done) {
|
|
3444
|
-
|
|
3446
|
+
// Final pass with the complete raw text (aiResponse) for a clean render
|
|
3447
|
+
lastItem.message = this.processMessageForDisplay(this.aiResponse);
|
|
3445
3448
|
this.chatLog.pop();
|
|
3446
3449
|
this.chatLog.push(lastItem);
|
|
3447
3450
|
if (allSuggestions?.length) {
|
|
@@ -3470,7 +3473,7 @@ class ChatDrawerComponent {
|
|
|
3470
3473
|
allSuggestions.push(match?.replace(/<\/?sug>/g, ''));
|
|
3471
3474
|
});
|
|
3472
3475
|
}
|
|
3473
|
-
lastItem.message = this.aiResponse;
|
|
3476
|
+
lastItem.message = this.processMessageForDisplay(this.aiResponse);
|
|
3474
3477
|
this.cdr.markForCheck();
|
|
3475
3478
|
}
|
|
3476
3479
|
else {
|
|
@@ -3757,7 +3760,7 @@ class ChatDrawerComponent {
|
|
|
3757
3760
|
}
|
|
3758
3761
|
this.cdr.detectChanges();
|
|
3759
3762
|
}
|
|
3760
|
-
scrollToBottom(force = false
|
|
3763
|
+
scrollToBottom(force = false) {
|
|
3761
3764
|
if (!force && !this.autoScrollOnNewMessage) {
|
|
3762
3765
|
return;
|
|
3763
3766
|
}
|
|
@@ -3770,10 +3773,36 @@ class ChatDrawerComponent {
|
|
|
3770
3773
|
this.isProgrammaticScroll = true;
|
|
3771
3774
|
el.scrollTo({
|
|
3772
3775
|
top: el.scrollHeight,
|
|
3773
|
-
behavior:
|
|
3776
|
+
behavior: 'smooth',
|
|
3777
|
+
});
|
|
3778
|
+
setTimeout(() => { this.isProgrammaticScroll = false; }, 400);
|
|
3779
|
+
}
|
|
3780
|
+
/**
|
|
3781
|
+
* Lightweight scroll-to-bottom used during streaming chunks.
|
|
3782
|
+
* Coalesces via requestAnimationFrame so we get at most one smooth
|
|
3783
|
+
* nudge per paint frame, no matter how fast chunks arrive.
|
|
3784
|
+
*/
|
|
3785
|
+
scrollToBottomStreaming() {
|
|
3786
|
+
if (this.userHasScrolled)
|
|
3787
|
+
return;
|
|
3788
|
+
if (this.chunkScrollRAF !== null)
|
|
3789
|
+
return; // already queued
|
|
3790
|
+
this.chunkScrollRAF = requestAnimationFrame(() => {
|
|
3791
|
+
this.chunkScrollRAF = null;
|
|
3792
|
+
if (this.userHasScrolled)
|
|
3793
|
+
return;
|
|
3794
|
+
const el = this.chatMain?.nativeElement;
|
|
3795
|
+
if (!el)
|
|
3796
|
+
return;
|
|
3797
|
+
// Small gap check: only scroll if we're within a reasonable distance from the bottom
|
|
3798
|
+
const gap = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
3799
|
+
if (gap <= 0)
|
|
3800
|
+
return; // already at bottom
|
|
3801
|
+
this.isProgrammaticScroll = true;
|
|
3802
|
+
// Use a quick smooth nudge rather than instant jump
|
|
3803
|
+
el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });
|
|
3804
|
+
setTimeout(() => { this.isProgrammaticScroll = false; }, 300);
|
|
3774
3805
|
});
|
|
3775
|
-
// Release the guard after the smooth scroll settles
|
|
3776
|
-
setTimeout(() => { this.isProgrammaticScroll = false; }, smooth ? 400 : 50);
|
|
3777
3806
|
}
|
|
3778
3807
|
/** Scrolls the chat container so the top of the AI message with the given id is visible. */
|
|
3779
3808
|
scrollToAiMessage(messageId) {
|
|
@@ -4675,7 +4704,9 @@ class ChatDrawerComponent {
|
|
|
4675
4704
|
return;
|
|
4676
4705
|
const isNewAiMessage = !this.chatLog.find((p) => p._id == messageId);
|
|
4677
4706
|
const currentChatMessage = this.upsertAiChatMessage(messageId);
|
|
4678
|
-
|
|
4707
|
+
// Accumulate raw text and render markdown in real-time
|
|
4708
|
+
currentChatMessage.rawMessage = `${currentChatMessage.rawMessage || ''}${payload?.text || ''}`;
|
|
4709
|
+
currentChatMessage.message = this.processMessageForDisplay(currentChatMessage.rawMessage);
|
|
4679
4710
|
if (isNewAiMessage) {
|
|
4680
4711
|
// First chunk: hide Thinking indicator and scroll to the message start
|
|
4681
4712
|
this.isChatingWithAi = false;
|
|
@@ -4684,7 +4715,7 @@ class ChatDrawerComponent {
|
|
|
4684
4715
|
}
|
|
4685
4716
|
else {
|
|
4686
4717
|
this.cdr.markForCheck();
|
|
4687
|
-
|
|
4718
|
+
this.scrollToBottomStreaming();
|
|
4688
4719
|
}
|
|
4689
4720
|
break;
|
|
4690
4721
|
}
|
|
@@ -4695,12 +4726,14 @@ class ChatDrawerComponent {
|
|
|
4695
4726
|
break;
|
|
4696
4727
|
}
|
|
4697
4728
|
const currentChatMessage = this.upsertAiChatMessage(messageId);
|
|
4698
|
-
const finalAnswer = payload?.text ?? currentChatMessage.message ?? '';
|
|
4729
|
+
const finalAnswer = payload?.text ?? currentChatMessage.rawMessage ?? currentChatMessage.message ?? '';
|
|
4699
4730
|
const hasCardResponse = this.applyToolResultCardMessage(currentChatMessage, messageId, finalAnswer, payload?.tool_results);
|
|
4700
4731
|
if (!hasCardResponse) {
|
|
4701
4732
|
currentChatMessage.type = 'ai';
|
|
4702
4733
|
currentChatMessage.message = this.processMessageForDisplay(finalAnswer);
|
|
4703
4734
|
}
|
|
4735
|
+
// Clean up raw accumulator
|
|
4736
|
+
delete currentChatMessage.rawMessage;
|
|
4704
4737
|
if (Array.isArray(payload?.suggestions)) {
|
|
4705
4738
|
currentChatMessage.relatedListItems = payload.suggestions;
|
|
4706
4739
|
}
|
|
@@ -4715,13 +4748,10 @@ class ChatDrawerComponent {
|
|
|
4715
4748
|
this.showFeedBackIconsIndex = this.chatLog.length - 1;
|
|
4716
4749
|
this.activeAskMessageId = '';
|
|
4717
4750
|
this.isChatingWithAi = false;
|
|
4718
|
-
// For card responses (tool results), scroll
|
|
4719
|
-
//
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
setTimeout(() => this.scrollToAiMessage(messageId), 30);
|
|
4723
|
-
}
|
|
4724
|
-
else {
|
|
4751
|
+
// For card responses (tool results), don't scroll at all —
|
|
4752
|
+
// user is already viewing the AI text from the streaming phase,
|
|
4753
|
+
// cards render below and the user can scroll down to see them.
|
|
4754
|
+
if (!hasCardResponse) {
|
|
4725
4755
|
this.scrollToBottom();
|
|
4726
4756
|
}
|
|
4727
4757
|
this.focusOnTextarea();
|