aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ChatBox/ChatBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/ChatBox/ChatBox.tsx')
-rw-r--r--src/client/views/nodes/ChatBox/ChatBox.tsx101
1 files changed, 72 insertions, 29 deletions
diff --git a/src/client/views/nodes/ChatBox/ChatBox.tsx b/src/client/views/nodes/ChatBox/ChatBox.tsx
index 36416a330..4a98f8dc1 100644
--- a/src/client/views/nodes/ChatBox/ChatBox.tsx
+++ b/src/client/views/nodes/ChatBox/ChatBox.tsx
@@ -21,6 +21,8 @@ import { DocumentManager } from '../../../util/DocumentManager';
import { v4 as uuidv4 } from 'uuid';
import { chunk } from 'lodash';
import { DocUtils } from '../../../documents/DocUtils';
+import { createRef } from 'react';
+import { ClientUtils } from '../../../../ClientUtils';
dotenv.config();
@@ -37,10 +39,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable private linked_csv_files: { filename: string; id: string; text: string }[] = [];
private openai: OpenAI;
private vectorstore_id: string;
- private documents: AI_Document[] = [];
- private _oldWheel: any;
private vectorstore: Vectorstore;
private agent: Agent; // Add the ChatBot instance
+ private _oldWheel: HTMLDivElement | null = null;
+ private messagesRef: React.RefObject<HTMLDivElement>;
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(ChatBox, fieldKey);
@@ -59,6 +61,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
this.vectorstore = new Vectorstore(this.vectorstore_id, this.retrieveDocIds);
this.agent = new Agent(this.vectorstore, this.retrieveSummaries, this.retrieveFormattedHistory, this.retrieveCSVData, this.addLinkedUrlDoc);
+ this.messagesRef = React.createRef<HTMLDivElement>();
reaction(
() => this.history.map((msg: AssistantMessage) => ({ role: msg.role, content: msg.content, follow_up_questions: msg.follow_up_questions, citations: msg.citations })),
@@ -133,6 +136,23 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return new OpenAI(configuration);
}
+ addScrollListener = () => {
+ if (this.messagesRef.current) {
+ this.messagesRef.current.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ }
+ };
+
+ removeScrollListener = () => {
+ if (this.messagesRef.current) {
+ this.messagesRef.current.removeEventListener('wheel', this.onPassiveWheel);
+ }
+ };
+
+ scrollToBottom = () => {
+ if (this.messagesRef.current) {
+ }
+ };
+
onPassiveWheel = (e: WheelEvent) => {
if (this._props.isContentActive()) {
e.stopPropagation();
@@ -160,6 +180,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.current_message = { ...this.current_message, processing_info: update };
}
});
+ this.scrollToBottom();
};
const finalMessage = await this.agent.askAgent(trimmedText, onUpdate);
@@ -176,8 +197,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.history.push({ role: ASSISTANT_ROLE.ASSISTANT, content: [{ index: 0, type: TEXT_TYPE.ERROR, text: 'Sorry, I encountered an error while processing your request.', citation_ids: null }], processing_info: [] });
} finally {
this.isLoading = false;
+ this.scrollToBottom();
}
}
+ this.scrollToBottom();
};
@action
@@ -202,6 +225,11 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
doc.chunk_simpl = JSON.stringify({ chunks: [chunkToAdd] });
};
+ @computed
+ get userName() {
+ return ClientUtils.CurrentUserEmail;
+ }
+
@action
handleCitationClick = (citation: Citation) => {
console.log('Citation clicked:', citation);
@@ -276,6 +304,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return highlight_doc;
};
+ componentDidUpdate() {
+ this.scrollToBottom();
+ }
+
componentDidMount() {
this._props.setContentViewBox?.(this);
if (this.dataDoc.data) {
@@ -332,6 +364,11 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
console.log('Deleted docs: ', change.oldValue);
}
});
+ this.addScrollListener();
+ }
+
+ componentWillUnmount() {
+ this.removeScrollListener();
}
@computed
@@ -411,35 +448,41 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
render() {
return (
- <div className="chatBox">
- {this.isUploadingDocs && <div className="uploading-overlay"></div>}
- <div
- className="scroll-box chat-content"
- ref={r => {
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = r;
- r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
- }}>
- <div className="messages">
- {this.history.map((message, index) => (
- <MessageComponentBox key={index} message={message} index={index} onFollowUpClick={this.handleFollowUpClick} onCitationClick={this.handleCitationClick} updateMessageCitations={this.updateMessageCitations} />
- ))}
- {!this.current_message ? null : (
- <MessageComponentBox
- key={this.history.length}
- message={this.current_message}
- index={this.history.length}
- onFollowUpClick={this.handleFollowUpClick}
- onCitationClick={this.handleCitationClick}
- updateMessageCitations={this.updateMessageCitations}
- />
- )}
+ <div className="chat-box">
+ {this.isUploadingDocs && (
+ <div className="uploading-overlay">
+ <div className="spinner"></div>
</div>
+ )}
+ <div className="chat-header">
+ <h2>{this.userName()}'s AI Assistant</h2>
</div>
- <form onSubmit={this.askGPT} className="chat-form">
- <input type="text" name="messageInput" autoComplete="off" placeholder="Type a message..." value={this.inputValue} onChange={e => (this.inputValue = e.target.value)} />
- <button type="submit" disabled={this.isLoading}>
- {this.isLoading ? 'Thinking...' : 'Send'}
+ <div className="chat-messages" ref={this.messagesRef}>
+ {this.history.map((message, index) => (
+ <MessageComponentBox key={index} message={message} index={index} onFollowUpClick={this.handleFollowUpClick} onCitationClick={this.handleCitationClick} updateMessageCitations={this.updateMessageCitations} />
+ ))}
+ {this.current_message && (
+ <MessageComponentBox
+ key={this.history.length}
+ message={this.current_message}
+ index={this.history.length}
+ onFollowUpClick={this.handleFollowUpClick}
+ onCitationClick={this.handleCitationClick}
+ updateMessageCitations={this.updateMessageCitations}
+ />
+ )}
+ </div>
+ <form onSubmit={this.askGPT} className="chat-input">
+ <input type="text" name="messageInput" autoComplete="off" placeholder="Type your message here..." value={this.inputValue} onChange={e => (this.inputValue = e.target.value)} />
+ <button className="submit-button" type="submit" disabled={this.isLoading}>
+ {this.isLoading ? (
+ <div className="spinner"></div>
+ ) : (
+ <svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
+ <line x1="22" y1="2" x2="11" y2="13"></line>
+ <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
+ </svg>
+ )}
</button>
</form>
</div>