@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
@@ -14,43 +14,23 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
14
14
  // Chatbot Main - Message
15
15
  // ============================================================================
16
16
  import { forwardRef, useEffect, useState } from 'react';
17
- import Markdown from 'react-markdown';
18
- import remarkGfm from 'remark-gfm';
19
- import { Avatar, ContentVariants, Label, Timestamp, Truncate } from '@patternfly/react-core';
17
+ import { Avatar, Label, Timestamp, Truncate } from '@patternfly/react-core';
20
18
  import MessageLoading from './MessageLoading';
21
- import CodeBlockMessage from './CodeBlockMessage/CodeBlockMessage';
22
- import TextMessage from './TextMessage/TextMessage';
23
19
  import FileDetailsLabel from '../FileDetailsLabel/FileDetailsLabel';
24
20
  import ResponseActions from '../ResponseActions/ResponseActions';
25
21
  import SourcesCard from '../SourcesCard';
26
- import ListItemMessage from './ListMessage/ListItemMessage';
27
- import UnorderedListMessage from './ListMessage/UnorderedListMessage';
28
- import OrderedListMessage from './ListMessage/OrderedListMessage';
29
22
  import QuickStartTile from './QuickStarts/QuickStartTile';
30
23
  import QuickResponse from './QuickResponse/QuickResponse';
31
24
  import UserFeedback from './UserFeedback/UserFeedback';
32
25
  import UserFeedbackComplete from './UserFeedback/UserFeedbackComplete';
33
- import TableMessage from './TableMessage/TableMessage';
34
- import TrMessage from './TableMessage/TrMessage';
35
- import TdMessage from './TableMessage/TdMessage';
36
- import TbodyMessage from './TableMessage/TbodyMessage';
37
- import TheadMessage from './TableMessage/TheadMessage';
38
- import ThMessage from './TableMessage/ThMessage';
39
- import ImageMessage from './ImageMessage/ImageMessage';
40
- import rehypeUnwrapImages from 'rehype-unwrap-images';
41
- import rehypeExternalLinks from 'rehype-external-links';
42
- import rehypeSanitize from 'rehype-sanitize';
43
- import rehypeHighlight from 'rehype-highlight';
44
26
  // see the full list of styles here: https://highlightjs.org/examples
45
27
  import 'highlight.js/styles/vs2015.css';
46
- import LinkMessage from './LinkMessage/LinkMessage';
47
28
  import ErrorMessage from './ErrorMessage/ErrorMessage';
48
29
  import MessageInput from './MessageInput';
49
- import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs';
50
30
  import ToolResponse from '../ToolResponse';
51
31
  import DeepThinking from '../DeepThinking';
52
- import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
53
32
  import ToolCall from '../ToolCall';
33
+ import MarkdownContent from '../MarkdownContent';
54
34
  export const MessageBase = (_a) => {
55
35
  var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, persistActionSelection, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], additionalRemarkPlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, inputRef, editFormProps, isCompact, isMarkdownDisabled, reactMarkdownProps, toolResponse, deepThinking, remarkGfmProps, toolCall, hasNoImagesInUserMessages = true, isPrimary } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "persistActionSelection", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "additionalRemarkPlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "inputRef", "editFormProps", "isCompact", "isMarkdownDisabled", "reactMarkdownProps", "toolResponse", "deepThinking", "remarkGfmProps", "toolCall", "hasNoImagesInUserMessages", "isPrimary"]);
56
36
  const [messageText, setMessageText] = useState(content);
@@ -58,13 +38,6 @@ export const MessageBase = (_a) => {
58
38
  setMessageText(content);
59
39
  }, [content]);
60
40
  const { beforeMainContent, afterMainContent, endContent } = extraContent || {};
61
- let rehypePlugins = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs, rehypeHighlight];
62
- if (openLinkInNewTab) {
63
- rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
64
- }
65
- if (additionalRehypePlugins) {
66
- rehypePlugins.push(...additionalRehypePlugins);
67
- }
68
41
  let avatarClassName;
69
42
  if (avatarProps && 'className' in avatarProps) {
70
43
  const { className } = avatarProps, rest = __rest(avatarProps, ["className"]);
@@ -74,133 +47,7 @@ export const MessageBase = (_a) => {
74
47
  // Keep timestamps consistent between Timestamp component and aria-label
75
48
  const date = new Date();
76
49
  const dateString = timestamp !== null && timestamp !== void 0 ? timestamp : `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
77
- const disallowedElements = role === 'user' && hasNoImagesInUserMessages ? ['img'] : [];
78
- if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
79
- disallowedElements.push(...reactMarkdownProps.disallowedElements);
80
- }
81
- const handleMarkdown = () => {
82
- if (isMarkdownDisabled) {
83
- return (_jsx(TextMessage, Object.assign({ component: ContentVariants.p }, props, { children: messageText })));
84
- }
85
- return (_jsx(Markdown, Object.assign({ components: {
86
- section: (props) => {
87
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
88
- const { node } = props, rest = __rest(props, ["node"]);
89
- return _jsx("section", Object.assign({}, rest, { className: `pf-chatbot__message-text ${rest === null || rest === void 0 ? void 0 : rest.className}` }));
90
- },
91
- p: (props) => {
92
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
93
- const { node } = props, rest = __rest(props, ["node"]);
94
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, rest, { isPrimary: isPrimary }));
95
- },
96
- code: (_a) => {
97
- var { children } = _a, props = __rest(_a, ["children"]);
98
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
99
- const { node } = props, codeProps = __rest(props, ["node"]);
100
- return (_jsx(CodeBlockMessage, Object.assign({}, codeProps, codeBlockProps, { isPrimary: isPrimary, children: children })));
101
- },
102
- h1: (props) => {
103
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
104
- const { node } = props, rest = __rest(props, ["node"]);
105
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.h1 }, rest));
106
- },
107
- h2: (props) => {
108
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
109
- const { node } = props, rest = __rest(props, ["node"]);
110
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.h2 }, rest));
111
- },
112
- h3: (props) => {
113
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
114
- const { node } = props, rest = __rest(props, ["node"]);
115
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.h3 }, rest));
116
- },
117
- h4: (props) => {
118
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
119
- const { node } = props, rest = __rest(props, ["node"]);
120
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.h4 }, rest));
121
- },
122
- h5: (props) => {
123
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
124
- const { node } = props, rest = __rest(props, ["node"]);
125
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.h5 }, rest));
126
- },
127
- h6: (props) => {
128
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
129
- const { node } = props, rest = __rest(props, ["node"]);
130
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.h6 }, rest));
131
- },
132
- blockquote: (props) => {
133
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
134
- const { node } = props, rest = __rest(props, ["node"]);
135
- return _jsx(TextMessage, Object.assign({ component: ContentVariants.blockquote }, rest));
136
- },
137
- ul: (props) => {
138
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
139
- const { node } = props, rest = __rest(props, ["node"]);
140
- return _jsx(UnorderedListMessage, Object.assign({}, rest));
141
- },
142
- ol: (props) => {
143
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
144
- const { node } = props, rest = __rest(props, ["node"]);
145
- return _jsx(OrderedListMessage, Object.assign({}, rest));
146
- },
147
- li: (props) => {
148
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
149
- const { node } = props, rest = __rest(props, ["node"]);
150
- return _jsx(ListItemMessage, Object.assign({}, rest));
151
- },
152
- // table requires node attribute for calculating headers for mobile breakpoint
153
- table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps, { isPrimary: isPrimary })),
154
- tbody: (props) => {
155
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
156
- const { node } = props, rest = __rest(props, ["node"]);
157
- return _jsx(TbodyMessage, Object.assign({}, rest));
158
- },
159
- thead: (props) => {
160
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
161
- const { node } = props, rest = __rest(props, ["node"]);
162
- return _jsx(TheadMessage, Object.assign({}, rest));
163
- },
164
- tr: (props) => {
165
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
166
- const { node } = props, rest = __rest(props, ["node"]);
167
- return _jsx(TrMessage, Object.assign({}, rest));
168
- },
169
- td: (props) => {
170
- // Conflicts with Td type
171
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
172
- const { node, width } = props, rest = __rest(props, ["node", "width"]);
173
- return _jsx(TdMessage, Object.assign({}, rest));
174
- },
175
- th: (props) => {
176
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
177
- const { node } = props, rest = __rest(props, ["node"]);
178
- return _jsx(ThMessage, Object.assign({}, rest));
179
- },
180
- img: (props) => {
181
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
182
- const { node } = props, rest = __rest(props, ["node"]);
183
- return _jsx(ImageMessage, Object.assign({}, rest));
184
- },
185
- a: (props) => {
186
- // node is just the details of the document structure - not needed
187
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
188
- const { node } = props, rest = __rest(props, ["node"]);
189
- return (
190
- // some a types conflict with ButtonProps, but it's ok because we are using an a tag
191
- // there are too many to handle manually
192
- _jsx(LinkMessage, Object.assign({}, rest, linkProps, { children: props.children })));
193
- },
194
- // used for footnotes
195
- sup: (props) => {
196
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
197
- const { node } = props, rest = __rest(props, ["node"]);
198
- return _jsx(SuperscriptMessage, Object.assign({}, rest));
199
- }
200
- }, remarkPlugins: [[remarkGfm, Object.assign({}, remarkGfmProps)], ...additionalRemarkPlugins], rehypePlugins: rehypePlugins }, reactMarkdownProps, { remarkRehypeOptions: Object.assign({
201
- // removes sr-only class from footnote labels applied by default
202
- footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), disallowedElements: disallowedElements, children: messageText })));
203
- };
50
+ const handleMarkdown = () => (_jsx(MarkdownContent, { content: messageText, isMarkdownDisabled: isMarkdownDisabled, codeBlockProps: codeBlockProps, tableProps: tableProps, openLinkInNewTab: openLinkInNewTab, additionalRehypePlugins: additionalRehypePlugins, additionalRemarkPlugins: additionalRemarkPlugins, linkProps: linkProps, reactMarkdownProps: reactMarkdownProps, remarkGfmProps: remarkGfmProps, hasNoImages: role === 'user' && hasNoImagesInUserMessages, isPrimary: isPrimary }));
204
51
  const renderMessage = () => {
205
52
  if (isLoading) {
206
53
  return _jsx(MessageLoading, { loadingWord: loadingWord, isPrimary: isPrimary });
@@ -16,7 +16,12 @@ export interface TableNode {
16
16
  type: string;
17
17
  }
18
18
  export interface TableMessageProps {
19
+ /** Content of the table */
20
+ children?: React.ReactNode;
21
+ /** Flag indicating whether primary styles should be applied. */
19
22
  isPrimary?: boolean;
23
+ /** Flag indicating that the content should retain message styles when using Markdown. */
24
+ shouldRetainStyles?: boolean;
20
25
  }
21
- declare const TableMessage: ({ children, isPrimary, ...props }: Omit<TableProps, "ref"> & ExtraProps & TableMessageProps) => import("react/jsx-runtime").JSX.Element;
26
+ declare const TableMessage: ({ children, isPrimary, shouldRetainStyles, ...props }: Omit<TableProps, "ref"> & ExtraProps & TableMessageProps) => import("react/jsx-runtime").JSX.Element;
22
27
  export default TableMessage;
@@ -15,9 +15,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
15
15
  // ============================================================================
16
16
  import { Children, cloneElement } from 'react';
17
17
  import { Table } from '@patternfly/react-table';
18
+ import { css } from '@patternfly/react-styles';
18
19
  const TableMessage = (_a) => {
19
20
  var _b;
20
- var { children, isPrimary } = _a, props = __rest(_a, ["children", "isPrimary"]);
21
+ var { children, isPrimary, shouldRetainStyles } = _a, props = __rest(_a, ["children", "isPrimary", "shouldRetainStyles"]);
21
22
  const { className } = props, rest = __rest(props, ["className"]);
22
23
  // This allows us to parse the nested data we get back from the 3rd party Markdown parser
23
24
  // Open to feedback here if there is a better way to do this
@@ -58,6 +59,6 @@ const TableMessage = (_a) => {
58
59
  }
59
60
  return (
60
61
  // gridBreakPoint is so we show mobile-styled-PF table
61
- _jsx(Table, Object.assign({ "aria-label": props['aria-label'], gridBreakPoint: "grid", className: `pf-chatbot__message-table ${isPrimary ? 'pf-m-primary' : ''} ${className ? className : ''}` }, rest, { children: modifyChildren(children) })));
62
+ _jsx(Table, Object.assign({ "aria-label": props['aria-label'], gridBreakPoint: "grid", className: css('pf-chatbot__message-table', isPrimary && 'pf-m-primary', shouldRetainStyles && 'pf-m-markdown', className) }, rest, { children: modifyChildren(children) })));
62
63
  };
63
64
  export default TableMessage;
@@ -1,7 +1,14 @@
1
1
  import { ExtraProps } from 'react-markdown';
2
2
  import { ContentProps } from '@patternfly/react-core';
3
3
  export interface TextMessageProps {
4
+ /** The text message content */
5
+ children?: React.ReactNode;
6
+ /** Flag indicating whether primary styling is applied. */
4
7
  isPrimary?: boolean;
8
+ /** The wrapper component to use for the PatternFly Content component. Defaults to a div. */
9
+ component?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'a' | 'small' | 'blockquote' | 'pre' | 'hr' | 'ul' | 'ol' | 'dl' | 'li' | 'dt' | 'dd';
10
+ /** Flag indicating that the content should retain message styles when using Markdown. */
11
+ shouldRetainStyles?: boolean;
5
12
  }
6
- declare const TextMessage: ({ component, children, isPrimary, ...props }: Omit<ContentProps, "ref"> & ExtraProps & TextMessageProps) => import("react/jsx-runtime").JSX.Element;
13
+ declare const TextMessage: ({ component, children, isPrimary, shouldRetainStyles, ...props }: Omit<ContentProps, "ref"> & ExtraProps & TextMessageProps) => import("react/jsx-runtime").JSX.Element;
7
14
  export default TextMessage;
@@ -11,8 +11,9 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
13
  import { Content } from '@patternfly/react-core';
14
+ import { css } from '@patternfly/react-styles';
14
15
  const TextMessage = (_a) => {
15
- var { component, children, isPrimary } = _a, props = __rest(_a, ["component", "children", "isPrimary"]);
16
- return (_jsx("span", { className: `pf-chatbot__message-text ${isPrimary ? 'pf-m-primary' : ''}`, children: _jsx(Content, Object.assign({ component: component }, props, { children: children })) }));
16
+ var { component, children, isPrimary, shouldRetainStyles } = _a, props = __rest(_a, ["component", "children", "isPrimary", "shouldRetainStyles"]);
17
+ return (_jsx("span", { className: css('pf-chatbot__message-text', isPrimary && 'pf-m-primary', shouldRetainStyles && 'pf-m-markdown'), children: _jsx(Content, Object.assign({ component: component }, props, { className: css(props === null || props === void 0 ? void 0 : props.className), children: children })) }));
17
18
  };
18
19
  export default TextMessage;
@@ -48,6 +48,8 @@ export interface UserFeedbackProps extends Omit<CardProps, 'onSubmit'>, OUIAProp
48
48
  textAreaProps?: TextAreaProps;
49
49
  /** Additional props passed to action group */
50
50
  actionGroupProps?: ActionGroupProps;
51
+ /** Optional privacy statement text displayed under text area */
52
+ privacyStatement?: string;
51
53
  }
52
54
  declare const UserFeedback: FunctionComponent<UserFeedbackProps>;
53
55
  export default UserFeedback;
@@ -9,14 +9,14 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { useState, useRef, useEffect } from 'react';
14
14
  // Import PatternFly components
15
15
  import { ActionGroup, Button, Card, CardBody, CardHeader, Form, TextArea } from '@patternfly/react-core';
16
16
  import QuickResponse from '../QuickResponse/QuickResponse';
17
17
  import CloseButton from './CloseButton';
18
18
  const UserFeedback = (_a) => {
19
- var { className, timestamp, title = 'Why did you choose this rating?', hasTextArea, textAreaAriaLabel = `Provide optional additional feedback for message received at ${timestamp}`, textAreaPlaceholder = 'Provide optional additional feedback', onTextAreaChange, submitWord = 'Submit', quickResponses, quickResponseContainerProps = { 'aria-label': `Quick feedback for message received at ${timestamp}` }, onSubmit, onClose, closeButtonAriaLabel = `Close feedback for message received at ${timestamp}`, id, headingLevel: HeadingLevel = 'h1', focusOnLoad = true, isCompact, children, cardHeaderProps, cardBodyProps, headingLevelProps, formProps, textAreaProps, actionGroupProps, submitButtonProps } = _a, props = __rest(_a, ["className", "timestamp", "title", "hasTextArea", "textAreaAriaLabel", "textAreaPlaceholder", "onTextAreaChange", "submitWord", "quickResponses", "quickResponseContainerProps", "onSubmit", "onClose", "closeButtonAriaLabel", "id", "headingLevel", "focusOnLoad", "isCompact", "children", "cardHeaderProps", "cardBodyProps", "headingLevelProps", "formProps", "textAreaProps", "actionGroupProps", "submitButtonProps"]);
19
+ var { className, timestamp, title = 'Why did you choose this rating?', hasTextArea, textAreaAriaLabel = `Provide optional additional feedback for message received at ${timestamp}`, textAreaPlaceholder = 'Provide optional additional feedback', onTextAreaChange, submitWord = 'Submit', quickResponses, quickResponseContainerProps = { 'aria-label': `Quick feedback for message received at ${timestamp}` }, onSubmit, onClose, closeButtonAriaLabel = `Close feedback for message received at ${timestamp}`, id, headingLevel: HeadingLevel = 'h1', focusOnLoad = true, isCompact, children, cardHeaderProps, cardBodyProps, headingLevelProps, formProps, textAreaProps, actionGroupProps, submitButtonProps, privacyStatement } = _a, props = __rest(_a, ["className", "timestamp", "title", "hasTextArea", "textAreaAriaLabel", "textAreaPlaceholder", "onTextAreaChange", "submitWord", "quickResponses", "quickResponseContainerProps", "onSubmit", "onClose", "closeButtonAriaLabel", "id", "headingLevel", "focusOnLoad", "isCompact", "children", "cardHeaderProps", "cardBodyProps", "headingLevelProps", "formProps", "textAreaProps", "actionGroupProps", "submitButtonProps", "privacyStatement"]);
20
20
  const [selectedResponse, setSelectedResponse] = useState();
21
21
  const [value, setValue] = useState('');
22
22
  const divRef = useRef(null);
@@ -30,9 +30,9 @@ const UserFeedback = (_a) => {
30
30
  /* card does not have ref forwarding; hence wrapper div */
31
31
  _jsx("div", { ref: divRef, id: id, tabIndex: 0, "aria-label": title, children: _jsxs(Card, Object.assign({ isCompact: isCompact, className: `pf-chatbot__feedback-card ${className ? className : ''}` }, props, { children: [_jsx(CardHeader, Object.assign({ actions: {
32
32
  actions: _jsx(CloseButton, { onClose: onClose, ariaLabel: closeButtonAriaLabel })
33
- } }, cardHeaderProps, { children: _jsx(HeadingLevel, Object.assign({ className: "pf-chatbot__feedback-card-title" }, headingLevelProps, { children: title })) })), _jsx(CardBody, Object.assign({}, cardBodyProps, { children: _jsxs(Form, Object.assign({ className: `pf-chatbot__feedback-card-form ${isCompact ? 'pf-m-compact' : ''}` }, formProps, { children: [quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, onSelect: (id) => setSelectedResponse(id), isCompact: isCompact })), hasTextArea && (_jsx(TextArea, Object.assign({ value: value, onChange: (_event, value) => {
34
- setValue(value);
35
- onTextAreaChange && onTextAreaChange(_event, value);
36
- }, placeholder: textAreaPlaceholder, "aria-label": textAreaAriaLabel, resizeOrientation: "vertical" }, textAreaProps))), children, _jsx(ActionGroup, Object.assign({}, actionGroupProps, { children: _jsx(Button, Object.assign({ onClick: () => onSubmit(selectedResponse, value) }, submitButtonProps, { children: submitWord })) }))] })) }))] })) }));
33
+ } }, cardHeaderProps, { children: _jsx(HeadingLevel, Object.assign({ className: "pf-chatbot__feedback-card-title" }, headingLevelProps, { children: title })) })), _jsx(CardBody, Object.assign({}, cardBodyProps, { children: _jsxs(Form, Object.assign({ className: `pf-chatbot__feedback-card-form ${isCompact ? 'pf-m-compact' : ''}` }, formProps, { children: [quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, onSelect: (id) => setSelectedResponse(id), isCompact: isCompact })), hasTextArea && (_jsx(_Fragment, { children: _jsx(TextArea, Object.assign({ value: value, onChange: (_event, value) => {
34
+ setValue(value);
35
+ onTextAreaChange && onTextAreaChange(_event, value);
36
+ }, placeholder: textAreaPlaceholder, "aria-label": textAreaAriaLabel, resizeOrientation: "vertical" }, textAreaProps)) })), privacyStatement && _jsx("div", { className: "pf-chatbot__feedback-card-privacy", children: privacyStatement }), children, _jsx(ActionGroup, Object.assign({}, actionGroupProps, { children: _jsx(Button, Object.assign({ onClick: () => onSubmit(selectedResponse, value), size: isCompact ? 'sm' : undefined }, submitButtonProps, { children: submitWord })) }))] })) }))] })) }));
37
37
  };
38
38
  export default UserFeedback;
@@ -28,6 +28,7 @@ export const MessageBarBase = (_a) => {
28
28
  const [isListeningMessage, setIsListeningMessage] = useState(false);
29
29
  const [hasSentMessage, setHasSentMessage] = useState(false);
30
30
  const [isComposing, setIsComposing] = useState(false);
31
+ const [isMultiline, setIsMultiline] = useState(false);
31
32
  const inputRef = useRef(null);
32
33
  const textareaRef = (_b = innerRef) !== null && _b !== void 0 ? _b : inputRef;
33
34
  const attachButtonRef = useRef(null);
@@ -68,6 +69,15 @@ export const MessageBarBase = (_a) => {
68
69
  const lines = field.scrollHeight / lineHeight;
69
70
  return lines > 2;
70
71
  };
72
+ const checkIfMultiline = useCallback((field) => {
73
+ const parent = field.parentElement;
74
+ const grandparent = parent === null || parent === void 0 ? void 0 : parent.parentElement;
75
+ if (grandparent) {
76
+ const containerHeight = grandparent.offsetHeight;
77
+ const threshold = isCompact ? 56 : 70;
78
+ setIsMultiline(containerHeight > threshold);
79
+ }
80
+ }, [isCompact]);
71
81
  const setAutoWidth = useCallback((field) => {
72
82
  const parent = field.parentElement;
73
83
  if (parent) {
@@ -120,13 +130,15 @@ export const MessageBarBase = (_a) => {
120
130
  if (field) {
121
131
  if (field.value === '') {
122
132
  setInitialLineHeight(field);
133
+ setIsMultiline(false);
123
134
  }
124
135
  else {
125
136
  setAutoHeight(field);
126
137
  setAutoWidth(field);
138
+ checkIfMultiline(field);
127
139
  }
128
140
  }
129
- }, [displayMode, message, setAutoWidth]);
141
+ }, [displayMode, message, setAutoWidth, checkIfMultiline]);
130
142
  useEffect(() => {
131
143
  const field = textareaRef.current;
132
144
  if (field) {
@@ -139,13 +151,15 @@ export const MessageBarBase = (_a) => {
139
151
  if (textareaRef.current) {
140
152
  if (event.target.value === '') {
141
153
  setInitialLineHeight(textareaRef.current);
154
+ setIsMultiline(false);
142
155
  }
143
156
  else {
144
157
  setAutoHeight(textareaRef.current);
158
+ checkIfMultiline(textareaRef.current);
145
159
  }
146
160
  }
147
161
  setMessage(event.target.value);
148
- }, [onChange]);
162
+ }, [onChange, checkIfMultiline]);
149
163
  // Handle sending message
150
164
  const handleSend = useCallback((newMessage) => {
151
165
  onSendMessage(newMessage);
@@ -196,14 +210,14 @@ export const MessageBarBase = (_a) => {
196
210
  };
197
211
  const messageBarContents = (_jsxs(_Fragment, { children: [_jsx("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}`, children: _jsx(TextArea, Object.assign({ className: "pf-chatbot__message-textarea", value: message, onChange: handleChange, "aria-label": isListeningMessage ? listeningText : placeholder, placeholder: isListeningMessage ? listeningText : placeholder, ref: textareaRef, onKeyDown: handleKeyDown, onCompositionStart: handleCompositionStart, onCompositionEnd: handleCompositionEnd }, props)) }), _jsx("div", { className: "pf-chatbot__message-bar-actions", children: renderButtons() })] }));
198
212
  if (attachMenuProps) {
199
- return (_jsx(AttachMenu, Object.assign({ toggle: (toggleRef) => (_jsx("div", { ref: toggleRef, className: `pf-chatbot__message-bar ${className !== null && className !== void 0 ? className : ''}`, children: messageBarContents })), filteredItems: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.attachMenuItems }, (attachMenuProps && { isOpen: attachMenuProps.isAttachMenuOpen }), { onOpenChange: (isAttachMenuOpen) => {
213
+ return (_jsx(AttachMenu, Object.assign({ toggle: (toggleRef) => (_jsx("div", { ref: toggleRef, className: css('pf-chatbot__message-bar', isMultiline && 'pf-m-multiline', className), children: messageBarContents })), filteredItems: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.attachMenuItems }, (attachMenuProps && { isOpen: attachMenuProps.isAttachMenuOpen }), { onOpenChange: (isAttachMenuOpen) => {
200
214
  var _a;
201
215
  (_a = attachButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
202
216
  attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.setIsAttachMenuOpen(isAttachMenuOpen);
203
217
  (attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuOpenChange) && (attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuOpenChange(isAttachMenuOpen));
204
218
  }, onOpenChangeKeys: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuOnOpenChangeKeys, onSelect: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuSelect }, (attachMenuProps && { handleTextInputChange: attachMenuProps.onAttachMenuInputChange }), { popperProps: { direction: 'up', distance: 8 }, searchInputPlaceholder: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.attachMenuInputPlaceholder }, attachMenuProps)));
205
219
  }
206
- return (_jsx("div", { className: css('pf-chatbot__message-bar', isPrimary && 'pf-m-primary', hasAiIndicator && 'pf-v6-m-ai-indicator', isThinking && 'pf-v6-m-thinking', className), children: messageBarContents }));
220
+ return (_jsx("div", { className: css('pf-chatbot__message-bar', isPrimary && 'pf-m-primary', hasAiIndicator && 'pf-v6-m-ai-indicator', isThinking && 'pf-v6-m-thinking', isMultiline && 'pf-m-multiline', className), children: messageBarContents }));
207
221
  };
208
222
  const MessageBar = forwardRef((props, ref) => (_jsx(MessageBarBase, Object.assign({ innerRef: ref }, props))));
209
223
  export { MessageBar };
@@ -1,5 +1,6 @@
1
1
  import { type FunctionComponent } from 'react';
2
2
  import { ActionListProps, ActionListGroupProps, ActionListItemProps, ButtonProps, CardProps, CardBodyProps, CardFooterProps, ExpandableSectionProps, SpinnerProps } from '@patternfly/react-core';
3
+ import type { MarkdownContentProps } from '../MarkdownContent';
3
4
  export interface ToolCallProps {
4
5
  /** Title text for the tool call. */
5
6
  titleText: string;
@@ -41,6 +42,14 @@ export interface ToolCallProps {
41
42
  cardFooterProps?: CardFooterProps;
42
43
  /** Additional props for the expandable section when expandableContent is passed. */
43
44
  expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
45
+ /** Whether to enable markdown rendering for titleText. When true, titleText will be parsed as markdown. */
46
+ isTitleMarkdown?: boolean;
47
+ /** Whether to enable markdown rendering for expandableContent. When true and expandableContent is a string, it will be parsed as markdown. */
48
+ isExpandableContentMarkdown?: boolean;
49
+ /** Props passed to MarkdownContent component when markdown is enabled */
50
+ markdownContentProps?: Omit<MarkdownContentProps, 'content'>;
51
+ /** Whether to retain styles in the MarkdownContent component. Defaults to false. */
52
+ shouldRetainStyles?: boolean;
44
53
  }
45
54
  export declare const ToolCall: FunctionComponent<ToolCallProps>;
46
55
  export default ToolCall;
@@ -1,15 +1,28 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { ActionList, ActionListGroup, ActionListItem, Button, Card, CardBody, CardFooter, ExpandableSection, Spinner } from '@patternfly/react-core';
4
- export const ToolCall = ({ titleText, loadingText, isLoading, expandableContent, isDefaultExpanded = false, runButtonText = 'Run tool', runButtonProps, runActionItemProps, cancelButtonText = 'Cancel', cancelButtonProps, cancelActionItemProps, actions, actionListProps, actionListGroupProps, actionListItemProps, cardProps, cardBodyProps, cardFooterProps, expandableSectionProps, spinnerProps }) => {
4
+ import MarkdownContent from '../MarkdownContent';
5
+ export const ToolCall = ({ titleText, loadingText, isLoading, expandableContent, isDefaultExpanded = false, runButtonText = 'Run tool', runButtonProps, runActionItemProps, cancelButtonText = 'Cancel', cancelButtonProps, cancelActionItemProps, actions, actionListProps, actionListGroupProps, actionListItemProps, cardProps, cardBodyProps, cardFooterProps, expandableSectionProps, spinnerProps, isTitleMarkdown, isExpandableContentMarkdown, markdownContentProps, shouldRetainStyles = false }) => {
5
6
  const [isExpanded, setIsExpanded] = useState(isDefaultExpanded);
6
7
  const onToggle = (_event, isExpanded) => {
7
8
  setIsExpanded(isExpanded);
8
9
  };
9
- const titleContent = (_jsx("span", { className: `pf-chatbot__tool-call-title-content`, children: isLoading ? (_jsxs(_Fragment, { children: [_jsx(Spinner, Object.assign({ diameter: "1em" }, spinnerProps)), ' ', _jsx("span", { className: "pf-chatbot__tool-call-title-text", children: loadingText })] })) : (_jsx("span", { className: "pf-chatbot__tool-call-title-text", children: titleText })) }));
10
+ const renderTitle = () => {
11
+ if (isTitleMarkdown) {
12
+ return _jsx(MarkdownContent, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: titleText }, markdownContentProps));
13
+ }
14
+ return titleText;
15
+ };
16
+ const titleContent = (_jsx("span", { className: `pf-chatbot__tool-call-title-content`, children: isLoading ? (_jsxs(_Fragment, { children: [_jsx(Spinner, Object.assign({ diameter: "1em" }, spinnerProps)), ' ', _jsx("span", { className: "pf-chatbot__tool-call-title-text", children: loadingText })] })) : (_jsx("span", { className: "pf-chatbot__tool-call-title-text", children: renderTitle() })) }));
17
+ const renderExpandableContent = () => {
18
+ if (isExpandableContentMarkdown && typeof expandableContent === 'string') {
19
+ return (_jsx(MarkdownContent, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: expandableContent }, markdownContentProps)));
20
+ }
21
+ return expandableContent;
22
+ };
10
23
  const defaultActions = (_jsxs(_Fragment, { children: [_jsx(ActionListItem, Object.assign({}, actionListItemProps, cancelActionItemProps, { children: _jsx(Button, Object.assign({ variant: "link" }, cancelButtonProps, { children: cancelButtonText })) })), _jsx(ActionListItem, Object.assign({}, actionListItemProps, runActionItemProps, { children: _jsx(Button, Object.assign({ variant: "secondary" }, runButtonProps, { children: runButtonText })) }))] }));
11
24
  const customActions = actions &&
12
25
  actions.map((action, index) => (_jsx(ActionListItem, Object.assign({}, actionListItemProps, { children: action }), index)));
13
- return (_jsxs(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-call" }, cardProps, { children: [_jsx(CardBody, Object.assign({ className: "pf-chatbot__tool-call-title" }, cardBodyProps, { children: expandableContent && !isLoading ? (_jsx(ExpandableSection, Object.assign({ className: "pf-chatbot__tool-call-expandable-section", toggleContent: titleContent, onToggle: onToggle, isExpanded: isExpanded, isIndented: true }, expandableSectionProps, { children: expandableContent }))) : (titleContent) })), !isLoading && (_jsx(CardFooter, Object.assign({}, cardFooterProps, { children: _jsx(ActionList, Object.assign({ className: "pf-chatbot__tool-call-action-list" }, actionListProps, { children: _jsx(ActionListGroup, Object.assign({}, actionListGroupProps, { children: customActions || defaultActions })) })) })))] })));
26
+ return (_jsxs(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-call" }, cardProps, { children: [_jsx(CardBody, Object.assign({ className: "pf-chatbot__tool-call-title" }, cardBodyProps, { children: expandableContent && !isLoading ? (_jsx(ExpandableSection, Object.assign({ className: "pf-chatbot__tool-call-expandable-section", toggleContent: titleContent, onToggle: onToggle, isExpanded: isExpanded, isIndented: true }, expandableSectionProps, { children: renderExpandableContent() }))) : (titleContent) })), !isLoading && (_jsx(CardFooter, Object.assign({}, cardFooterProps, { children: _jsx(ActionList, Object.assign({ className: "pf-chatbot__tool-call-action-list" }, actionListProps, { children: _jsx(ActionListGroup, Object.assign({}, actionListGroupProps, { children: customActions || defaultActions })) })) })))] })));
14
27
  };
15
28
  export default ToolCall;
@@ -162,4 +162,35 @@ describe('ToolCall', () => {
162
162
  expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
163
163
  expect(screen.queryByText('Expandable Content')).not.toBeVisible();
164
164
  }));
165
+ it('should render titleText as markdown when isTitleMarkdown is true', () => {
166
+ const titleText = '**Bold title**';
167
+ const { container } = render(_jsx(ToolCall, { titleText: titleText, isTitleMarkdown: true }));
168
+ expect(container.querySelector('strong')).toBeTruthy();
169
+ expect(screen.getByText('Bold title')).toBeTruthy();
170
+ });
171
+ it('should not render titleText as markdown when isTitleMarkdown is false', () => {
172
+ const titleText = '**Bold title**';
173
+ render(_jsx(ToolCall, { titleText: titleText }));
174
+ expect(screen.getByText('**Bold title**')).toBeTruthy();
175
+ });
176
+ it('should render expandableContent as markdown when isExpandableContentMarkdown is true', () => __awaiter(void 0, void 0, void 0, function* () {
177
+ const user = userEvent.setup();
178
+ const expandableContent = '**Bold expandable content**';
179
+ const { container } = render(_jsx(ToolCall, Object.assign({}, defaultProps, { expandableContent: expandableContent, isExpandableContentMarkdown: true })));
180
+ yield user.click(screen.getByRole('button', { name: defaultProps.titleText }));
181
+ expect(container.querySelector('strong')).toBeTruthy();
182
+ expect(screen.getByText('Bold expandable content')).toBeTruthy();
183
+ }));
184
+ it('should not render expandableContent as markdown when isExpandableContentMarkdown is false', () => __awaiter(void 0, void 0, void 0, function* () {
185
+ const user = userEvent.setup();
186
+ const expandableContent = '**Bold expandable content**';
187
+ render(_jsx(ToolCall, Object.assign({}, defaultProps, { expandableContent: expandableContent })));
188
+ yield user.click(screen.getByRole('button', { name: defaultProps.titleText }));
189
+ expect(screen.getByText('**Bold expandable content**')).toBeTruthy();
190
+ }));
191
+ it('should pass markdownContentProps to MarkdownContent component', () => {
192
+ const titleText = '**Bold title**';
193
+ const { container } = render(_jsx(ToolCall, { titleText: titleText, isTitleMarkdown: true, markdownContentProps: { isPrimary: true } }));
194
+ expect(container.querySelector('.pf-m-primary')).toBeTruthy();
195
+ });
165
196
  });
@@ -1,5 +1,6 @@
1
1
  import { CardBodyProps, CardProps, CardTitleProps, DividerProps, ExpandableSectionProps } from '@patternfly/react-core';
2
2
  import { type FunctionComponent } from 'react';
3
+ import type { MarkdownContentProps } from '../MarkdownContent';
3
4
  export interface ToolResponseProps {
4
5
  /** Toggle content shown for expandable section */
5
6
  toggleContent: React.ReactNode;
@@ -27,6 +28,20 @@ export interface ToolResponseProps {
27
28
  toolResponseCardDividerProps?: DividerProps;
28
29
  /** Additional props passed to tool response card title */
29
30
  toolResponseCardTitleProps?: CardTitleProps;
31
+ /** Whether to enable markdown rendering for toggleContent. When true and toggleContent is a string, it will be parsed as markdown. */
32
+ isToggleContentMarkdown?: boolean;
33
+ /** Whether to enable markdown rendering for subheading. When true, subheading will be parsed as markdown. */
34
+ isSubheadingMarkdown?: boolean;
35
+ /** Whether to enable markdown rendering for body. When true and body is a string, it will be parsed as markdown. */
36
+ isBodyMarkdown?: boolean;
37
+ /** Whether to enable markdown rendering for cardBody. When true and cardBody is a string, it will be parsed as markdown. */
38
+ isCardBodyMarkdown?: boolean;
39
+ /** Whether to enable markdown rendering for cardTitle. When true and cardTitle is a string, it will be parsed as markdown. */
40
+ isCardTitleMarkdown?: boolean;
41
+ /** Props passed to MarkdownContent component when markdown is enabled */
42
+ markdownContentProps?: Omit<MarkdownContentProps, 'content'>;
43
+ /** Whether to retain styles in the MarkdownContent component. Defaults to false. */
44
+ shouldRetainStyles?: boolean;
30
45
  }
31
46
  export declare const ToolResponse: FunctionComponent<ToolResponseProps>;
32
47
  export default ToolResponse;
@@ -4,11 +4,54 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  // ============================================================================
5
5
  import { Card, CardBody, CardTitle, Divider, ExpandableSection } from '@patternfly/react-core';
6
6
  import { useState } from 'react';
7
- export const ToolResponse = ({ body, cardProps, expandableSectionProps, subheading, cardBody, cardTitle, cardBodyProps, toggleContent, isDefaultExpanded = true, toolResponseCardBodyProps, toolResponseCardDividerProps, toolResponseCardProps, toolResponseCardTitleProps }) => {
7
+ import MarkdownContent from '../MarkdownContent';
8
+ export const ToolResponse = ({ body, cardProps, expandableSectionProps, subheading, cardBody, cardTitle, cardBodyProps, toggleContent, isDefaultExpanded = true, toolResponseCardBodyProps, toolResponseCardDividerProps, toolResponseCardProps, toolResponseCardTitleProps, isToggleContentMarkdown, isSubheadingMarkdown, isBodyMarkdown, isCardBodyMarkdown, isCardTitleMarkdown, markdownContentProps, shouldRetainStyles = false }) => {
8
9
  const [isExpanded, setIsExpanded] = useState(isDefaultExpanded);
9
10
  const onToggle = (_event, isExpanded) => {
10
11
  setIsExpanded(isExpanded);
11
12
  };
12
- return (_jsx(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-response" }, cardProps, { children: _jsx(CardBody, Object.assign({}, cardBodyProps, { children: _jsx(ExpandableSection, Object.assign({ toggleContent: toggleContent, onToggle: onToggle, isExpanded: isExpanded, isIndented: true, className: "pf-chatbot__tool-response-expandable-section" }, expandableSectionProps, { children: _jsxs("div", { className: "pf-chatbot__tool-response-section", children: [subheading && (_jsx("div", { className: "pf-chatbot__tool-response-subheading", children: _jsx("span", { children: subheading }) })), body && _jsx("div", { className: "pf-chatbot__tool-response-body", children: body }), (cardTitle || cardBody) && (_jsxs(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-response-card" }, toolResponseCardProps, { children: [cardTitle && _jsx(CardTitle, Object.assign({}, toolResponseCardTitleProps, { children: cardTitle })), cardTitle && cardBody && _jsx(Divider, Object.assign({}, toolResponseCardDividerProps)), cardBody && _jsx(CardBody, Object.assign({}, toolResponseCardBodyProps, { children: cardBody }))] })))] }) })) })) })));
13
+ const renderToggleContent = () => {
14
+ if (isToggleContentMarkdown && typeof toggleContent === 'string') {
15
+ return (_jsx(MarkdownContent, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: toggleContent }, markdownContentProps)));
16
+ }
17
+ return toggleContent;
18
+ };
19
+ const renderSubheading = () => {
20
+ if (!subheading) {
21
+ return null;
22
+ }
23
+ if (isSubheadingMarkdown) {
24
+ return _jsx(MarkdownContent, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: subheading }, markdownContentProps));
25
+ }
26
+ return subheading;
27
+ };
28
+ const renderBody = () => {
29
+ if (!body) {
30
+ return null;
31
+ }
32
+ if (isBodyMarkdown && typeof body === 'string') {
33
+ return _jsx(MarkdownContent, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: body }, markdownContentProps));
34
+ }
35
+ return body;
36
+ };
37
+ const renderCardTitle = () => {
38
+ if (!cardTitle) {
39
+ return null;
40
+ }
41
+ if (isCardTitleMarkdown && typeof cardTitle === 'string') {
42
+ return _jsx(MarkdownContent, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: cardTitle }, markdownContentProps));
43
+ }
44
+ return cardTitle;
45
+ };
46
+ const renderCardBody = () => {
47
+ if (!cardBody) {
48
+ return null;
49
+ }
50
+ if (isCardBodyMarkdown && typeof cardBody === 'string') {
51
+ return _jsx(MarkdownContent, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: cardBody }, markdownContentProps));
52
+ }
53
+ return cardBody;
54
+ };
55
+ return (_jsx(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-response" }, cardProps, { children: _jsx(CardBody, Object.assign({}, cardBodyProps, { children: _jsx(ExpandableSection, Object.assign({ toggleContent: renderToggleContent(), onToggle: onToggle, isExpanded: isExpanded, isIndented: true, className: "pf-chatbot__tool-response-expandable-section" }, expandableSectionProps, { children: _jsxs("div", { className: "pf-chatbot__tool-response-section", children: [subheading && (_jsx("div", { className: "pf-chatbot__tool-response-subheading", children: _jsx("span", { children: renderSubheading() }) })), body && _jsx("div", { className: "pf-chatbot__tool-response-body", children: renderBody() }), (cardTitle || cardBody) && (_jsxs(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-response-card" }, toolResponseCardProps, { children: [cardTitle && _jsx(CardTitle, Object.assign({}, toolResponseCardTitleProps, { children: renderCardTitle() })), cardTitle && cardBody && _jsx(Divider, Object.assign({}, toolResponseCardDividerProps)), cardBody && _jsx(CardBody, Object.assign({}, toolResponseCardBodyProps, { children: renderCardBody() }))] })))] }) })) })) })));
13
56
  };
14
57
  export default ToolResponse;