@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.
- package/esm2020/lib/components/chat-drawer/chat-drawer.component.mjs +63 -17
- package/fesm2015/hivegpt-hiveai-angular.mjs +67 -19
- package/fesm2015/hivegpt-hiveai-angular.mjs.map +1 -1
- package/fesm2020/hivegpt-hiveai-angular.mjs +62 -16
- package/fesm2020/hivegpt-hiveai-angular.mjs.map +1 -1
- package/lib/components/chat-drawer/chat-drawer.component.d.ts +6 -1
- package/lib/components/chat-drawer/chat-drawer.component.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
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
|
|
3774
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|