@patternfly/chatbot 6.5.0-prerelease.22 → 6.5.0-prerelease.24

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 (110) hide show
  1. package/dist/cjs/DeepThinking/DeepThinking.d.ts +11 -0
  2. package/dist/cjs/DeepThinking/DeepThinking.js +30 -2
  3. package/dist/cjs/DeepThinking/DeepThinking.test.js +39 -0
  4. package/dist/cjs/MarkdownContent/MarkdownContent.d.ts +39 -0
  5. package/dist/cjs/MarkdownContent/MarkdownContent.js +181 -0
  6. package/dist/cjs/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  7. package/dist/cjs/MarkdownContent/MarkdownContent.test.js +192 -0
  8. package/dist/cjs/MarkdownContent/index.d.ts +2 -0
  9. package/dist/cjs/MarkdownContent/index.js +23 -0
  10. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  11. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
  12. package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +5 -1
  13. package/dist/cjs/Message/LinkMessage/LinkMessage.js +4 -3
  14. package/dist/cjs/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  15. package/dist/cjs/Message/ListMessage/OrderedListMessage.js +2 -1
  16. package/dist/cjs/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  17. package/dist/cjs/Message/ListMessage/UnorderedListMessage.js +2 -1
  18. package/dist/cjs/Message/Message.js +2 -155
  19. package/dist/cjs/Message/TableMessage/TableMessage.d.ts +6 -1
  20. package/dist/cjs/Message/TableMessage/TableMessage.js +3 -2
  21. package/dist/cjs/Message/TextMessage/TextMessage.d.ts +8 -1
  22. package/dist/cjs/Message/TextMessage/TextMessage.js +3 -2
  23. package/dist/cjs/Message/UserFeedback/UserFeedback.d.ts +2 -0
  24. package/dist/cjs/Message/UserFeedback/UserFeedback.js +5 -5
  25. package/dist/cjs/MessageBar/MessageBar.js +18 -4
  26. package/dist/cjs/ToolCall/ToolCall.d.ts +9 -0
  27. package/dist/cjs/ToolCall/ToolCall.js +19 -3
  28. package/dist/cjs/ToolCall/ToolCall.test.js +31 -0
  29. package/dist/cjs/ToolResponse/ToolResponse.d.ts +15 -0
  30. package/dist/cjs/ToolResponse/ToolResponse.js +48 -2
  31. package/dist/cjs/ToolResponse/ToolResponse.test.js +60 -0
  32. package/dist/cjs/index.d.ts +2 -0
  33. package/dist/cjs/index.js +4 -1
  34. package/dist/css/main.css +82 -9
  35. package/dist/css/main.css.map +1 -1
  36. package/dist/dynamic/MarkdownContent/package.json +1 -0
  37. package/dist/esm/DeepThinking/DeepThinking.d.ts +11 -0
  38. package/dist/esm/DeepThinking/DeepThinking.js +27 -2
  39. package/dist/esm/DeepThinking/DeepThinking.test.js +39 -0
  40. package/dist/esm/MarkdownContent/MarkdownContent.d.ts +39 -0
  41. package/dist/esm/MarkdownContent/MarkdownContent.js +174 -0
  42. package/dist/esm/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  43. package/dist/esm/MarkdownContent/MarkdownContent.test.js +187 -0
  44. package/dist/esm/MarkdownContent/index.d.ts +2 -0
  45. package/dist/esm/MarkdownContent/index.js +2 -0
  46. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  47. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
  48. package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +5 -1
  49. package/dist/esm/Message/LinkMessage/LinkMessage.js +4 -3
  50. package/dist/esm/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  51. package/dist/esm/Message/ListMessage/OrderedListMessage.js +2 -1
  52. package/dist/esm/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  53. package/dist/esm/Message/ListMessage/UnorderedListMessage.js +2 -1
  54. package/dist/esm/Message/Message.js +3 -156
  55. package/dist/esm/Message/TableMessage/TableMessage.d.ts +6 -1
  56. package/dist/esm/Message/TableMessage/TableMessage.js +3 -2
  57. package/dist/esm/Message/TextMessage/TextMessage.d.ts +8 -1
  58. package/dist/esm/Message/TextMessage/TextMessage.js +3 -2
  59. package/dist/esm/Message/UserFeedback/UserFeedback.d.ts +2 -0
  60. package/dist/esm/Message/UserFeedback/UserFeedback.js +6 -6
  61. package/dist/esm/MessageBar/MessageBar.js +18 -4
  62. package/dist/esm/ToolCall/ToolCall.d.ts +9 -0
  63. package/dist/esm/ToolCall/ToolCall.js +16 -3
  64. package/dist/esm/ToolCall/ToolCall.test.js +31 -0
  65. package/dist/esm/ToolResponse/ToolResponse.d.ts +15 -0
  66. package/dist/esm/ToolResponse/ToolResponse.js +45 -2
  67. package/dist/esm/ToolResponse/ToolResponse.test.js +60 -0
  68. package/dist/esm/index.d.ts +2 -0
  69. package/dist/esm/index.js +2 -0
  70. package/dist/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +1 -1
  72. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithFeedback.tsx +14 -1
  73. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownDeepThinking.tsx +26 -0
  74. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolCall.tsx +29 -0
  75. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolResponse.tsx +200 -0
  76. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +32 -0
  77. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +15 -1
  78. package/src/DeepThinking/DeepThinking.test.tsx +48 -0
  79. package/src/DeepThinking/DeepThinking.tsx +50 -4
  80. package/src/MarkdownContent/MarkdownContent.test.tsx +207 -0
  81. package/src/MarkdownContent/MarkdownContent.tsx +264 -0
  82. package/src/MarkdownContent/index.ts +2 -0
  83. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +4 -0
  84. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +5 -1
  85. package/src/Message/LinkMessage/LinkMessage.scss +5 -0
  86. package/src/Message/LinkMessage/LinkMessage.tsx +24 -2
  87. package/src/Message/ListMessage/ListMessage.scss +8 -0
  88. package/src/Message/ListMessage/OrderedListMessage.tsx +16 -2
  89. package/src/Message/ListMessage/UnorderedListMessage.tsx +12 -2
  90. package/src/Message/Message.tsx +21 -181
  91. package/src/Message/QuickResponse/QuickResponse.scss +3 -1
  92. package/src/Message/TableMessage/TableMessage.scss +11 -0
  93. package/src/Message/TableMessage/TableMessage.tsx +18 -2
  94. package/src/Message/TextMessage/TextMessage.scss +8 -0
  95. package/src/Message/TextMessage/TextMessage.tsx +29 -2
  96. package/src/Message/UserFeedback/UserFeedback.scss +28 -1
  97. package/src/Message/UserFeedback/UserFeedback.tsx +22 -12
  98. package/src/MessageBar/AttachButton.scss +0 -1
  99. package/src/MessageBar/MessageBar.scss +11 -2
  100. package/src/MessageBar/MessageBar.tsx +22 -3
  101. package/src/MessageBar/MicrophoneButton.scss +0 -1
  102. package/src/MessageBar/SendButton.scss +0 -1
  103. package/src/MessageBar/StopButton.scss +0 -1
  104. package/src/ToolCall/ToolCall.test.tsx +40 -0
  105. package/src/ToolCall/ToolCall.tsx +37 -3
  106. package/src/ToolResponse/ToolResponse.scss +10 -0
  107. package/src/ToolResponse/ToolResponse.test.tsx +75 -0
  108. package/src/ToolResponse/ToolResponse.tsx +78 -6
  109. package/src/index.ts +3 -0
  110. package/src/main.scss +1 -0
@@ -4,9 +4,23 @@
4
4
 
5
5
  import { ExtraProps } from 'react-markdown';
6
6
  import { List, ListComponent, OrderType } from '@patternfly/react-core';
7
+ import { css } from '@patternfly/react-styles';
7
8
 
8
- const OrderedListMessage = ({ children, start }: JSX.IntrinsicElements['ol'] & ExtraProps) => (
9
- <div className="pf-chatbot__message-ordered-list">
9
+ export interface OrderedListMessageProps {
10
+ /** The ordered list content */
11
+ children?: React.ReactNode;
12
+ /** The number to start the ordered list at. */
13
+ start?: number;
14
+ /** Flag indicating that the content should retain message styles when using Markdown. */
15
+ shouldRetainStyles?: boolean;
16
+ }
17
+
18
+ const OrderedListMessage = ({
19
+ children,
20
+ start,
21
+ shouldRetainStyles
22
+ }: OrderedListMessageProps & JSX.IntrinsicElements['ol'] & ExtraProps) => (
23
+ <div className={css('pf-chatbot__message-ordered-list', shouldRetainStyles && 'pf-m-markdown')}>
10
24
  <List component={ListComponent.ol} type={OrderType.number} start={start}>
11
25
  {children}
12
26
  </List>
@@ -4,9 +4,19 @@
4
4
 
5
5
  import { ExtraProps } from 'react-markdown';
6
6
  import { List } from '@patternfly/react-core';
7
+ import { css } from '@patternfly/react-styles';
8
+ export interface UnrderedListMessageProps {
9
+ /** The ordered list content */
10
+ children?: React.ReactNode;
11
+ /** Flag indicating that the content should retain message styles when using Markdown. */
12
+ shouldRetainStyles?: boolean;
13
+ }
7
14
 
8
- const UnorderedListMessage = ({ children }: JSX.IntrinsicElements['ul'] & ExtraProps) => (
9
- <div className="pf-chatbot__message-unordered-list">
15
+ const UnorderedListMessage = ({
16
+ children,
17
+ shouldRetainStyles
18
+ }: UnrderedListMessageProps & JSX.IntrinsicElements['ul'] & ExtraProps) => (
19
+ <div className={css('pf-chatbot__message-unordered-list', shouldRetainStyles && 'pf-m-markdown')}>
10
20
  <List>{children}</List>
11
21
  </div>
12
22
  );
@@ -3,14 +3,12 @@
3
3
  // ============================================================================
4
4
  import { forwardRef, ReactNode, useEffect, useState } from 'react';
5
5
  import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
6
- import Markdown, { Options } from 'react-markdown';
7
- import remarkGfm from 'remark-gfm';
6
+ import { Options } from 'react-markdown';
8
7
  import {
9
8
  AlertProps,
10
9
  Avatar,
11
10
  AvatarProps,
12
11
  ButtonProps,
13
- ContentVariants,
14
12
  FormProps,
15
13
  Label,
16
14
  LabelGroupProps,
@@ -18,42 +16,25 @@ import {
18
16
  Truncate
19
17
  } from '@patternfly/react-core';
20
18
  import MessageLoading from './MessageLoading';
21
- import CodeBlockMessage, { CodeBlockMessageProps } from './CodeBlockMessage/CodeBlockMessage';
22
- import TextMessage from './TextMessage/TextMessage';
19
+ import { CodeBlockMessageProps } from './CodeBlockMessage/CodeBlockMessage';
23
20
  import FileDetailsLabel from '../FileDetailsLabel/FileDetailsLabel';
24
21
  import ResponseActions, { ActionProps } from '../ResponseActions/ResponseActions';
25
22
  import SourcesCard, { SourcesCardProps } from '../SourcesCard';
26
- import ListItemMessage from './ListMessage/ListItemMessage';
27
- import UnorderedListMessage from './ListMessage/UnorderedListMessage';
28
- import OrderedListMessage from './ListMessage/OrderedListMessage';
29
23
  import QuickStartTile from './QuickStarts/QuickStartTile';
30
24
  import { QuickStart, QuickstartAction } from './QuickStarts/types';
31
25
  import QuickResponse from './QuickResponse/QuickResponse';
32
26
  import UserFeedback, { UserFeedbackProps } from './UserFeedback/UserFeedback';
33
27
  import UserFeedbackComplete, { UserFeedbackCompleteProps } from './UserFeedback/UserFeedbackComplete';
34
- import TableMessage from './TableMessage/TableMessage';
35
- import TrMessage from './TableMessage/TrMessage';
36
- import TdMessage from './TableMessage/TdMessage';
37
- import TbodyMessage from './TableMessage/TbodyMessage';
38
- import TheadMessage from './TableMessage/TheadMessage';
39
- import ThMessage from './TableMessage/ThMessage';
40
28
  import { TableProps } from '@patternfly/react-table';
41
- import ImageMessage from './ImageMessage/ImageMessage';
42
- import rehypeUnwrapImages from 'rehype-unwrap-images';
43
- import rehypeExternalLinks from 'rehype-external-links';
44
- import rehypeSanitize from 'rehype-sanitize';
45
- import rehypeHighlight from 'rehype-highlight';
46
29
  // see the full list of styles here: https://highlightjs.org/examples
47
30
  import 'highlight.js/styles/vs2015.css';
48
31
  import { PluggableList } from 'unified';
49
- import LinkMessage from './LinkMessage/LinkMessage';
50
32
  import ErrorMessage from './ErrorMessage/ErrorMessage';
51
33
  import MessageInput from './MessageInput';
52
- import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs';
53
34
  import ToolResponse, { ToolResponseProps } from '../ToolResponse';
54
35
  import DeepThinking, { DeepThinkingProps } from '../DeepThinking';
55
- import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
56
36
  import ToolCall, { ToolCallProps } from '../ToolCall';
37
+ import MarkdownContent from '../MarkdownContent';
57
38
 
58
39
  export interface MessageAttachment {
59
40
  /** Name of file attached to the message */
@@ -267,14 +248,8 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
267
248
  }, [content]);
268
249
 
269
250
  const { beforeMainContent, afterMainContent, endContent } = extraContent || {};
270
- let rehypePlugins: PluggableList = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs, rehypeHighlight];
271
- if (openLinkInNewTab) {
272
- rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
273
- }
274
- if (additionalRehypePlugins) {
275
- rehypePlugins.push(...additionalRehypePlugins);
276
- }
277
- let avatarClassName;
251
+
252
+ let avatarClassName: string | undefined;
278
253
  if (avatarProps && 'className' in avatarProps) {
279
254
  const { className, ...rest } = avatarProps;
280
255
  avatarClassName = className;
@@ -284,157 +259,22 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
284
259
  const date = new Date();
285
260
  const dateString = timestamp ?? `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
286
261
 
287
- const disallowedElements = role === 'user' && hasNoImagesInUserMessages ? ['img'] : [];
288
- if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
289
- disallowedElements.push(...reactMarkdownProps.disallowedElements);
290
- }
291
-
292
- const handleMarkdown = () => {
293
- if (isMarkdownDisabled) {
294
- return (
295
- <TextMessage component={ContentVariants.p} {...props}>
296
- {messageText}
297
- </TextMessage>
298
- );
299
- }
300
- return (
301
- <Markdown
302
- components={{
303
- section: (props) => {
304
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
305
- const { node, ...rest } = props;
306
- return <section {...rest} className={`pf-chatbot__message-text ${rest?.className}`} />;
307
- },
308
- p: (props) => {
309
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
310
- const { node, ...rest } = props;
311
- return <TextMessage component={ContentVariants.p} {...rest} isPrimary={isPrimary} />;
312
- },
313
- code: ({ children, ...props }) => {
314
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
315
- const { node, ...codeProps } = props;
316
- return (
317
- <CodeBlockMessage {...codeProps} {...codeBlockProps} isPrimary={isPrimary}>
318
- {children}
319
- </CodeBlockMessage>
320
- );
321
- },
322
- h1: (props) => {
323
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
324
- const { node, ...rest } = props;
325
- return <TextMessage component={ContentVariants.h1} {...rest} />;
326
- },
327
- h2: (props) => {
328
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
329
- const { node, ...rest } = props;
330
- return <TextMessage component={ContentVariants.h2} {...rest} />;
331
- },
332
- h3: (props) => {
333
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
334
- const { node, ...rest } = props;
335
- return <TextMessage component={ContentVariants.h3} {...rest} />;
336
- },
337
- h4: (props) => {
338
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
339
- const { node, ...rest } = props;
340
- return <TextMessage component={ContentVariants.h4} {...rest} />;
341
- },
342
- h5: (props) => {
343
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
344
- const { node, ...rest } = props;
345
- return <TextMessage component={ContentVariants.h5} {...rest} />;
346
- },
347
- h6: (props) => {
348
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
349
- const { node, ...rest } = props;
350
- return <TextMessage component={ContentVariants.h6} {...rest} />;
351
- },
352
- blockquote: (props) => {
353
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
354
- const { node, ...rest } = props;
355
- return <TextMessage component={ContentVariants.blockquote} {...rest} />;
356
- },
357
- ul: (props) => {
358
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
359
- const { node, ...rest } = props;
360
- return <UnorderedListMessage {...rest} />;
361
- },
362
- ol: (props) => {
363
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
364
- const { node, ...rest } = props;
365
- return <OrderedListMessage {...rest} />;
366
- },
367
- li: (props) => {
368
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
369
- const { node, ...rest } = props;
370
- return <ListItemMessage {...rest} />;
371
- },
372
- // table requires node attribute for calculating headers for mobile breakpoint
373
- table: (props) => <TableMessage {...props} {...tableProps} isPrimary={isPrimary} />,
374
- tbody: (props) => {
375
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
376
- const { node, ...rest } = props;
377
- return <TbodyMessage {...rest} />;
378
- },
379
- thead: (props) => {
380
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
381
- const { node, ...rest } = props;
382
- return <TheadMessage {...rest} />;
383
- },
384
- tr: (props) => {
385
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
386
- const { node, ...rest } = props;
387
- return <TrMessage {...rest} />;
388
- },
389
- td: (props) => {
390
- // Conflicts with Td type
391
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
392
- const { node, width, ...rest } = props;
393
- return <TdMessage {...rest} />;
394
- },
395
- th: (props) => {
396
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
397
- const { node, ...rest } = props;
398
- return <ThMessage {...rest} />;
399
- },
400
- img: (props) => {
401
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
402
- const { node, ...rest } = props;
403
- return <ImageMessage {...rest} />;
404
- },
405
- a: (props) => {
406
- // node is just the details of the document structure - not needed
407
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
408
- const { node, ...rest } = props;
409
- return (
410
- // some a types conflict with ButtonProps, but it's ok because we are using an a tag
411
- // there are too many to handle manually
412
- <LinkMessage {...(rest as any)} {...linkProps}>
413
- {props.children}
414
- </LinkMessage>
415
- );
416
- },
417
- // used for footnotes
418
- sup: (props) => {
419
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
420
- const { node, ...rest } = props;
421
- return <SuperscriptMessage {...rest} />;
422
- }
423
- }}
424
- remarkPlugins={[[remarkGfm, { ...remarkGfmProps }], ...additionalRemarkPlugins]}
425
- rehypePlugins={rehypePlugins}
426
- {...reactMarkdownProps}
427
- remarkRehypeOptions={{
428
- // removes sr-only class from footnote labels applied by default
429
- footnoteLabelProperties: { className: [''] },
430
- ...reactMarkdownProps?.remarkRehypeOptions
431
- }}
432
- disallowedElements={disallowedElements}
433
- >
434
- {messageText}
435
- </Markdown>
436
- );
437
- };
262
+ const handleMarkdown = () => (
263
+ <MarkdownContent
264
+ content={messageText}
265
+ isMarkdownDisabled={isMarkdownDisabled}
266
+ codeBlockProps={codeBlockProps}
267
+ tableProps={tableProps}
268
+ openLinkInNewTab={openLinkInNewTab}
269
+ additionalRehypePlugins={additionalRehypePlugins}
270
+ additionalRemarkPlugins={additionalRemarkPlugins}
271
+ linkProps={linkProps}
272
+ reactMarkdownProps={reactMarkdownProps}
273
+ remarkGfmProps={remarkGfmProps}
274
+ hasNoImages={role === 'user' && hasNoImagesInUserMessages}
275
+ isPrimary={isPrimary}
276
+ />
277
+ );
438
278
 
439
279
  const renderMessage = () => {
440
280
  if (isLoading) {
@@ -1,6 +1,8 @@
1
1
  .pf-chatbot__message-quick-response {
2
2
  .pf-v6-c-label {
3
- --pf-v6-c-label--FontSize: var(--pf-t--global--font--size--md);
3
+ &:not(.pf-m-compact) {
4
+ --pf-v6-c-label--FontSize: var(--pf-t--global--font--size--md);
5
+ }
4
6
 
5
7
  @media screen and (min-width: 401px) and (max-width: 600px) {
6
8
  --pf-v6-c-label__text--MaxWidth: 20ch;
@@ -25,4 +25,15 @@
25
25
  .pf-v6-c-table__tr:last-of-type {
26
26
  border-block-end: 0;
27
27
  }
28
+
29
+ &.pf-m-markdown {
30
+ table,
31
+ tbody,
32
+ td,
33
+ thead,
34
+ th,
35
+ tr {
36
+ font-size: inherit;
37
+ }
38
+ }
28
39
  }
@@ -5,6 +5,7 @@
5
5
  import { Children, cloneElement } from 'react';
6
6
  import { ExtraProps } from 'react-markdown';
7
7
  import { Table, TableProps } from '@patternfly/react-table';
8
+ import { css } from '@patternfly/react-styles';
8
9
 
9
10
  interface Properties {
10
11
  line: number;
@@ -20,10 +21,20 @@ export interface TableNode {
20
21
  }
21
22
 
22
23
  export interface TableMessageProps {
24
+ /** Content of the table */
25
+ children?: React.ReactNode;
26
+ /** Flag indicating whether primary styles should be applied. */
23
27
  isPrimary?: boolean;
28
+ /** Flag indicating that the content should retain message styles when using Markdown. */
29
+ shouldRetainStyles?: boolean;
24
30
  }
25
31
 
26
- const TableMessage = ({ children, isPrimary, ...props }: Omit<TableProps, 'ref'> & ExtraProps & TableMessageProps) => {
32
+ const TableMessage = ({
33
+ children,
34
+ isPrimary,
35
+ shouldRetainStyles,
36
+ ...props
37
+ }: Omit<TableProps, 'ref'> & ExtraProps & TableMessageProps) => {
27
38
  const { className, ...rest } = props;
28
39
 
29
40
  // This allows us to parse the nested data we get back from the 3rd party Markdown parser
@@ -76,7 +87,12 @@ const TableMessage = ({ children, isPrimary, ...props }: Omit<TableProps, 'ref'>
76
87
  <Table
77
88
  aria-label={props['aria-label']}
78
89
  gridBreakPoint="grid"
79
- className={`pf-chatbot__message-table ${isPrimary ? 'pf-m-primary' : ''} ${className ? className : ''}`}
90
+ className={css(
91
+ 'pf-chatbot__message-table',
92
+ isPrimary && 'pf-m-primary',
93
+ shouldRetainStyles && 'pf-m-markdown',
94
+ className
95
+ )}
80
96
  {...rest}
81
97
  >
82
98
  {modifyChildren(children)}
@@ -53,6 +53,14 @@
53
53
  background-color: var(--pf-t--global--background--color--secondary--default);
54
54
  }
55
55
  }
56
+
57
+ &.pf-m-markdown {
58
+ display: block;
59
+ }
60
+ &.pf-m-markdown > [class^='pf-v6-c-content'] {
61
+ font-size: inherit;
62
+ color: inherit;
63
+ }
56
64
  }
57
65
 
58
66
  // ============================================================================
@@ -4,19 +4,46 @@
4
4
 
5
5
  import { ExtraProps } from 'react-markdown';
6
6
  import { Content, ContentProps } from '@patternfly/react-core';
7
+ import { css } from '@patternfly/react-styles';
7
8
 
8
9
  export interface TextMessageProps {
10
+ /** The text message content */
11
+ children?: React.ReactNode;
12
+ /** Flag indicating whether primary styling is applied. */
9
13
  isPrimary?: boolean;
14
+ /** The wrapper component to use for the PatternFly Content component. Defaults to a div. */
15
+ component?:
16
+ | 'h1'
17
+ | 'h2'
18
+ | 'h3'
19
+ | 'h4'
20
+ | 'h5'
21
+ | 'h6'
22
+ | 'p'
23
+ | 'a'
24
+ | 'small'
25
+ | 'blockquote'
26
+ | 'pre'
27
+ | 'hr'
28
+ | 'ul'
29
+ | 'ol'
30
+ | 'dl'
31
+ | 'li'
32
+ | 'dt'
33
+ | 'dd';
34
+ /** Flag indicating that the content should retain message styles when using Markdown. */
35
+ shouldRetainStyles?: boolean;
10
36
  }
11
37
 
12
38
  const TextMessage = ({
13
39
  component,
14
40
  children,
15
41
  isPrimary,
42
+ shouldRetainStyles,
16
43
  ...props
17
44
  }: Omit<ContentProps, 'ref'> & ExtraProps & TextMessageProps) => (
18
- <span className={`pf-chatbot__message-text ${isPrimary ? 'pf-m-primary' : ''}`}>
19
- <Content component={component} {...props}>
45
+ <span className={css('pf-chatbot__message-text', isPrimary && 'pf-m-primary', shouldRetainStyles && 'pf-m-markdown')}>
46
+ <Content component={component} {...props} className={css(props?.className)}>
20
47
  {children}
21
48
  </Content>
22
49
  </span>
@@ -36,6 +36,11 @@
36
36
  }
37
37
 
38
38
  // feedback card
39
+ .pf-chatbot__feedback-card-complete-header {
40
+ .pf-v6-c-card__actions {
41
+ --pf-v6-c-card__actions--MarginBlockEnd: calc(var(--pf-t--global--spacer--xl) * -1);
42
+ }
43
+ }
39
44
  .pf-chatbot__feedback-card-title {
40
45
  font-family: var(--pf-t--global--font--family--heading);
41
46
  font-size: var(--pf-t--global--font--size--md);
@@ -51,10 +56,32 @@
51
56
  font-weight: initial;
52
57
  }
53
58
 
59
+ // Privacy statement
60
+ .pf-chatbot__feedback-card-privacy {
61
+ font-size: var(--pf-t--global--font--size--body--sm);
62
+ font-weight: var(--pf-t--global--font--weight--body--default);
63
+ line-height: var(--pf-t--global--font--line-height--body);
64
+ color: var(--pf-t--global--text--color--subtle);
65
+ }
66
+
54
67
  // Compact styles
55
68
  .pf-v6-c-card.pf-m-compact.pf-chatbot__feedback-card {
69
+ --pf-v6-c-card--first-child--PaddingBlockStart: var(--pf-t--global--spacer--md);
70
+ --pf-v6-c-card--child--PaddingInlineEnd: var(--pf-t--global--spacer--md);
71
+ --pf-v6-c-card--child--PaddingInlineStart: var(--pf-t--global--spacer--md);
72
+ --pf-v6-c-card--last-child--PaddingBlockEnd: var(--pf-t--global--spacer--md);
73
+ --pf-v6-c-card__title--not--last-child--PaddingBlockEnd: var(--pf-t--global--spacer--md);
74
+
56
75
  .pf-chatbot__feedback-card-form.pf-m-compact {
57
- --pf-v6-c-form--GridGap: var(--pf-t--global--spacer--md);
76
+ --pf-v6-c-form--GridGap: var(--pf-t--global--spacer--sm);
77
+
78
+ .pf-v6-c-form__group.pf-m-action {
79
+ margin-block-start: var(--pf-t--global--spacer--sm);
80
+ }
81
+
82
+ .pf-v6-c-form-control {
83
+ font-size: var(--pf-t--global--font--size--body--sm);
84
+ }
58
85
  }
59
86
  }
60
87
 
@@ -74,6 +74,8 @@ export interface UserFeedbackProps extends Omit<CardProps, 'onSubmit'>, OUIAProp
74
74
  textAreaProps?: TextAreaProps;
75
75
  /** Additional props passed to action group */
76
76
  actionGroupProps?: ActionGroupProps;
77
+ /** Optional privacy statement text displayed under text area */
78
+ privacyStatement?: string;
77
79
  }
78
80
 
79
81
  const UserFeedback: FunctionComponent<UserFeedbackProps> = ({
@@ -102,6 +104,7 @@ const UserFeedback: FunctionComponent<UserFeedbackProps> = ({
102
104
  textAreaProps,
103
105
  actionGroupProps,
104
106
  submitButtonProps,
107
+ privacyStatement,
105
108
  ...props
106
109
  }: UserFeedbackProps) => {
107
110
  const [selectedResponse, setSelectedResponse] = useState<string>();
@@ -139,21 +142,28 @@ const UserFeedback: FunctionComponent<UserFeedbackProps> = ({
139
142
  />
140
143
  )}
141
144
  {hasTextArea && (
142
- <TextArea
143
- value={value}
144
- onChange={(_event, value) => {
145
- setValue(value);
146
- onTextAreaChange && onTextAreaChange(_event, value);
147
- }}
148
- placeholder={textAreaPlaceholder}
149
- aria-label={textAreaAriaLabel}
150
- resizeOrientation="vertical"
151
- {...textAreaProps}
152
- />
145
+ <>
146
+ <TextArea
147
+ value={value}
148
+ onChange={(_event, value) => {
149
+ setValue(value);
150
+ onTextAreaChange && onTextAreaChange(_event, value);
151
+ }}
152
+ placeholder={textAreaPlaceholder}
153
+ aria-label={textAreaAriaLabel}
154
+ resizeOrientation="vertical"
155
+ {...textAreaProps}
156
+ />
157
+ </>
153
158
  )}
159
+ {privacyStatement && <div className="pf-chatbot__feedback-card-privacy">{privacyStatement}</div>}
154
160
  {children}
155
161
  <ActionGroup {...actionGroupProps}>
156
- <Button onClick={() => onSubmit(selectedResponse, value)} {...submitButtonProps}>
162
+ <Button
163
+ onClick={() => onSubmit(selectedResponse, value)}
164
+ size={isCompact ? 'sm' : undefined}
165
+ {...submitButtonProps}
166
+ >
157
167
  {submitWord}
158
168
  </Button>
159
169
  </ActionGroup>
@@ -41,6 +41,5 @@
41
41
  .pf-v6-c-button.pf-chatbot__button--attach.pf-m-compact {
42
42
  width: 1.5rem;
43
43
  height: 1.5rem;
44
- padding: var(--pf-t--global--spacer--sm);
45
44
  align-items: center;
46
45
  }
@@ -147,13 +147,22 @@
147
147
  .pf-chatbot__message-textarea {
148
148
  font-size: var(--pf-t--global--font--size--sm) !important;
149
149
  }
150
+ }
150
151
 
152
+ .pf-m-compact {
151
153
  .pf-chatbot__message-bar-actions {
152
- padding-block-start: var(--pf-t--global--spacer--md);
153
- padding-block-end: var(--pf-t--global--spacer--md);
154
+ padding-block-start: var(--pf-t--global--spacer--sm);
155
+ padding-block-end: var(--pf-t--global--spacer--sm);
154
156
  }
155
157
  }
156
158
 
159
+ // ============================================================================
160
+ // Multiline textarea styles (2+ lines)
161
+ // ============================================================================
162
+ .pf-chatbot__message-bar.pf-m-multiline {
163
+ border-radius: calc(var(--pf-t--global--border--radius--small) * 2);
164
+ }
165
+
157
166
  // ============================================================================
158
167
  // High contrast styles
159
168
  // ============================================================================
@@ -161,6 +161,7 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
161
161
  const [isListeningMessage, setIsListeningMessage] = useState<boolean>(false);
162
162
  const [hasSentMessage, setHasSentMessage] = useState(false);
163
163
  const [isComposing, setIsComposing] = useState(false);
164
+ const [isMultiline, setIsMultiline] = useState(false);
164
165
  const inputRef = useRef<HTMLTextAreaElement>(null);
165
166
  const textareaRef = (innerRef as React.RefObject<HTMLTextAreaElement>) ?? inputRef;
166
167
  const attachButtonRef = useRef<HTMLButtonElement>(null);
@@ -209,6 +210,19 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
209
210
  return lines > 2;
210
211
  };
211
212
 
213
+ const checkIfMultiline = useCallback(
214
+ (field: HTMLTextAreaElement) => {
215
+ const parent = field.parentElement;
216
+ const grandparent = parent?.parentElement;
217
+ if (grandparent) {
218
+ const containerHeight = grandparent.offsetHeight;
219
+ const threshold = isCompact ? 56 : 70;
220
+ setIsMultiline(containerHeight > threshold);
221
+ }
222
+ },
223
+ [isCompact]
224
+ );
225
+
212
226
  const setAutoWidth = useCallback((field: HTMLTextAreaElement) => {
213
227
  const parent = field.parentElement;
214
228
  if (parent) {
@@ -263,12 +277,14 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
263
277
  if (field) {
264
278
  if (field.value === '') {
265
279
  setInitialLineHeight(field);
280
+ setIsMultiline(false);
266
281
  } else {
267
282
  setAutoHeight(field);
268
283
  setAutoWidth(field);
284
+ checkIfMultiline(field);
269
285
  }
270
286
  }
271
- }, [displayMode, message, setAutoWidth]);
287
+ }, [displayMode, message, setAutoWidth, checkIfMultiline]);
272
288
 
273
289
  useEffect(() => {
274
290
  const field = textareaRef.current;
@@ -284,13 +300,15 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
284
300
  if (textareaRef.current) {
285
301
  if (event.target.value === '') {
286
302
  setInitialLineHeight(textareaRef.current);
303
+ setIsMultiline(false);
287
304
  } else {
288
305
  setAutoHeight(textareaRef.current);
306
+ checkIfMultiline(textareaRef.current);
289
307
  }
290
308
  }
291
309
  setMessage(event.target.value);
292
310
  },
293
- [onChange]
311
+ [onChange, checkIfMultiline]
294
312
  );
295
313
 
296
314
  // Handle sending message
@@ -453,7 +471,7 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
453
471
  return (
454
472
  <AttachMenu
455
473
  toggle={(toggleRef) => (
456
- <div ref={toggleRef} className={`pf-chatbot__message-bar ${className ?? ''}`}>
474
+ <div ref={toggleRef} className={css('pf-chatbot__message-bar', isMultiline && 'pf-m-multiline', className)}>
457
475
  {messageBarContents}
458
476
  </div>
459
477
  )}
@@ -481,6 +499,7 @@ export const MessageBarBase: FunctionComponent<MessageBarProps> = ({
481
499
  isPrimary && 'pf-m-primary',
482
500
  hasAiIndicator && 'pf-v6-m-ai-indicator',
483
501
  isThinking && 'pf-v6-m-thinking',
502
+ isMultiline && 'pf-m-multiline',
484
503
  className
485
504
  )}
486
505
  >
@@ -53,6 +53,5 @@
53
53
  .pf-v6-c-button.pf-chatbot__button--microphone.pf-m-compact {
54
54
  width: 1.5rem;
55
55
  height: 1.5rem;
56
- padding: var(--pf-t--global--spacer--sm);
57
56
  align-items: center;
58
57
  }
@@ -58,6 +58,5 @@
58
58
  .pf-v6-c-button.pf-chatbot__button--send.pf-m-compact {
59
59
  width: 1.5rem;
60
60
  height: 1.5rem;
61
- padding: var(--pf-t--global--spacer--sm);
62
61
  align-items: center;
63
62
  }