@patternfly/chatbot 6.9.0-prerelease.1 → 6.9.0-prerelease.3

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.
Files changed (38) hide show
  1. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +1 -1
  2. package/dist/cjs/MessageBar/MessageBar.js +1 -1
  3. package/dist/cjs/MessageBar/SendButton.js +2 -2
  4. package/dist/cjs/SourcesCard/SourcesCard.d.ts +2 -0
  5. package/dist/cjs/SourcesCardBase/SourcesCardBase.d.ts +2 -0
  6. package/dist/cjs/SourcesCardBase/SourcesCardBase.js +2 -2
  7. package/dist/css/main.css +163 -57
  8. package/dist/css/main.css.map +1 -1
  9. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +1 -1
  10. package/dist/esm/MessageBar/MessageBar.js +1 -1
  11. package/dist/esm/MessageBar/SendButton.js +2 -2
  12. package/dist/esm/SourcesCard/SourcesCard.d.ts +2 -0
  13. package/dist/esm/SourcesCardBase/SourcesCardBase.d.ts +2 -0
  14. package/dist/esm/SourcesCardBase/SourcesCardBase.js +2 -2
  15. package/package.json +3 -3
  16. package/patternfly-docs/content/extensions/chatbot/chatbot.md +1 -0
  17. package/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +1 -1
  18. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +31 -0
  19. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +2 -0
  20. package/patternfly-docs/content/extensions/chatbot/examples/demos/Canvas.md +110 -0
  21. package/patternfly-docs/content/extensions/chatbot/examples/demos/Canvas.tsx +609 -0
  22. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +25 -3
  23. package/patternfly-docs/patternfly-docs.config.js +1 -1
  24. package/src/Canvas/Canvas.scss +138 -0
  25. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +91 -77
  26. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +13 -0
  27. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +1 -0
  28. package/src/MessageBar/AttachButton.scss +4 -21
  29. package/src/MessageBar/MessageBar.test.tsx +3 -0
  30. package/src/MessageBar/MessageBar.tsx +2 -0
  31. package/src/MessageBar/SendButton.scss +1 -32
  32. package/src/MessageBar/SendButton.tsx +2 -2
  33. package/src/SourcesCard/SourcesCard.scss +6 -0
  34. package/src/SourcesCard/SourcesCard.test.tsx +16 -0
  35. package/src/SourcesCard/SourcesCard.tsx +2 -0
  36. package/src/SourcesCardBase/SourcesCardBase.test.tsx +62 -0
  37. package/src/SourcesCardBase/SourcesCardBase.tsx +8 -0
  38. package/src/main.scss +1 -0
@@ -0,0 +1,609 @@
1
+ import { useState, useRef, useCallback, useEffect, FunctionComponent, ReactNode } from 'react';
2
+ import {
3
+ Brand,
4
+ Divider,
5
+ Drawer,
6
+ DrawerActions,
7
+ DrawerCloseButton,
8
+ DrawerContent,
9
+ DrawerContentBody,
10
+ DrawerHead,
11
+ DrawerPanelContent,
12
+ DropdownItem,
13
+ DropdownList,
14
+ Flex,
15
+ FlexItem,
16
+ Label,
17
+ MenuToggle,
18
+ Popover,
19
+ Select,
20
+ SelectList,
21
+ SelectOption,
22
+ Title,
23
+ Tooltip
24
+ } from '@patternfly/react-core';
25
+ import { CodeEditor, CodeEditorControl, Language } from '@patternfly/react-code-editor';
26
+ import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
27
+ import ChatbotContent from '@patternfly/chatbot/dist/dynamic/ChatbotContent';
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
+ ChatbotHeaderActions,
37
+ ChatbotHeaderCloseButton,
38
+ ChatbotHeaderMain,
39
+ ChatbotHeaderMenu,
40
+ ChatbotHeaderTitle
41
+ } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
42
+ import RhUiAiEditIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-edit-icon';
43
+ import RhUiAiInfoIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-info-icon';
44
+ import RhUiImageFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-image-fill-icon';
45
+ import RhUiAddIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-add-icon';
46
+ import RhUiTaskFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-task-fill-icon';
47
+ import RhUiExportIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-export-icon';
48
+ import RhUiRedoIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-redo-icon';
49
+ import RhUiUndoIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-undo-icon';
50
+ import RhUiServerUploadFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-server-upload-fill-icon';
51
+ import RhUiNotificationFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-notification-fill-icon';
52
+ import RhUiCalendarFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-calendar-fill-icon';
53
+ import { useDropzone } from 'react-dropzone';
54
+ import PFIconLogoColor from '../UI/PF-IconLogo-Color.svg';
55
+ import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg';
56
+ import userAvatar from '../Messages/user_avatar.svg';
57
+ import '@patternfly/react-core/dist/styles/base.css';
58
+ import '@patternfly/chatbot/dist/css/main.css';
59
+
60
+ const sampleCode = `apiVersion: v1
61
+ kind: ConfigMap
62
+ metadata:
63
+ name: canvas-demo
64
+ data:
65
+ greeting: Hello, how can I help you today?
66
+ `;
67
+
68
+ const date = new Date();
69
+
70
+ const initialMessages: MessageProps[] = [
71
+ {
72
+ id: '1',
73
+ role: 'bot',
74
+ content: 'How can I help?',
75
+ name: 'Bot',
76
+ timestamp: date.toLocaleString()
77
+ },
78
+ {
79
+ id: '2',
80
+ role: 'user',
81
+ content: 'I want to live edit YAML code using canvas mode',
82
+ name: 'You',
83
+ avatar: userAvatar,
84
+ avatarProps: { isBordered: true },
85
+ timestamp: date.toLocaleString()
86
+ },
87
+ {
88
+ id: '3',
89
+ role: 'bot',
90
+ content: 'The canvas mode text editor is ready. Edit, share or ask me to change something for you.',
91
+ name: 'Bot',
92
+ timestamp: date.toLocaleString(),
93
+ attachments: [{ name: 'canvas mode.yaml', id: 'canvas-mode' }]
94
+ }
95
+ ];
96
+
97
+ const initialConversations = {
98
+ Today: [{ id: '1', text: 'I want to live edit YAML code using canvas mode' }],
99
+ 'This month': [
100
+ { id: '2', text: 'Enterprise Linux installation and setup' },
101
+ { id: '3', text: 'Troubleshoot system crash' }
102
+ ]
103
+ };
104
+
105
+ const footnoteProps = {
106
+ label: 'Always review AI-generated content prior to use.'
107
+ };
108
+
109
+ const modelOptions = ['GPT-4', 'GPT-3.5', 'Claude', 'Llama 2'];
110
+
111
+ interface CanvasEditor {
112
+ trigger: (source: string, handlerId: string, payload?: unknown) => void;
113
+ }
114
+
115
+ const iconLogo = (
116
+ <>
117
+ <Brand className="show-light" src={PFIconLogoColor} alt="PatternFly" />
118
+ <Brand className="show-dark" src={PFIconLogoReverse} alt="PatternFly" />
119
+ </>
120
+ );
121
+
122
+ export const Canvas: FunctionComponent = () => {
123
+ const [messages, setMessages] = useState<MessageProps[]>(initialMessages);
124
+ const [announcement, setAnnouncement] = useState<string>();
125
+ const [isSendButtonDisabled, setIsSendButtonDisabled] = useState(false);
126
+ const [isAttachMenuOpen, setIsAttachMenuOpen] = useState(false);
127
+ const [isModelSelectOpen, setIsModelSelectOpen] = useState(false);
128
+ const [selectedModel, setSelectedModel] = useState('GPT-4');
129
+ const [showCanvasLabel, setShowCanvasLabel] = useState(true);
130
+ const [isCanvasOpen, setIsCanvasOpen] = useState(true);
131
+ const [isGeneratedAiPopoverOpen, setIsGeneratedAiPopoverOpen] = useState(false);
132
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
133
+ const [chatbotVisible, setChatbotVisible] = useState(true);
134
+ const [conversations, setConversations] = useState<Conversation[] | { [key: string]: Conversation[] }>(
135
+ initialConversations
136
+ );
137
+ const [code, setCode] = useState(sampleCode);
138
+ const scrollToBottomRef = useRef<HTMLDivElement>(null);
139
+ const historyRef = useRef<HTMLButtonElement>(null);
140
+ const editorRef = useRef<CanvasEditor>(null);
141
+ const messageInputRef = useRef<HTMLTextAreaElement>(null);
142
+ const messageActionsRef = useRef<HTMLButtonElement>(null);
143
+ const canvasSectionRef = useRef<HTMLElement>(null);
144
+ const wasCanvasOpen = useRef(isCanvasOpen);
145
+ const shouldFocusCanvasCloseButton = useRef(false);
146
+ const displayMode = ChatbotDisplayMode.fullscreen;
147
+
148
+ const { open, getInputProps } = useDropzone({
149
+ multiple: true,
150
+ // eslint-disable-next-line no-console
151
+ onDropAccepted: () => console.log('fileUploaded')
152
+ });
153
+
154
+ const generateId = () => {
155
+ const id = Date.now() + Math.random();
156
+ return id.toString();
157
+ };
158
+
159
+ const handleSend = useCallback(
160
+ (input: string) => {
161
+ const date = new Date();
162
+ const newMessages: MessageProps[] = [];
163
+ messages.forEach((message) => newMessages.push(message));
164
+ newMessages.push({
165
+ avatar: userAvatar,
166
+ avatarProps: { isBordered: true },
167
+ id: generateId(),
168
+ name: 'You',
169
+ role: 'user',
170
+ content: input,
171
+ timestamp: `${date?.toLocaleDateString()} ${date?.toLocaleTimeString()}`
172
+ });
173
+ newMessages.push({
174
+ id: generateId(),
175
+ name: 'Bot',
176
+ role: 'bot',
177
+ timestamp: `${date?.toLocaleDateString()} ${date?.toLocaleTimeString()}`,
178
+ isLoading: true
179
+ });
180
+ setMessages(newMessages);
181
+ setAnnouncement(`Message from You: ${input}. Message from Bot is loading.`);
182
+ setIsSendButtonDisabled(true);
183
+
184
+ setTimeout(() => {
185
+ const loadedMessages: MessageProps[] = [];
186
+ newMessages.forEach((message) => loadedMessages.push(message));
187
+ loadedMessages.pop();
188
+ loadedMessages.push({
189
+ id: generateId(),
190
+ role: 'bot',
191
+ content: 'API response from Bot goes here',
192
+ name: 'Bot',
193
+ isLoading: false,
194
+ actions: {
195
+ // eslint-disable-next-line no-console
196
+ positive: { onClick: () => console.log('Good response') },
197
+ // eslint-disable-next-line no-console
198
+ negative: { onClick: () => console.log('Bad response') },
199
+ // eslint-disable-next-line no-console
200
+ copy: { onClick: () => console.log('Copy') },
201
+ // eslint-disable-next-line no-console
202
+ download: { onClick: () => console.log('Download') },
203
+ // eslint-disable-next-line no-console
204
+ listen: { onClick: () => console.log('Listen') }
205
+ },
206
+ timestamp: date.toLocaleString()
207
+ });
208
+ setMessages(loadedMessages);
209
+ setAnnouncement('Message from Bot: API response goes here');
210
+ setIsSendButtonDisabled(false);
211
+ }, 5000);
212
+ },
213
+ [messages]
214
+ );
215
+
216
+ useEffect(() => {
217
+ if (messages.length > 3) {
218
+ scrollToBottomRef.current?.scrollIntoView();
219
+ }
220
+ }, [messages]);
221
+
222
+ const onModelSelect = (
223
+ _event: React.MouseEvent<Element, MouseEvent> | undefined,
224
+ value: string | number | undefined
225
+ ) => {
226
+ setSelectedModel(value as string);
227
+ setIsModelSelectOpen(false);
228
+ };
229
+
230
+ const openCanvas = () => {
231
+ setShowCanvasLabel(true);
232
+ setIsCanvasOpen(true);
233
+ };
234
+
235
+ const closeCanvasMode = () => {
236
+ setShowCanvasLabel(false);
237
+ setIsCanvasOpen(false);
238
+ window.requestAnimationFrame(() => messageInputRef.current?.focus());
239
+ };
240
+
241
+ const focusCanvasSection = () => {
242
+ canvasSectionRef.current?.focus();
243
+ };
244
+
245
+ useEffect(() => {
246
+ const wasClosed = !wasCanvasOpen.current;
247
+ wasCanvasOpen.current = isCanvasOpen;
248
+
249
+ if (isCanvasOpen && wasClosed) {
250
+ shouldFocusCanvasCloseButton.current = true;
251
+ } else if (!isCanvasOpen) {
252
+ shouldFocusCanvasCloseButton.current = false;
253
+ }
254
+ }, [isCanvasOpen]);
255
+
256
+ const onCanvasExpand = () => {
257
+ if (shouldFocusCanvasCloseButton.current) {
258
+ shouldFocusCanvasCloseButton.current = false;
259
+ focusCanvasSection();
260
+ }
261
+ };
262
+
263
+ const focusMessageActions = () => {
264
+ window.requestAnimationFrame(() => messageActionsRef.current?.focus());
265
+ };
266
+
267
+ const handleGeneratedAiLabelKeyDown = (event: React.KeyboardEvent) => {
268
+ if (event.key === 'Enter' || event.key === ' ') {
269
+ event.preventDefault();
270
+ event.currentTarget.click();
271
+ }
272
+ };
273
+
274
+ const findMatchingItems = (targetValue: string) => {
275
+ let filteredConversations = Object.entries(initialConversations).reduce((acc, [key, items]) => {
276
+ const filteredItems = items.filter((item) => item.text.toLowerCase().includes(targetValue.toLowerCase()));
277
+ if (filteredItems.length > 0) {
278
+ acc[key] = filteredItems;
279
+ }
280
+ return acc;
281
+ }, {});
282
+
283
+ if (Object.keys(filteredConversations).length === 0) {
284
+ filteredConversations = [{ id: '13', noIcon: true, text: 'No results found' }];
285
+ }
286
+ return filteredConversations;
287
+ };
288
+
289
+ const attachMenuItems: ReactNode = (
290
+ <>
291
+ <DropdownList>
292
+ <DropdownItem value="Alerts" id="logs" icon={<RhUiNotificationFillIcon />}>
293
+ Alerts
294
+ </DropdownItem>
295
+ <DropdownItem value="Events" id="events" icon={<RhUiCalendarFillIcon />}>
296
+ Events
297
+ </DropdownItem>
298
+ <DropdownItem value="Logs" id="logs" icon={<RhUiTaskFillIcon />}>
299
+ Logs
300
+ </DropdownItem>
301
+ </DropdownList>
302
+ <Divider key="divider-1" />
303
+ <DropdownList>
304
+ <DropdownItem value="Upload from computer" id="upload" icon={<RhUiServerUploadFillIcon />} onClick={open}>
305
+ Upload from computer
306
+ </DropdownItem>
307
+ <DropdownItem value="canvas" id="canvas" icon={<RhUiImageFillIcon />}>
308
+ {`${isCanvasOpen ? 'Disable' : 'Enable'} canvas mode`}
309
+ </DropdownItem>
310
+ </DropdownList>
311
+ </>
312
+ );
313
+
314
+ const customControls = [
315
+ <CodeEditorControl
316
+ key="undo"
317
+ icon={<RhUiUndoIcon />}
318
+ aria-label="Undo"
319
+ tooltipProps={{
320
+ content: 'Undo',
321
+ // prevents VO announcements of both aria label and tooltip
322
+ aria: 'none'
323
+ }}
324
+ onClick={() => editorRef.current?.trigger('keyboard', 'undo', null)}
325
+ />,
326
+ <CodeEditorControl
327
+ key="redo"
328
+ icon={<RhUiRedoIcon />}
329
+ aria-label="Redo"
330
+ tooltipProps={{
331
+ content: 'Redo',
332
+ // prevents VO announcements of both aria label and tooltip
333
+ aria: 'none'
334
+ }}
335
+ onClick={() => editorRef.current?.trigger('keyboard', 'redo', null)}
336
+ />,
337
+ <CodeEditorControl
338
+ key="export"
339
+ icon={<RhUiExportIcon />}
340
+ aria-label="Export"
341
+ tooltipProps={{
342
+ content: 'Export',
343
+ // prevents VO announcements of both aria label and tooltip
344
+ aria: 'none'
345
+ }}
346
+ onClick={(value) => {
347
+ // eslint-disable-next-line no-console
348
+ console.log('Export', value);
349
+ }}
350
+ />
351
+ ];
352
+
353
+ const panelContent = (
354
+ <DrawerPanelContent
355
+ isResizable
356
+ isGlass
357
+ defaultSize="50%"
358
+ minSize="20%"
359
+ id="chatbot-canvas-panel"
360
+ resizeAriaLabel="Resize canvas"
361
+ className="pf-chatbot__canvas-panel"
362
+ >
363
+ <section
364
+ ref={canvasSectionRef}
365
+ className="pf-chatbot__canvas-section"
366
+ tabIndex={-1}
367
+ aria-label="Canvas"
368
+ aria-labelledby="chatbot-canvas-section chatbot-canvas-title"
369
+ id="chatbot-canvas-section"
370
+ >
371
+ <DrawerHead className="pf-chatbot__canvas-head">
372
+ <Flex spaceItems={{ default: 'spaceItemsMd' }} alignItems={{ default: 'alignItemsCenter' }}>
373
+ <FlexItem>
374
+ <Title id="chatbot-canvas-title" headingLevel="h2" size="lg">
375
+ Edit code
376
+ </Title>
377
+ </FlexItem>
378
+ <FlexItem>
379
+ <Popover
380
+ headerContent={
381
+ <Flex spaceItems={{ default: 'spaceItemsSm' }}>
382
+ <FlexItem>
383
+ <RhUiAiInfoIcon aria-hidden />
384
+ </FlexItem>
385
+ <FlexItem>What is canvas mode?</FlexItem>
386
+ </Flex>
387
+ }
388
+ bodyContent={
389
+ <>
390
+ <div className="pf-v6-u-font-size-sm pf-v6-u-mb-md">
391
+ This canvas is a collaborative workspace that blends AI-generated content with manual human edits.
392
+ You can accept an AI-generated baseline and immediately mold it with direct keystrokes, creating a
393
+ seamless partnership over a shared deliverable.
394
+ </div>
395
+ <div className="pf-v6-u-font-size-xs">Always review AI-generated code prior to use.</div>
396
+ </>
397
+ }
398
+ onShow={() => setIsGeneratedAiPopoverOpen(true)}
399
+ onHide={() => setIsGeneratedAiPopoverOpen(false)}
400
+ >
401
+ <Label
402
+ isClickable
403
+ variant="outline"
404
+ icon={<RhUiAiEditIcon aria-hidden />}
405
+ role="button"
406
+ tabIndex={0}
407
+ aria-expanded={isGeneratedAiPopoverOpen}
408
+ onKeyDown={handleGeneratedAiLabelKeyDown}
409
+ >
410
+ Generated with AI
411
+ </Label>
412
+ </Popover>
413
+ </FlexItem>
414
+ </Flex>
415
+ <DrawerActions>
416
+ <Tooltip content="Close canvas" position="bottom" aria="none">
417
+ <span>
418
+ <DrawerCloseButton aria-label="Exit canvas mode" onClose={closeCanvasMode} />
419
+ </span>
420
+ </Tooltip>
421
+ </DrawerActions>
422
+ </DrawerHead>
423
+ <div className="pf-chatbot__canvas-panel-body">
424
+ <div className="pf-chatbot__canvas-editor">
425
+ <CodeEditor
426
+ isFullHeight
427
+ isLineNumbersVisible
428
+ isLanguageLabelVisible
429
+ isCopyEnabled
430
+ isDownloadEnabled
431
+ downloadFileName="canvas-mode"
432
+ customControls={customControls}
433
+ code={code}
434
+ language={Language.yaml}
435
+ onCodeChange={setCode}
436
+ onEditorDidMount={(editor) => {
437
+ editorRef.current = editor;
438
+ }}
439
+ />
440
+ </div>
441
+ </div>
442
+ </section>
443
+ </DrawerPanelContent>
444
+ );
445
+
446
+ return (
447
+ <>
448
+ {/* This is required for react-dropzone to work in Safari and Firefox */}
449
+ <input {...getInputProps()} hidden />
450
+ <Chatbot displayMode={displayMode} isVisible={chatbotVisible}>
451
+ <div className="pf-chatbot__canvas">
452
+ <ChatbotConversationHistoryNav
453
+ displayMode={displayMode}
454
+ onDrawerToggle={() => {
455
+ setIsDrawerOpen(!isDrawerOpen);
456
+ setConversations(initialConversations);
457
+ }}
458
+ isDrawerOpen={isDrawerOpen}
459
+ setIsDrawerOpen={setIsDrawerOpen}
460
+ activeItemId="1"
461
+ conversations={conversations}
462
+ drawerCloseButtonProps={{ 'aria-label': 'Close chat history' }}
463
+ onNewChat={() => {
464
+ setIsDrawerOpen(!isDrawerOpen);
465
+ setMessages([]);
466
+ setConversations(initialConversations);
467
+ }}
468
+ handleTextInputChange={(value: string) => {
469
+ if (value === '') {
470
+ setConversations(initialConversations);
471
+ }
472
+ const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value);
473
+ setConversations(newConversations);
474
+ }}
475
+ drawerContent={
476
+ <Drawer
477
+ className="pf-chatbot__canvas-drawer"
478
+ isExpanded={isCanvasOpen}
479
+ isInline
480
+ position="end"
481
+ onExpand={onCanvasExpand}
482
+ >
483
+ <DrawerContent panelContent={panelContent}>
484
+ <DrawerContentBody className="pf-chatbot__canvas-body">
485
+ <div className="pf-chatbot__canvas-column">
486
+ <ChatbotHeader>
487
+ <ChatbotHeaderMain>
488
+ <ChatbotHeaderMenu
489
+ ref={historyRef}
490
+ aria-expanded={isDrawerOpen}
491
+ onMenuToggle={() => setIsDrawerOpen(!isDrawerOpen)}
492
+ tooltipContent="Chat history"
493
+ menuAriaLabel="Chat history"
494
+ />
495
+ <ChatbotHeaderTitle>{iconLogo}</ChatbotHeaderTitle>
496
+ </ChatbotHeaderMain>
497
+ <ChatbotHeaderActions>
498
+ <ChatbotHeaderCloseButton
499
+ tooltipContent="Close ChatBot"
500
+ menuAriaLabel="Close ChatBot"
501
+ onClick={() => setChatbotVisible(false)}
502
+ />
503
+ </ChatbotHeaderActions>
504
+ </ChatbotHeader>
505
+ <ChatbotContent>
506
+ <MessageBox ariaLabel="Scrollable message log for ChatBot" announcement={announcement}>
507
+ {messages.map((message) => (
508
+ <Message
509
+ key={message.id}
510
+ {...message}
511
+ attachments={message.attachments?.map((attachment) => ({
512
+ ...attachment,
513
+ onClick: openCanvas
514
+ }))}
515
+ />
516
+ ))}
517
+ <div ref={scrollToBottomRef}></div>
518
+ </MessageBox>
519
+ </ChatbotContent>
520
+ <ChatbotFooter>
521
+ <MessageBar
522
+ onSendMessage={handleSend}
523
+ innerRef={messageInputRef}
524
+ attachButtonPosition="start"
525
+ alwayShowSendButton
526
+ isSendButtonDisabled={isSendButtonDisabled}
527
+ attachMenuProps={{
528
+ isAttachMenuOpen,
529
+ setIsAttachMenuOpen,
530
+ attachMenuItems,
531
+ onAttachMenuOnOpenChangeKeys: ['Escape', 'Tab'],
532
+ onAttachMenuSelect: (_ev, value) => {
533
+ // eslint-disable-next-line no-console
534
+ console.log('selected', value);
535
+ if (value === 'canvas') {
536
+ if (showCanvasLabel) {
537
+ closeCanvasMode();
538
+ } else {
539
+ setShowCanvasLabel(true);
540
+ setIsCanvasOpen(true);
541
+ }
542
+ }
543
+ setIsAttachMenuOpen(false);
544
+ focusMessageActions();
545
+ },
546
+ onAttachMenuToggleClick: () => setIsAttachMenuOpen(!isAttachMenuOpen)
547
+ }}
548
+ buttonProps={{
549
+ attach: {
550
+ innerRef: messageActionsRef,
551
+ icon: <RhUiAddIcon />,
552
+ tooltipContent: 'Message actions',
553
+ 'aria-label': 'Message actions'
554
+ }
555
+ }}
556
+ additionalActions={
557
+ <>
558
+ {showCanvasLabel && (
559
+ <Label
560
+ closeBtnAriaLabel="Exit canvas mode"
561
+ onClose={closeCanvasMode}
562
+ icon={<RhUiImageFillIcon />}
563
+ >
564
+ Canvas
565
+ </Label>
566
+ )}
567
+ <Select
568
+ isOpen={isModelSelectOpen}
569
+ selected={selectedModel}
570
+ shouldFocusToggleOnSelect
571
+ onSelect={onModelSelect}
572
+ onOpenChange={(isOpen) => setIsModelSelectOpen(isOpen)}
573
+ toggle={(toggleRef) => (
574
+ <MenuToggle
575
+ ref={toggleRef}
576
+ variant="plainText"
577
+ className="pf-chatbot__canvas-model-toggle"
578
+ onClick={() => setIsModelSelectOpen(!isModelSelectOpen)}
579
+ isExpanded={isModelSelectOpen}
580
+ aria-label={`${selectedModel}, Select a model`}
581
+ >
582
+ {selectedModel}
583
+ </MenuToggle>
584
+ )}
585
+ >
586
+ <SelectList>
587
+ {modelOptions.map((option) => (
588
+ <SelectOption key={option} value={option}>
589
+ {option}
590
+ </SelectOption>
591
+ ))}
592
+ </SelectList>
593
+ </Select>
594
+ </>
595
+ }
596
+ />
597
+ <ChatbotFootnote {...footnoteProps} />
598
+ </ChatbotFooter>
599
+ </div>
600
+ </DrawerContentBody>
601
+ </DrawerContent>
602
+ </Drawer>
603
+ }
604
+ />
605
+ </div>
606
+ </Chatbot>
607
+ </>
608
+ );
609
+ };
@@ -48,11 +48,28 @@ ChatbotHeaderOptionsDropdown,
48
48
  ChatbotHeaderCloseButton,
49
49
  } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
50
50
 
51
- import { RhUiExpandIcon, RhUiOpenDrawerRightIcon, RhUiMenuBarsIcon, RhUiRestoreWindowIcon } from '@patternfly/react-icons';
51
+ import { RhUiExpandIcon, RhUiOpenDrawerRightIcon, RhUiMenuBarsIcon, RhUiRestoreWindowIcon, RhUiAiEditIcon } from '@patternfly/react-icons';
52
+ import { CodeEditor, Language } from '@patternfly/react-code-editor';
53
+ import { useDropzone } from 'react-dropzone';
54
+ import RhUiAddIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-add-icon';
55
+ import RhUiClipboardIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-clipboard-icon';
56
+ import RhUiCodeIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-code-icon';
57
+ import RhUiUploadIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-upload-icon';
52
58
  import { BarsIcon } from '@patternfly/react-icons/dist/esm/icons/bars-icon';
53
59
  import { RhUiBuildFillIcon, RhUiCopyFillIcon } from '@patternfly/react-icons';
60
+ import RhUiImageFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-image-fill-icon';
54
61
  import {
55
62
  Button,
63
+ Divider,
64
+ Drawer,
65
+ DrawerActions,
66
+ DrawerCloseButton,
67
+ DrawerContent,
68
+ DrawerContentBody,
69
+ DrawerHead,
70
+ DrawerPanelContent,
71
+ DropdownItem,
72
+ DropdownList,
56
73
  DescriptionList,
57
74
  DescriptionListDescription,
58
75
  DescriptionListGroup,
@@ -61,7 +78,12 @@ ExpandableSection,
61
78
  ExpandableSectionVariant,
62
79
  Flex,
63
80
  FlexItem,
64
- Label
81
+ Label,
82
+ MenuToggle,
83
+ Select,
84
+ SelectList,
85
+ SelectOption,
86
+ Title
65
87
  } from '@patternfly/react-core';
66
88
  import PFHorizontalLogoColor from '../UI/PF-HorizontalLogo-Color.svg';
67
89
  import PFHorizontalLogoReverse from '../UI/PF-HorizontalLogo-Reverse.svg';
@@ -70,7 +92,7 @@ import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg';
70
92
  import userAvatar from '../Messages/user_avatar.svg';
71
93
  import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
72
94
  import { getTrackingProviders } from "@patternfly/chatbot/dist/dynamic/tracking";
73
- import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent, MouseEvent as ReactMouseEvent } from 'react';
95
+ import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent, MouseEvent as ReactMouseEvent, ReactNode } from 'react';
74
96
  import saveAs from 'file-saver';
75
97
 
76
98
  ### Basic ChatBot
@@ -1,6 +1,6 @@
1
1
  // This module is shared between NodeJS and babelled ES5
2
2
  module.exports = {
3
- sideNavItems: [{ section: 'extensions' }],
3
+ sideNavItems: [{ section: 'extensions' }, { section: 'patterns' }],
4
4
  topNavItems: [],
5
5
  hasThemeSwitcher: true,
6
6
  hasRTLSwitcher: true,