Skip to content

Commit 53c9f9d

Browse files
authored
Merge pull request #3 from vipinnair22/vipnai/ChatBotChanges
Changes to fix chat interaction experience
2 parents 4fa5303 + e31b442 commit 53c9f9d

File tree

7 files changed

+219
-166
lines changed

7 files changed

+219
-166
lines changed

src/frontend/src/components/agents/AgentPreview.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
flex-direction: column;
4444
align-items: center;
4545
justify-content: center;
46+
overflow: hidden;
4647

4748
box-sizing: border-box;
4849
width: 50%;

src/frontend/src/components/agents/AgentPreviewChatBot.module.css

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,85 @@
11
.chatContainer {
22
position: relative;
3+
4+
overflow: hidden;
35
display: flex;
46
flex-direction: column;
5-
width: 100%;
6-
overflow: hidden;
7-
background-color: var(--colorNeutralBackground1);
87
gap: 16px;
8+
9+
width: 100%;
10+
height: 100%;
11+
12+
> div:first-child {
13+
overflow: hidden;
14+
display: flex;
15+
flex: 1;
16+
flex-direction: column;
17+
gap: 16px;
18+
}
19+
20+
div[role='feed'] {
21+
overflow-y: auto;
22+
display: flex;
23+
flex: 1;
24+
flex-direction: column;
25+
26+
padding: 8px 12px 0;
27+
}
928
}
1029

11-
/* When there are messages, make the chat fill the viewport */
30+
/* When there are messages, make the chat fill the available space */
1231
.hasMessages {
13-
height: 100vh;
32+
height: 100%;
1433
}
1534

1635
.emptyChatContainer {
1736
height: auto;
1837
}
1938

39+
.userMessage {
40+
padding-top: 0;
41+
padding-bottom: 0;
42+
}
43+
2044
.copilotChatContainer {
45+
transform: translateY(0);
46+
2147
display: flex;
2248
flex-direction: column;
23-
gap: 1.5rem;
49+
flex: 1;
50+
51+
border-radius: 8px;
52+
53+
opacity: 1;
54+
background-color: var(--colorNeutralBackground3);
55+
56+
transition:
57+
opacity 0.3s ease-in-out,
58+
transform 0.3s ease-in-out;
59+
animation: fade-in 0.3s ease-in-out;
60+
}
61+
62+
@keyframes fade-in {
63+
from {
64+
transform: translateY(10px);
65+
opacity: 0;
66+
}
67+
68+
to {
69+
transform: translateY(0);
70+
opacity: 1;
71+
}
72+
}
73+
74+
/* Transition for empty state to messages state */
75+
.emptyChatContainer + div div:empty + .inputContainer,
76+
.emptyChatContainer + .copilotChatContainer {
77+
transition:
78+
opacity 0.3s ease-in-out,
79+
transform 0.3s ease-in-out;
2480
}
2581

82+
2683
.inputContainer {
2784
/* TODO: File an issue with Fluent team to allow for a safer override of the input box styles, or figure out a better approach to customize the chat input box styles */
2885
> div:first-child > div:first-child {
@@ -69,9 +126,15 @@
69126
max-height: 100%;
70127
}
71128

129+
.copilotChatMessage {
130+
display: flex;
131+
margin: 0 16px;
132+
padding: 4px 0;
133+
}
134+
72135
/* Transition for empty state to messages state */
73-
.emptyChatContainer + div div:empty + .inputContainer,
74-
.emptyChatContainer + div .copilotChatContainer {
136+
.emptyChatContainer .copilotChatContainer,
137+
.hasMessages .copilotChatContainer {
75138
transition:
76139
opacity 0.3s ease-in-out,
77140
transform 0.3s ease-in-out;

src/frontend/src/components/agents/AgentPreviewChatBot.tsx

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from "react";
1+
import React, { useState, useMemo } from "react";
22
import { AssistantMessage } from "./AssistantMessage";
33
import { UserMessage } from "./UserMessage";
44
import { ChatInput } from "./chatbot/ChatInput";
@@ -15,46 +15,56 @@ export function AgentPreviewChatBot({
1515
const [currentUserMessage, setCurrentUserMessage] = useState<
1616
string | undefined
1717
>();
18-
const messageListFromChatContext = chatContext.messageList || [];
18+
19+
const messageListFromChatContext = useMemo(
20+
() => chatContext.messageList ?? [],
21+
[chatContext.messageList]
22+
);
1923

2024
const onEditMessage = (messageId: string) => {
2125
const selectedMessage = messageListFromChatContext.find(
2226
(message) => !message.isAnswer && message.id === messageId
2327
)?.content;
2428
setCurrentUserMessage(selectedMessage);
2529
};
30+
2631
const isEmpty = messageListFromChatContext.length === 0;
2732

2833
return (
2934
<div
3035
className={clsx(
3136
styles.chatContainer,
32-
isEmpty ? styles.emptyChatContainer : styles.hasMessages
37+
isEmpty ? styles.emptyChatContainer : undefined
3338
)}
3439
>
35-
<div className={styles.copilotChatContainer}>
36-
{messageListFromChatContext.map((message, index, messageList) =>
37-
message.isAnswer ? (
38-
<AssistantMessage
39-
key={message.id}
40-
agentLogo={agentLogo}
41-
agentName={agentName}
42-
loadingState={
43-
index === messageList.length - 1 && chatContext.isResponding
44-
? "loading"
45-
: "none"
46-
}
47-
message={message}
48-
/>
49-
) : (
50-
<UserMessage
51-
key={message.id}
52-
message={message}
53-
onEditMessage={onEditMessage}
54-
/>
55-
)
56-
)}
57-
</div>
40+
{!isEmpty ? (
41+
<div className={styles.copilotChatContainer}>
42+
{messageListFromChatContext.map((message, index, messageList) =>
43+
message.isAnswer ? (
44+
<AssistantMessage
45+
key={message.id}
46+
agentLogo={agentLogo}
47+
agentName={agentName}
48+
loadingState={
49+
index === messageList.length - 1 && chatContext.isResponding
50+
? "loading"
51+
: "none"
52+
}
53+
message={message}
54+
/>
55+
) : (
56+
<UserMessage
57+
key={message.id}
58+
message={message}
59+
onEditMessage={onEditMessage}
60+
/>
61+
)
62+
)}
63+
</div>
64+
) : (
65+
// Empty div needed for proper animation when transitioning to non-empty state
66+
<div />
67+
)}
5868
<div className={styles.inputContainer}>
5969
<ChatInput
6070
currentUserMessage={currentUserMessage}
Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import { Button, Spinner } from "@fluentui/react-components";
22
import { bundleIcon, DeleteFilled, DeleteRegular } from "@fluentui/react-icons";
3+
import { CopilotMessageV2 as CopilotMessage } from "@fluentui-copilot/react-copilot-chat";
4+
import {
5+
ReferenceListV2 as ReferenceList,
6+
ReferenceOverflowButton,
7+
} from "@fluentui-copilot/react-reference";
38
import { Suspense } from "react";
49

510
import { Markdown } from "../core/Markdown";
611
import { UsageInfo } from "./UsageInfo";
712
import { IAssistantMessageProps } from "./chatbot/types";
813

9-
import styles from "./AssistantMessage.module.css";
14+
import styles from "./AgentPreviewChatBot.module.css";
15+
import { AgentIcon } from "./AgentIcon";
1016

1117
const DeleteIcon = bundleIcon(DeleteFilled, DeleteRegular);
1218

@@ -19,67 +25,62 @@ export function AssistantMessage({
1925
onDelete,
2026
}: IAssistantMessageProps): React.JSX.Element {
2127
const hasAnnotations = message.annotations && message.annotations.length > 0;
28+
const references = hasAnnotations
29+
? message.annotations?.map((annotation, index) => (
30+
<div key={index} className="reference-item">
31+
{annotation.text || annotation.file_name}
32+
</div>
33+
))
34+
: [];
2235

2336
return (
24-
<div className={styles.assistantMessageContainer}>
25-
<div className={styles.messageHeader}>
26-
<div className={styles.avatarAndName}>
27-
{agentLogo && (
28-
<div className={styles.avatar}>
29-
<img src={agentLogo} alt="" className={styles.avatarImage} />
30-
</div>
31-
)}
32-
<span className={styles.botName}>{agentName ?? "Bot"}</span>
33-
</div>
34-
<div className={styles.actions}>
37+
<CopilotMessage
38+
key={message.id}
39+
actions={
40+
<span>
3541
{onDelete && message.usageInfo && (
3642
<Button
37-
appearance="transparent"
43+
appearance="subtle"
3844
icon={<DeleteIcon />}
3945
onClick={() => {
4046
void onDelete(message.id);
4147
}}
4248
/>
4349
)}
44-
</div>
45-
</div>
46-
47-
<div className={styles.messageContent}>
48-
{loadingState === "loading" ? (
49-
<Spinner size="small" />
50-
) : (
51-
<Suspense fallback={<Spinner size="small" />}>
52-
<Markdown content={message.content} />
53-
</Suspense>
54-
)}
55-
</div>
56-
57-
{(hasAnnotations || (showUsageInfo && message.usageInfo)) && (
58-
<div className={styles.messageFootnote}>
50+
</span>
51+
}
52+
avatar={<AgentIcon alt="" iconName={agentLogo} />}
53+
className={styles.copilotChatMessage}
54+
disclaimer={<span>AI-generated content may be incorrect</span>}
55+
footnote={
56+
<>
5957
{hasAnnotations && (
60-
<div className={styles.references}>
61-
{/* Simple reference list implementation */}
62-
<div className={styles.referenceList}>
63-
{message.annotations?.map((annotation, index) => (
64-
<div key={index} className={styles.reference}>
65-
{annotation.text || annotation.file_name}
66-
</div>
67-
))}
68-
</div>
69-
</div>
58+
<ReferenceList
59+
maxVisibleReferences={3}
60+
minVisibleReferences={2}
61+
showLessButton={
62+
<ReferenceOverflowButton>Show Less</ReferenceOverflowButton>
63+
}
64+
showMoreButton={
65+
<ReferenceOverflowButton
66+
text={(overflowCount) => `+${overflowCount.toString()}`}
67+
/>
68+
}
69+
>
70+
{references}
71+
</ReferenceList>
7072
)}
71-
7273
{showUsageInfo && message.usageInfo && (
7374
<UsageInfo info={message.usageInfo} duration={message.duration} />
7475
)}
75-
</div>
76-
)}
77-
78-
{message.content.includes("disclaimer") && (
79-
<div className={styles.disclaimer}>
80-
<span>AI-generated content may contain errors</span>
81-
</div>
82-
)}
83-
</div>
76+
</>
77+
}
78+
loadingState={loadingState}
79+
name={agentName ?? "Bot"}
80+
>
81+
<Suspense fallback={<Spinner size="small" />}>
82+
<Markdown content={message.content} />
83+
</Suspense>
84+
</CopilotMessage>
8485
);
8586
}

0 commit comments

Comments
 (0)