@patternfly/chatbot 6.3.2 → 6.4.0-prerelease.10
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/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +8 -14
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +53 -2
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +25 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +22 -0
- package/dist/cjs/ChatbotHeader/index.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/index.js +1 -0
- package/dist/cjs/FileDropZone/FileDropZone.d.ts +1 -2
- package/dist/cjs/Message/Message.d.ts +9 -2
- package/dist/cjs/Message/Message.js +40 -34
- package/dist/cjs/Message/Message.test.js +37 -0
- package/dist/cjs/Message/MessageInput.d.ts +3 -1
- package/dist/cjs/Message/MessageInput.js +2 -2
- package/dist/cjs/MessageBar/AttachButton.d.ts +2 -2
- package/dist/cjs/MessageBar/MessageBar.d.ts +2 -2
- package/dist/cjs/MessageBox/JumpButton.d.ts +5 -0
- package/dist/cjs/MessageBox/JumpButton.js +1 -1
- package/dist/cjs/MessageBox/JumpButton.test.js +4 -4
- package/dist/cjs/MessageBox/MessageBox.d.ts +9 -0
- package/dist/cjs/MessageBox/MessageBox.js +2 -2
- package/dist/cjs/MessageBox/MessageBox.test.js +2 -2
- package/dist/cjs/MessageDivider/MessageDivider.d.ts +9 -0
- package/dist/cjs/MessageDivider/MessageDivider.js +23 -0
- package/dist/cjs/MessageDivider/MessageDivider.test.d.ts +1 -0
- package/dist/cjs/MessageDivider/MessageDivider.test.js +29 -0
- package/dist/cjs/MessageDivider/index.d.ts +2 -0
- package/dist/cjs/MessageDivider/index.js +23 -0
- package/dist/cjs/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/cjs/ResponseActions/ResponseActions.js +4 -4
- package/dist/cjs/ResponseActions/ResponseActions.test.js +6 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/css/main.css +103 -81
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/MessageDivider/package.json +1 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +10 -16
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +54 -3
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +22 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +20 -0
- package/dist/esm/ChatbotHeader/index.d.ts +1 -0
- package/dist/esm/ChatbotHeader/index.js +1 -0
- package/dist/esm/FileDropZone/FileDropZone.d.ts +1 -2
- package/dist/esm/Message/Message.d.ts +9 -2
- package/dist/esm/Message/Message.js +40 -34
- package/dist/esm/Message/Message.test.js +37 -0
- package/dist/esm/Message/MessageInput.d.ts +3 -1
- package/dist/esm/Message/MessageInput.js +2 -2
- package/dist/esm/MessageBar/AttachButton.d.ts +2 -2
- package/dist/esm/MessageBar/MessageBar.d.ts +2 -2
- package/dist/esm/MessageBox/JumpButton.d.ts +5 -0
- package/dist/esm/MessageBox/JumpButton.js +1 -1
- package/dist/esm/MessageBox/JumpButton.test.js +4 -4
- package/dist/esm/MessageBox/MessageBox.d.ts +9 -0
- package/dist/esm/MessageBox/MessageBox.js +2 -2
- package/dist/esm/MessageBox/MessageBox.test.js +2 -2
- package/dist/esm/MessageDivider/MessageDivider.d.ts +9 -0
- package/dist/esm/MessageDivider/MessageDivider.js +21 -0
- package/dist/esm/MessageDivider/MessageDivider.test.d.ts +1 -0
- package/dist/esm/MessageDivider/MessageDivider.test.js +24 -0
- package/dist/esm/MessageDivider/index.d.ts +2 -0
- package/dist/esm/MessageDivider/index.js +2 -0
- package/dist/esm/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/esm/ResponseActions/ResponseActions.js +5 -5
- package/dist/esm/ResponseActions/ResponseActions.test.js +6 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -4
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDividers.tsx +24 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +15 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +39 -7
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotConversationEditing.tsx +202 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +17 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +45 -5
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +206 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +30 -4
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +33 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotDisplayMode.tsx +486 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotTranscripts.tsx +565 -0
- package/src/Chatbot/Chatbot.scss +1 -1
- package/src/ChatbotContent/ChatbotContent.scss +1 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx +6 -6
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -2
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +70 -32
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +176 -3
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +110 -60
- package/src/ChatbotFooter/ChatbotFooter.scss +1 -1
- package/src/ChatbotHeader/ChatbotHeader.scss +3 -3
- package/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +1 -1
- package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx +25 -0
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +64 -0
- package/src/ChatbotHeader/index.ts +1 -0
- package/src/ChatbotModal/ChatbotModal.scss +1 -1
- package/src/ChatbotToggle/ChatbotToggle.scss +2 -2
- package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +6 -9
- package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +6 -9
- package/src/FileDropZone/FileDropZone.tsx +2 -2
- package/src/Message/Message.scss +9 -7
- package/src/Message/Message.test.tsx +54 -0
- package/src/Message/Message.tsx +70 -50
- package/src/Message/MessageInput.tsx +5 -1
- package/src/MessageBar/AttachButton.tsx +2 -2
- package/src/MessageBar/MessageBar.tsx +2 -2
- package/src/MessageBar/SendButton.scss +3 -3
- package/src/MessageBox/JumpButton.scss +1 -1
- package/src/MessageBox/JumpButton.test.tsx +4 -4
- package/src/MessageBox/JumpButton.tsx +20 -4
- package/src/MessageBox/MessageBox.test.tsx +2 -2
- package/src/MessageBox/MessageBox.tsx +23 -2
- package/src/MessageDivider/MessageDivider.scss +45 -0
- package/src/MessageDivider/MessageDivider.test.tsx +24 -0
- package/src/MessageDivider/MessageDivider.tsx +35 -0
- package/src/MessageDivider/index.ts +3 -0
- package/src/ResponseActions/ResponseActions.test.tsx +6 -1
- package/src/ResponseActions/ResponseActions.tsx +24 -3
- package/src/index.ts +3 -0
- package/src/main.scss +1 -52
|
@@ -44,7 +44,8 @@ ChatbotHeaderMenu,
|
|
|
44
44
|
ChatbotHeaderTitle,
|
|
45
45
|
ChatbotHeaderActions,
|
|
46
46
|
ChatbotHeaderSelectorDropdown,
|
|
47
|
-
ChatbotHeaderOptionsDropdown
|
|
47
|
+
ChatbotHeaderOptionsDropdown,
|
|
48
|
+
ChatbotHeaderCloseButton,
|
|
48
49
|
} from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
|
|
49
50
|
|
|
50
51
|
import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
|
|
@@ -59,6 +60,7 @@ import userAvatar from '../Messages/user_avatar.svg';
|
|
|
59
60
|
import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
|
|
60
61
|
import { getTrackingProviders } from "@patternfly/chatbot/dist/dynamic/tracking";
|
|
61
62
|
import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent } from 'react';
|
|
63
|
+
import saveAs from 'file-saver';
|
|
62
64
|
|
|
63
65
|
### Basic ChatBot
|
|
64
66
|
|
|
@@ -129,6 +131,22 @@ This demo displays a ChatBot in a static, inline drawer. This demo includes:
|
|
|
129
131
|
|
|
130
132
|
```
|
|
131
133
|
|
|
134
|
+
### Display mode switcher
|
|
135
|
+
|
|
136
|
+
This demo showcases how the ChatBot can be rendered in different display modes to suit various application layouts. It demonstrates how to dynamically change the page structure in response to the user's selection. This demo includes:
|
|
137
|
+
|
|
138
|
+
1. The ability to switch between overlay, drawer, and fullscreen modes using the [`<ChatbotHeaderOptionsDropdown>`](/patternfly-ai/chatbot/ui#header-options) in the header.
|
|
139
|
+
2. A conditional page layout that renders the ChatBot for each display mode option:
|
|
140
|
+
- **Overlay:** As a floating window on top of the page content.
|
|
141
|
+
- **Drawer:** Inside an inline PatternFly `<Drawer>` as a side panel.
|
|
142
|
+
- **Fullscreen:** As a top-level component that covers the entire screen for an embedded experience.
|
|
143
|
+
3. Logic to show or hide the `<ChatbotToggle>` button, which is only present in the default overlay mode.
|
|
144
|
+
4. A [basic ChatBot](#basic-chatbot) with a header, welcome prompt, and message bar to populate the different layouts.
|
|
145
|
+
|
|
146
|
+
```js file="./ChatbotDisplayMode.tsx" isFullscreen
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
|
|
132
150
|
### Comparing ChatBots
|
|
133
151
|
|
|
134
152
|
To let users compare how different ChatBots respond to the same prompt, you can add multiple ChatBots within the same window. The following demo illustrates a comparison view pattern that allows users to toggle between different conversations in a single ChatBot window.
|
|
@@ -149,3 +167,17 @@ Your code structure should look like this:
|
|
|
149
167
|
```js file="./EmbeddedComparisonChatbot.tsx" isFullscreen
|
|
150
168
|
|
|
151
169
|
```
|
|
170
|
+
|
|
171
|
+
### Chat transcripts
|
|
172
|
+
|
|
173
|
+
This demo illustrates how you could add downloadable transcripts to your ChatBot, which outline conversation details in a Markdown file. This approach allows users to easily share information from a conversation with others.
|
|
174
|
+
|
|
175
|
+
A message transcript includes details from a single chat message. To download a sample message transcript in this demo, click the "Download" action under a bot message.
|
|
176
|
+
|
|
177
|
+
A conversation transcript includes details from the entirety of a ChatBot conversation. To download a sample conversation transcript in this demo, open the chat history menu and click "Download" in the options menu for the conversation.
|
|
178
|
+
|
|
179
|
+
In this example, file download is implemented with [file-saver](https://www.npmjs.com/package/file-saver).
|
|
180
|
+
|
|
181
|
+
```js file="./ChatbotTranscripts.tsx" isFullscreen
|
|
182
|
+
|
|
183
|
+
```
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import React, { FunctionComponent, MouseEvent, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Brand,
|
|
5
|
+
DropdownList,
|
|
6
|
+
DropdownItem,
|
|
7
|
+
Page,
|
|
8
|
+
Masthead,
|
|
9
|
+
MastheadMain,
|
|
10
|
+
MastheadBrand,
|
|
11
|
+
MastheadLogo,
|
|
12
|
+
PageSidebarBody,
|
|
13
|
+
PageSidebar,
|
|
14
|
+
MastheadToggle,
|
|
15
|
+
PageToggleButton,
|
|
16
|
+
SkipToContent,
|
|
17
|
+
Drawer,
|
|
18
|
+
DrawerContent,
|
|
19
|
+
DrawerContentBody,
|
|
20
|
+
DrawerPanelContent,
|
|
21
|
+
DropdownGroup
|
|
22
|
+
} from '@patternfly/react-core';
|
|
23
|
+
|
|
24
|
+
import ChatbotToggle from '@patternfly/chatbot/dist/dynamic/ChatbotToggle';
|
|
25
|
+
import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
|
|
26
|
+
import ChatbotContent from '@patternfly/chatbot/dist/dynamic/ChatbotContent';
|
|
27
|
+
import ChatbotWelcomePrompt from '@patternfly/chatbot/dist/dynamic/ChatbotWelcomePrompt';
|
|
28
|
+
import ChatbotFooter, { ChatbotFootnote } from '@patternfly/chatbot/dist/dynamic/ChatbotFooter';
|
|
29
|
+
import MessageBar from '@patternfly/chatbot/dist/dynamic/MessageBar';
|
|
30
|
+
import MessageBox from '@patternfly/chatbot/dist/dynamic/MessageBox';
|
|
31
|
+
import Message, { MessageProps } from '@patternfly/chatbot/dist/dynamic/Message';
|
|
32
|
+
import ChatbotConversationHistoryNav, {
|
|
33
|
+
Conversation
|
|
34
|
+
} from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
|
|
35
|
+
import ChatbotHeader, {
|
|
36
|
+
ChatbotHeaderMenu,
|
|
37
|
+
ChatbotHeaderMain,
|
|
38
|
+
ChatbotHeaderTitle,
|
|
39
|
+
ChatbotHeaderActions,
|
|
40
|
+
ChatbotHeaderSelectorDropdown,
|
|
41
|
+
ChatbotHeaderOptionsDropdown,
|
|
42
|
+
ChatbotHeaderCloseButton
|
|
43
|
+
} from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
|
|
44
|
+
import PFIconLogoColor from '../UI/PF-IconLogo-Color.svg';
|
|
45
|
+
import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg';
|
|
46
|
+
import { BarsIcon } from '@patternfly/react-icons';
|
|
47
|
+
import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
|
|
48
|
+
import OpenDrawerRightIcon from '@patternfly/react-icons/dist/esm/icons/open-drawer-right-icon';
|
|
49
|
+
import OutlinedWindowRestoreIcon from '@patternfly/react-icons/dist/esm/icons/outlined-window-restore-icon';
|
|
50
|
+
import userAvatar from '../Messages/user_avatar.svg';
|
|
51
|
+
import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
|
|
52
|
+
import '@patternfly/react-core/dist/styles/base.css';
|
|
53
|
+
import '@patternfly/chatbot/dist/css/main.css';
|
|
54
|
+
|
|
55
|
+
const footnoteProps = {
|
|
56
|
+
label: 'ChatBot uses AI. Check for mistakes.',
|
|
57
|
+
popover: {
|
|
58
|
+
title: 'Verify accuracy',
|
|
59
|
+
description: `While ChatBot strives for accuracy, there's always a possibility of errors. It's a good practice to verify critical information from reliable sources, especially if it's crucial for decision-making or actions.`,
|
|
60
|
+
bannerImage: {
|
|
61
|
+
src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',
|
|
62
|
+
alt: 'Example image for footnote popover'
|
|
63
|
+
},
|
|
64
|
+
cta: {
|
|
65
|
+
label: 'Got it',
|
|
66
|
+
onClick: () => {
|
|
67
|
+
alert('Do something!');
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
link: {
|
|
71
|
+
label: 'Learn more',
|
|
72
|
+
url: 'https://www.redhat.com/'
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const markdown = `A paragraph with *emphasis* and **strong importance**.`;
|
|
78
|
+
|
|
79
|
+
const date = new Date();
|
|
80
|
+
|
|
81
|
+
const initialMessages: MessageProps[] = [
|
|
82
|
+
{
|
|
83
|
+
id: '1',
|
|
84
|
+
role: 'user',
|
|
85
|
+
content: 'Hello, can you give me an example of what you can do?',
|
|
86
|
+
name: 'User',
|
|
87
|
+
avatar: userAvatar,
|
|
88
|
+
timestamp: date.toLocaleString(),
|
|
89
|
+
avatarProps: { isBordered: true }
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: '2',
|
|
93
|
+
role: 'bot',
|
|
94
|
+
content: markdown,
|
|
95
|
+
name: 'Bot',
|
|
96
|
+
avatar: patternflyAvatar,
|
|
97
|
+
timestamp: date.toLocaleString(),
|
|
98
|
+
actions: {
|
|
99
|
+
// eslint-disable-next-line no-console
|
|
100
|
+
positive: { onClick: () => console.log('Good response') },
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
negative: { onClick: () => console.log('Bad response') },
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
copy: { onClick: () => console.log('Copy') },
|
|
105
|
+
// eslint-disable-next-line no-console
|
|
106
|
+
download: { onClick: () => console.log('Download') },
|
|
107
|
+
// eslint-disable-next-line no-console
|
|
108
|
+
listen: { onClick: () => console.log('Listen') }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const welcomePrompts = [
|
|
114
|
+
{
|
|
115
|
+
title: 'Topic 1',
|
|
116
|
+
message: 'Helpful prompt for Topic 1'
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
title: 'Topic 2',
|
|
120
|
+
message: 'Helpful prompt for Topic 2'
|
|
121
|
+
}
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
const initialConversations = {
|
|
125
|
+
Today: [{ id: '1', text: 'Hello, can you give me an example of what you can do?' }],
|
|
126
|
+
'This month': [
|
|
127
|
+
{
|
|
128
|
+
id: '2',
|
|
129
|
+
text: 'Enterprise Linux installation and setup'
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const ChatbotDisplayModeDemo: FunctionComponent = () => {
|
|
135
|
+
const [messages, setMessages] = useState<MessageProps[]>(initialMessages);
|
|
136
|
+
const [selectedModel, setSelectedModel] = useState('Granite 7B');
|
|
137
|
+
const [isSendButtonDisabled, setIsSendButtonDisabled] = useState(false);
|
|
138
|
+
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
139
|
+
const [conversations, setConversations] = useState<Conversation[] | { [key: string]: Conversation[] }>(
|
|
140
|
+
initialConversations
|
|
141
|
+
);
|
|
142
|
+
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
|
143
|
+
const [announcement, setAnnouncement] = useState<string>();
|
|
144
|
+
const scrollToBottomRef = useRef<HTMLDivElement>(null);
|
|
145
|
+
const historyRef = useRef<HTMLButtonElement>(null);
|
|
146
|
+
const drawerRef = useRef<HTMLDivElement>();
|
|
147
|
+
const [chatbotVisible, setChatbotVisible] = useState<boolean>(true);
|
|
148
|
+
const toggleRef = useRef<HTMLButtonElement>(null);
|
|
149
|
+
const chatbotRef = useRef<HTMLDivElement>(null);
|
|
150
|
+
const [shouldFocusOptions, setShouldFocusOptions] = useState(false);
|
|
151
|
+
|
|
152
|
+
const [displayMode, setDisplayMode] = useState<ChatbotDisplayMode>(ChatbotDisplayMode.default);
|
|
153
|
+
|
|
154
|
+
const isDrawerPanelExpanded = displayMode === ChatbotDisplayMode.drawer;
|
|
155
|
+
|
|
156
|
+
const onExpand = () => {
|
|
157
|
+
drawerRef.current && drawerRef.current.focus();
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (messages.length > 2) {
|
|
162
|
+
scrollToBottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
163
|
+
}
|
|
164
|
+
}, [messages]);
|
|
165
|
+
|
|
166
|
+
// Focus options dropdown after display mode changes
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (shouldFocusOptions) {
|
|
169
|
+
// Use a more reliable timeout to ensure DOM has updated
|
|
170
|
+
const timeoutId = setTimeout(() => {
|
|
171
|
+
// Find the options dropdown button via CSS selector since ref forwarding isn't supported
|
|
172
|
+
const optionsButton = document.querySelector('.pf-chatbot__button--toggle-options');
|
|
173
|
+
if (optionsButton instanceof HTMLElement) {
|
|
174
|
+
optionsButton.focus();
|
|
175
|
+
}
|
|
176
|
+
setShouldFocusOptions(false);
|
|
177
|
+
}, 100);
|
|
178
|
+
|
|
179
|
+
return () => clearTimeout(timeoutId);
|
|
180
|
+
}
|
|
181
|
+
}, [displayMode, shouldFocusOptions]);
|
|
182
|
+
|
|
183
|
+
const onSelectModel = (_event: MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => {
|
|
184
|
+
setSelectedModel(value as string);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const onSelectDisplayMode = (
|
|
188
|
+
_event: MouseEvent<Element, MouseEvent> | undefined,
|
|
189
|
+
value: string | number | undefined
|
|
190
|
+
) => {
|
|
191
|
+
setDisplayMode(value as ChatbotDisplayMode);
|
|
192
|
+
// Flag to focus options dropdown after render
|
|
193
|
+
setShouldFocusOptions(true);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const generateId = () => {
|
|
197
|
+
const id = Date.now() + Math.random();
|
|
198
|
+
return id.toString();
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const handleSend = (message: string) => {
|
|
202
|
+
setIsSendButtonDisabled(true);
|
|
203
|
+
const newMessages: MessageProps[] = [...messages];
|
|
204
|
+
const date = new Date();
|
|
205
|
+
newMessages.push({
|
|
206
|
+
id: generateId(),
|
|
207
|
+
role: 'user',
|
|
208
|
+
content: message,
|
|
209
|
+
name: 'User',
|
|
210
|
+
avatar: userAvatar,
|
|
211
|
+
timestamp: date.toLocaleString(),
|
|
212
|
+
avatarProps: { isBordered: true }
|
|
213
|
+
});
|
|
214
|
+
newMessages.push({
|
|
215
|
+
id: generateId(),
|
|
216
|
+
role: 'bot',
|
|
217
|
+
content: 'API response goes here',
|
|
218
|
+
name: 'Bot',
|
|
219
|
+
avatar: patternflyAvatar,
|
|
220
|
+
isLoading: true,
|
|
221
|
+
timestamp: date.toLocaleString()
|
|
222
|
+
});
|
|
223
|
+
setMessages(newMessages);
|
|
224
|
+
setAnnouncement(`Message from User: ${message}. Message from Bot is loading.`);
|
|
225
|
+
|
|
226
|
+
setTimeout(() => {
|
|
227
|
+
const loadedMessages: MessageProps[] = [...newMessages];
|
|
228
|
+
loadedMessages.pop();
|
|
229
|
+
loadedMessages.push({
|
|
230
|
+
id: generateId(),
|
|
231
|
+
role: 'bot',
|
|
232
|
+
content: 'API response goes here',
|
|
233
|
+
name: 'Bot',
|
|
234
|
+
avatar: patternflyAvatar,
|
|
235
|
+
isLoading: false,
|
|
236
|
+
actions: {
|
|
237
|
+
// eslint-disable-next-line no-console
|
|
238
|
+
positive: { onClick: () => console.log('Good response') },
|
|
239
|
+
// eslint-disable-next-line no-console
|
|
240
|
+
negative: { onClick: () => console.log('Bad response') },
|
|
241
|
+
// eslint-disable-next-line no-console
|
|
242
|
+
copy: { onClick: () => console.log('Copy') },
|
|
243
|
+
// eslint-disable-next-line no-console
|
|
244
|
+
download: { onClick: () => console.log('Download') },
|
|
245
|
+
// eslint-disable-next-line no-console
|
|
246
|
+
listen: { onClick: () => console.log('Listen') }
|
|
247
|
+
},
|
|
248
|
+
timestamp: date.toLocaleString()
|
|
249
|
+
});
|
|
250
|
+
setMessages(loadedMessages);
|
|
251
|
+
setAnnouncement(`Message from Bot: API response goes here`);
|
|
252
|
+
setIsSendButtonDisabled(false);
|
|
253
|
+
}, 2000);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const findMatchingItems = (targetValue: string) => {
|
|
257
|
+
let filteredConversations = Object.entries(initialConversations).reduce((acc, [key, items]) => {
|
|
258
|
+
const filteredItems = items.filter((item) => item.text.toLowerCase().includes(targetValue.toLowerCase()));
|
|
259
|
+
if (filteredItems.length > 0) {
|
|
260
|
+
acc[key] = filteredItems;
|
|
261
|
+
}
|
|
262
|
+
return acc;
|
|
263
|
+
}, {});
|
|
264
|
+
|
|
265
|
+
if (Object.keys(filteredConversations).length === 0) {
|
|
266
|
+
filteredConversations = [{ id: '13', noIcon: true, text: 'No results found' }];
|
|
267
|
+
}
|
|
268
|
+
return filteredConversations;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const iconLogo = (
|
|
272
|
+
<>
|
|
273
|
+
<Brand className="show-light" src={PFIconLogoColor} alt="PatternFly" />
|
|
274
|
+
<Brand className="show-dark" src={PFIconLogoReverse} alt="PatternFly" />
|
|
275
|
+
</>
|
|
276
|
+
);
|
|
277
|
+
const masthead = (
|
|
278
|
+
<Masthead>
|
|
279
|
+
<MastheadMain>
|
|
280
|
+
<MastheadToggle>
|
|
281
|
+
<PageToggleButton
|
|
282
|
+
variant="plain"
|
|
283
|
+
aria-label="Global navigation"
|
|
284
|
+
isSidebarOpen={isSidebarOpen}
|
|
285
|
+
onSidebarToggle={() => setIsSidebarOpen(!isSidebarOpen)}
|
|
286
|
+
id="fill-nav-toggle"
|
|
287
|
+
>
|
|
288
|
+
<BarsIcon />
|
|
289
|
+
</PageToggleButton>
|
|
290
|
+
</MastheadToggle>
|
|
291
|
+
<MastheadBrand>
|
|
292
|
+
<MastheadLogo href="https://patternfly.org" target="_blank">
|
|
293
|
+
Logo
|
|
294
|
+
</MastheadLogo>
|
|
295
|
+
</MastheadBrand>
|
|
296
|
+
</MastheadMain>
|
|
297
|
+
</Masthead>
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const sidebar = (
|
|
301
|
+
<PageSidebar isSidebarOpen={isSidebarOpen} id="fill-sidebar">
|
|
302
|
+
<PageSidebarBody>Navigation</PageSidebarBody>
|
|
303
|
+
</PageSidebar>
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
const skipToChatbot = (e: MouseEvent) => {
|
|
307
|
+
e.preventDefault();
|
|
308
|
+
/* eslint-disable indent */
|
|
309
|
+
switch (displayMode) {
|
|
310
|
+
case ChatbotDisplayMode.default:
|
|
311
|
+
if (!chatbotVisible && toggleRef.current) {
|
|
312
|
+
toggleRef.current.focus();
|
|
313
|
+
}
|
|
314
|
+
if (chatbotVisible && chatbotRef.current) {
|
|
315
|
+
chatbotRef.current.focus();
|
|
316
|
+
}
|
|
317
|
+
break;
|
|
318
|
+
default:
|
|
319
|
+
if (historyRef.current) {
|
|
320
|
+
historyRef.current.focus();
|
|
321
|
+
}
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
/* eslint-enable indent */
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const skipToContent = (
|
|
328
|
+
<SkipToContent href="#" onClick={skipToChatbot}>
|
|
329
|
+
Skip to chatbot
|
|
330
|
+
</SkipToContent>
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const handleChatbotVisibilityDrawer = () => {
|
|
334
|
+
setChatbotVisible(!chatbotVisible);
|
|
335
|
+
setDisplayMode(ChatbotDisplayMode.default);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const chatbotComponent = (
|
|
339
|
+
<Chatbot isVisible={chatbotVisible} displayMode={displayMode} ref={chatbotRef}>
|
|
340
|
+
<ChatbotConversationHistoryNav
|
|
341
|
+
displayMode={displayMode}
|
|
342
|
+
onDrawerToggle={() => {
|
|
343
|
+
setIsDrawerOpen(!isDrawerOpen);
|
|
344
|
+
setConversations(initialConversations);
|
|
345
|
+
}}
|
|
346
|
+
isDrawerOpen={isDrawerOpen}
|
|
347
|
+
setIsDrawerOpen={setIsDrawerOpen}
|
|
348
|
+
activeItemId="1"
|
|
349
|
+
conversations={conversations}
|
|
350
|
+
onNewChat={() => {
|
|
351
|
+
setIsDrawerOpen(!isDrawerOpen);
|
|
352
|
+
setMessages([]);
|
|
353
|
+
setConversations(initialConversations);
|
|
354
|
+
}}
|
|
355
|
+
handleTextInputChange={(value: string) => {
|
|
356
|
+
if (value === '') {
|
|
357
|
+
setConversations(initialConversations);
|
|
358
|
+
}
|
|
359
|
+
const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value);
|
|
360
|
+
setConversations(newConversations);
|
|
361
|
+
}}
|
|
362
|
+
drawerContent={
|
|
363
|
+
<>
|
|
364
|
+
<ChatbotHeader>
|
|
365
|
+
<ChatbotHeaderMain>
|
|
366
|
+
<ChatbotHeaderMenu
|
|
367
|
+
ref={historyRef}
|
|
368
|
+
aria-expanded={isDrawerOpen}
|
|
369
|
+
onMenuToggle={() => setIsDrawerOpen(!isDrawerOpen)}
|
|
370
|
+
/>
|
|
371
|
+
<ChatbotHeaderTitle>{iconLogo}</ChatbotHeaderTitle>
|
|
372
|
+
</ChatbotHeaderMain>
|
|
373
|
+
<ChatbotHeaderActions>
|
|
374
|
+
<ChatbotHeaderSelectorDropdown value={selectedModel} onSelect={onSelectModel}>
|
|
375
|
+
<DropdownList>
|
|
376
|
+
<DropdownItem value="Granite 7B" key="granite">
|
|
377
|
+
Granite 7B
|
|
378
|
+
</DropdownItem>
|
|
379
|
+
<DropdownItem value="Llama 3.0" key="llama">
|
|
380
|
+
Llama 3.0
|
|
381
|
+
</DropdownItem>
|
|
382
|
+
</DropdownList>
|
|
383
|
+
</ChatbotHeaderSelectorDropdown>
|
|
384
|
+
<ChatbotHeaderOptionsDropdown onSelect={onSelectDisplayMode}>
|
|
385
|
+
<DropdownGroup label="Display mode">
|
|
386
|
+
<DropdownList>
|
|
387
|
+
<DropdownItem
|
|
388
|
+
value={ChatbotDisplayMode.default}
|
|
389
|
+
key="switchDisplayOverlay"
|
|
390
|
+
icon={<OutlinedWindowRestoreIcon aria-hidden />}
|
|
391
|
+
isSelected={displayMode === ChatbotDisplayMode.default}
|
|
392
|
+
>
|
|
393
|
+
<span>Overlay</span>
|
|
394
|
+
</DropdownItem>
|
|
395
|
+
<DropdownItem
|
|
396
|
+
value={ChatbotDisplayMode.drawer}
|
|
397
|
+
key="switchDisplayDock"
|
|
398
|
+
icon={<OpenDrawerRightIcon aria-hidden />}
|
|
399
|
+
isSelected={displayMode === ChatbotDisplayMode.drawer}
|
|
400
|
+
>
|
|
401
|
+
<span>Drawer</span>
|
|
402
|
+
</DropdownItem>
|
|
403
|
+
<DropdownItem
|
|
404
|
+
value={ChatbotDisplayMode.fullscreen}
|
|
405
|
+
key="switchDisplayFullscreen"
|
|
406
|
+
icon={<ExpandIcon aria-hidden />}
|
|
407
|
+
isSelected={displayMode === ChatbotDisplayMode.fullscreen}
|
|
408
|
+
>
|
|
409
|
+
<span>Fullscreen</span>
|
|
410
|
+
</DropdownItem>
|
|
411
|
+
</DropdownList>
|
|
412
|
+
</DropdownGroup>
|
|
413
|
+
</ChatbotHeaderOptionsDropdown>
|
|
414
|
+
{(displayMode === ChatbotDisplayMode.drawer || displayMode === ChatbotDisplayMode.fullscreen) && (
|
|
415
|
+
<ChatbotHeaderCloseButton onClick={handleChatbotVisibilityDrawer} />
|
|
416
|
+
)}
|
|
417
|
+
</ChatbotHeaderActions>
|
|
418
|
+
</ChatbotHeader>
|
|
419
|
+
<ChatbotContent>
|
|
420
|
+
<MessageBox announcement={announcement}>
|
|
421
|
+
<ChatbotWelcomePrompt
|
|
422
|
+
title="Hello, Chatbot User"
|
|
423
|
+
description="How may I help you today?"
|
|
424
|
+
prompts={welcomePrompts}
|
|
425
|
+
/>
|
|
426
|
+
{messages.map((message, index) => {
|
|
427
|
+
if (index === messages.length - 1) {
|
|
428
|
+
return (
|
|
429
|
+
<React.Fragment key={message.id}>
|
|
430
|
+
<div ref={scrollToBottomRef}></div>
|
|
431
|
+
<Message {...message} />
|
|
432
|
+
</React.Fragment>
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
return <Message key={message.id} {...message} />;
|
|
436
|
+
})}
|
|
437
|
+
</MessageBox>
|
|
438
|
+
</ChatbotContent>
|
|
439
|
+
<ChatbotFooter>
|
|
440
|
+
<MessageBar onSendMessage={handleSend} hasMicrophoneButton isSendButtonDisabled={isSendButtonDisabled} />
|
|
441
|
+
<ChatbotFootnote {...footnoteProps} />
|
|
442
|
+
</ChatbotFooter>
|
|
443
|
+
</>
|
|
444
|
+
}
|
|
445
|
+
/>
|
|
446
|
+
</Chatbot>
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
const chatbotToggle =
|
|
450
|
+
displayMode === ChatbotDisplayMode.default ? (
|
|
451
|
+
<ChatbotToggle
|
|
452
|
+
tooltipLabel="Chatbot"
|
|
453
|
+
isChatbotVisible={chatbotVisible}
|
|
454
|
+
onToggleChatbot={() => {
|
|
455
|
+
setChatbotVisible(!chatbotVisible);
|
|
456
|
+
}}
|
|
457
|
+
id="chatbot-toggle"
|
|
458
|
+
ref={toggleRef}
|
|
459
|
+
/>
|
|
460
|
+
) : null;
|
|
461
|
+
|
|
462
|
+
const panelContent = <DrawerPanelContent>{chatbotComponent}</DrawerPanelContent>;
|
|
463
|
+
|
|
464
|
+
const pageContent = (
|
|
465
|
+
<Page skipToContent={skipToContent} masthead={masthead} sidebar={sidebar} isContentFilled>
|
|
466
|
+
{displayMode === ChatbotDisplayMode.default && chatbotToggle}
|
|
467
|
+
</Page>
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
if (displayMode === ChatbotDisplayMode.drawer) {
|
|
471
|
+
return (
|
|
472
|
+
<Drawer isExpanded={isDrawerPanelExpanded} isInline onExpand={onExpand}>
|
|
473
|
+
<DrawerContent panelContent={panelContent}>
|
|
474
|
+
<DrawerContentBody>{pageContent}</DrawerContentBody>
|
|
475
|
+
</DrawerContent>
|
|
476
|
+
</Drawer>
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return (
|
|
481
|
+
<>
|
|
482
|
+
{pageContent}
|
|
483
|
+
{chatbotComponent}
|
|
484
|
+
</>
|
|
485
|
+
);
|
|
486
|
+
};
|