@patternfly/chatbot 6.5.0-prerelease.21 → 6.5.0-prerelease.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/DeepThinking/DeepThinking.d.ts +13 -0
- package/dist/cjs/DeepThinking/DeepThinking.js +31 -3
- package/dist/cjs/DeepThinking/DeepThinking.test.js +80 -0
- package/dist/cjs/MarkdownContent/MarkdownContent.d.ts +39 -0
- package/dist/cjs/MarkdownContent/MarkdownContent.js +181 -0
- package/dist/cjs/MarkdownContent/MarkdownContent.test.d.ts +1 -0
- package/dist/cjs/MarkdownContent/MarkdownContent.test.js +192 -0
- package/dist/cjs/MarkdownContent/index.d.ts +2 -0
- package/dist/cjs/MarkdownContent/index.js +23 -0
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
- package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +5 -1
- package/dist/cjs/Message/LinkMessage/LinkMessage.js +4 -3
- package/dist/cjs/Message/ListMessage/OrderedListMessage.d.ts +9 -1
- package/dist/cjs/Message/ListMessage/OrderedListMessage.js +2 -1
- package/dist/cjs/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
- package/dist/cjs/Message/ListMessage/UnorderedListMessage.js +2 -1
- package/dist/cjs/Message/Message.js +2 -155
- package/dist/cjs/Message/TableMessage/TableMessage.d.ts +6 -1
- package/dist/cjs/Message/TableMessage/TableMessage.js +3 -2
- package/dist/cjs/Message/TextMessage/TextMessage.d.ts +8 -1
- package/dist/cjs/Message/TextMessage/TextMessage.js +3 -2
- package/dist/cjs/ToolCall/ToolCall.d.ts +11 -0
- package/dist/cjs/ToolCall/ToolCall.js +24 -3
- package/dist/cjs/ToolCall/ToolCall.test.js +57 -0
- package/dist/cjs/ToolResponse/ToolResponse.d.ts +17 -0
- package/dist/cjs/ToolResponse/ToolResponse.js +49 -3
- package/dist/cjs/ToolResponse/ToolResponse.test.js +100 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/css/main.css +48 -0
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/MarkdownContent/package.json +1 -0
- package/dist/esm/DeepThinking/DeepThinking.d.ts +13 -0
- package/dist/esm/DeepThinking/DeepThinking.js +28 -3
- package/dist/esm/DeepThinking/DeepThinking.test.js +80 -0
- package/dist/esm/MarkdownContent/MarkdownContent.d.ts +39 -0
- package/dist/esm/MarkdownContent/MarkdownContent.js +174 -0
- package/dist/esm/MarkdownContent/MarkdownContent.test.d.ts +1 -0
- package/dist/esm/MarkdownContent/MarkdownContent.test.js +187 -0
- package/dist/esm/MarkdownContent/index.d.ts +2 -0
- package/dist/esm/MarkdownContent/index.js +2 -0
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
- package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +5 -1
- package/dist/esm/Message/LinkMessage/LinkMessage.js +4 -3
- package/dist/esm/Message/ListMessage/OrderedListMessage.d.ts +9 -1
- package/dist/esm/Message/ListMessage/OrderedListMessage.js +2 -1
- package/dist/esm/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
- package/dist/esm/Message/ListMessage/UnorderedListMessage.js +2 -1
- package/dist/esm/Message/Message.js +3 -156
- package/dist/esm/Message/TableMessage/TableMessage.d.ts +6 -1
- package/dist/esm/Message/TableMessage/TableMessage.js +3 -2
- package/dist/esm/Message/TextMessage/TextMessage.d.ts +8 -1
- package/dist/esm/Message/TextMessage/TextMessage.js +3 -2
- package/dist/esm/ToolCall/ToolCall.d.ts +11 -0
- package/dist/esm/ToolCall/ToolCall.js +21 -3
- package/dist/esm/ToolCall/ToolCall.test.js +57 -0
- package/dist/esm/ToolResponse/ToolResponse.d.ts +17 -0
- package/dist/esm/ToolResponse/ToolResponse.js +46 -3
- package/dist/esm/ToolResponse/ToolResponse.test.js +100 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +25 -11
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownDeepThinking.tsx +26 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolCall.tsx +29 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolResponse.tsx +200 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolCall.tsx +14 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +222 -105
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +32 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +15 -1
- package/src/DeepThinking/DeepThinking.test.tsx +109 -0
- package/src/DeepThinking/DeepThinking.tsx +54 -5
- package/src/MarkdownContent/MarkdownContent.test.tsx +207 -0
- package/src/MarkdownContent/MarkdownContent.tsx +264 -0
- package/src/MarkdownContent/index.ts +2 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +4 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +5 -1
- package/src/Message/LinkMessage/LinkMessage.scss +5 -0
- package/src/Message/LinkMessage/LinkMessage.tsx +24 -2
- package/src/Message/ListMessage/ListMessage.scss +8 -0
- package/src/Message/ListMessage/OrderedListMessage.tsx +16 -2
- package/src/Message/ListMessage/UnorderedListMessage.tsx +12 -2
- package/src/Message/Message.tsx +21 -181
- package/src/Message/TableMessage/TableMessage.scss +11 -0
- package/src/Message/TableMessage/TableMessage.tsx +18 -2
- package/src/Message/TextMessage/TextMessage.scss +8 -0
- package/src/Message/TextMessage/TextMessage.tsx +29 -2
- package/src/ToolCall/ToolCall.test.tsx +91 -0
- package/src/ToolCall/ToolCall.tsx +49 -4
- package/src/ToolResponse/ToolResponse.scss +10 -0
- package/src/ToolResponse/ToolResponse.test.tsx +119 -0
- package/src/ToolResponse/ToolResponse.tsx +82 -7
- package/src/index.ts +3 -0
- package/src/main.scss +1 -0
package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx
CHANGED
|
@@ -23,113 +23,230 @@ export const MessageWithToolResponseExample: FunctionComponent = () => {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<Flex gap={{ default: 'gapXs' }}>
|
|
41
|
-
<FlexItem>
|
|
42
|
-
<WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} />
|
|
43
|
-
</FlexItem>
|
|
44
|
-
<FlexItem>toolName</FlexItem>
|
|
45
|
-
</Flex>
|
|
46
|
-
</FlexItem>
|
|
47
|
-
<FlexItem>
|
|
48
|
-
<Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}>
|
|
49
|
-
<FlexItem>Execution time:</FlexItem>
|
|
50
|
-
<FlexItem>0.12 seconds</FlexItem>
|
|
51
|
-
</Flex>
|
|
52
|
-
</FlexItem>
|
|
53
|
-
</Flex>
|
|
54
|
-
</FlexItem>
|
|
55
|
-
<FlexItem>
|
|
56
|
-
<Button
|
|
57
|
-
variant="plain"
|
|
58
|
-
aria-label="Copy tool response to clipboard"
|
|
59
|
-
icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />}
|
|
60
|
-
></Button>
|
|
61
|
-
</FlexItem>
|
|
62
|
-
</Flex>
|
|
63
|
-
),
|
|
64
|
-
cardBody: (
|
|
65
|
-
<>
|
|
66
|
-
<DescriptionList
|
|
67
|
-
style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any}
|
|
68
|
-
aria-label="Tool response"
|
|
26
|
+
<>
|
|
27
|
+
<Message
|
|
28
|
+
name="Bot"
|
|
29
|
+
role="bot"
|
|
30
|
+
avatar={patternflyAvatar}
|
|
31
|
+
content="This message has a body description that's within the recommended limit of 2 lines:"
|
|
32
|
+
toolResponse={{
|
|
33
|
+
toggleContent: 'Tool response: toolName',
|
|
34
|
+
subheading: 'Thought for 3 seconds',
|
|
35
|
+
body: "Here's the summary for your toolName response:",
|
|
36
|
+
cardTitle: (
|
|
37
|
+
<Flex
|
|
38
|
+
alignItems={{ default: 'alignItemsCenter' }}
|
|
39
|
+
justifyContent={{ default: 'justifyContentSpaceBetween' }}
|
|
69
40
|
>
|
|
70
|
-
<
|
|
71
|
-
|
|
41
|
+
<FlexItem>
|
|
42
|
+
<Flex direction={{ default: 'column' }} gap={{ default: 'gapXs' }}>
|
|
43
|
+
<FlexItem grow={{ default: 'grow' }}>
|
|
44
|
+
<Flex gap={{ default: 'gapXs' }}>
|
|
45
|
+
<FlexItem>
|
|
46
|
+
<WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} />
|
|
47
|
+
</FlexItem>
|
|
48
|
+
<FlexItem>toolName</FlexItem>
|
|
49
|
+
</Flex>
|
|
50
|
+
</FlexItem>
|
|
51
|
+
<FlexItem>
|
|
52
|
+
<Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}>
|
|
53
|
+
<FlexItem>Execution time:</FlexItem>
|
|
54
|
+
<FlexItem>0.12 seconds</FlexItem>
|
|
55
|
+
</Flex>
|
|
56
|
+
</FlexItem>
|
|
57
|
+
</Flex>
|
|
58
|
+
</FlexItem>
|
|
59
|
+
<FlexItem>
|
|
60
|
+
<Button
|
|
61
|
+
variant="plain"
|
|
62
|
+
aria-label="Copy tool response to clipboard"
|
|
63
|
+
icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />}
|
|
64
|
+
></Button>
|
|
65
|
+
</FlexItem>
|
|
66
|
+
</Flex>
|
|
67
|
+
),
|
|
68
|
+
cardBody: (
|
|
69
|
+
<>
|
|
70
|
+
<DescriptionList
|
|
71
|
+
style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any}
|
|
72
|
+
aria-label="Tool response"
|
|
72
73
|
>
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
74
|
+
<DescriptionListGroup
|
|
75
|
+
style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
|
|
76
|
+
>
|
|
77
|
+
<DescriptionListTerm>Parameters</DescriptionListTerm>
|
|
78
|
+
<DescriptionListDescription>
|
|
79
|
+
<Flex direction={{ default: 'column' }}>
|
|
80
|
+
<FlexItem>Optional description text for parameters.</FlexItem>
|
|
81
|
+
<FlexItem>
|
|
82
|
+
<Flex gap={{ default: 'gapSm' }}>
|
|
83
|
+
<FlexItem>
|
|
84
|
+
<Label variant="outline" color="blue">
|
|
85
|
+
type
|
|
86
|
+
</Label>
|
|
87
|
+
</FlexItem>
|
|
88
|
+
<FlexItem>
|
|
89
|
+
<Label variant="outline" color="blue">
|
|
90
|
+
properties
|
|
91
|
+
</Label>
|
|
92
|
+
</FlexItem>
|
|
93
|
+
<FlexItem>
|
|
94
|
+
<Label variant="outline" color="blue">
|
|
95
|
+
label
|
|
96
|
+
</Label>
|
|
97
|
+
</FlexItem>
|
|
98
|
+
<FlexItem>
|
|
99
|
+
<Label variant="outline" color="blue">
|
|
100
|
+
label
|
|
101
|
+
</Label>
|
|
102
|
+
</FlexItem>
|
|
103
|
+
</Flex>
|
|
104
|
+
</FlexItem>
|
|
105
|
+
</Flex>
|
|
106
|
+
</DescriptionListDescription>
|
|
107
|
+
</DescriptionListGroup>
|
|
108
|
+
<DescriptionListGroup
|
|
109
|
+
style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
|
|
110
|
+
>
|
|
111
|
+
<DescriptionListTerm>Response</DescriptionListTerm>
|
|
112
|
+
<DescriptionListDescription>
|
|
113
|
+
<ExpandableSection
|
|
114
|
+
variant={ExpandableSectionVariant.truncate}
|
|
115
|
+
toggleTextExpanded="show less of response"
|
|
116
|
+
toggleTextCollapsed="show more of response"
|
|
117
|
+
onToggle={onToggle}
|
|
118
|
+
isExpanded={isExpanded}
|
|
119
|
+
style={
|
|
120
|
+
{
|
|
121
|
+
'--pf-v6-c-expandable-section__content--Opacity': '1',
|
|
122
|
+
'--pf-v6-c-expandable-section__content--PaddingInlineStart': 0,
|
|
123
|
+
'--pf-v6-c-expandable-section__content--TranslateY': 0,
|
|
124
|
+
'--pf-v6-c-expandable-section--m-expand-top__content--TranslateY': 0
|
|
125
|
+
} as any
|
|
126
|
+
}
|
|
127
|
+
>
|
|
128
|
+
Descriptive text about the tool response, including completion status, details on the data that
|
|
129
|
+
was processed, or anything else relevant to the use case.
|
|
130
|
+
</ExpandableSection>
|
|
131
|
+
</DescriptionListDescription>
|
|
132
|
+
</DescriptionListGroup>
|
|
133
|
+
</DescriptionList>
|
|
134
|
+
</>
|
|
135
|
+
)
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
138
|
+
<Message
|
|
139
|
+
name="Bot"
|
|
140
|
+
role="bot"
|
|
141
|
+
avatar={patternflyAvatar}
|
|
142
|
+
content="This message has a tool response that is collapsed by default:"
|
|
143
|
+
toolResponse={{
|
|
144
|
+
isDefaultExpanded: false,
|
|
145
|
+
toggleContent: 'Tool response: toolName',
|
|
146
|
+
subheading: 'Thought for 3 seconds',
|
|
147
|
+
body: "Here's the summary for your toolName response:",
|
|
148
|
+
cardTitle: (
|
|
149
|
+
<Flex
|
|
150
|
+
alignItems={{ default: 'alignItemsCenter' }}
|
|
151
|
+
justifyContent={{ default: 'justifyContentSpaceBetween' }}
|
|
152
|
+
>
|
|
153
|
+
<FlexItem>
|
|
154
|
+
<Flex direction={{ default: 'column' }} gap={{ default: 'gapXs' }}>
|
|
155
|
+
<FlexItem grow={{ default: 'grow' }}>
|
|
156
|
+
<Flex gap={{ default: 'gapXs' }}>
|
|
157
|
+
<FlexItem>
|
|
158
|
+
<WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} />
|
|
159
|
+
</FlexItem>
|
|
160
|
+
<FlexItem>toolName</FlexItem>
|
|
161
|
+
</Flex>
|
|
162
|
+
</FlexItem>
|
|
163
|
+
<FlexItem>
|
|
164
|
+
<Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}>
|
|
165
|
+
<FlexItem>Execution time:</FlexItem>
|
|
166
|
+
<FlexItem>0.12 seconds</FlexItem>
|
|
167
|
+
</Flex>
|
|
168
|
+
</FlexItem>
|
|
169
|
+
</Flex>
|
|
170
|
+
</FlexItem>
|
|
171
|
+
<FlexItem>
|
|
172
|
+
<Button
|
|
173
|
+
variant="plain"
|
|
174
|
+
aria-label="Copy tool response to clipboard"
|
|
175
|
+
icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />}
|
|
176
|
+
></Button>
|
|
177
|
+
</FlexItem>
|
|
178
|
+
</Flex>
|
|
179
|
+
),
|
|
180
|
+
cardBody: (
|
|
181
|
+
<>
|
|
182
|
+
<DescriptionList
|
|
183
|
+
style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any}
|
|
184
|
+
aria-label="Tool response"
|
|
106
185
|
>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
186
|
+
<DescriptionListGroup
|
|
187
|
+
style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
|
|
188
|
+
>
|
|
189
|
+
<DescriptionListTerm>Parameters</DescriptionListTerm>
|
|
190
|
+
<DescriptionListDescription>
|
|
191
|
+
<Flex direction={{ default: 'column' }}>
|
|
192
|
+
<FlexItem>Optional description text for parameters.</FlexItem>
|
|
193
|
+
<FlexItem>
|
|
194
|
+
<Flex gap={{ default: 'gapSm' }}>
|
|
195
|
+
<FlexItem>
|
|
196
|
+
<Label variant="outline" color="blue">
|
|
197
|
+
type
|
|
198
|
+
</Label>
|
|
199
|
+
</FlexItem>
|
|
200
|
+
<FlexItem>
|
|
201
|
+
<Label variant="outline" color="blue">
|
|
202
|
+
properties
|
|
203
|
+
</Label>
|
|
204
|
+
</FlexItem>
|
|
205
|
+
<FlexItem>
|
|
206
|
+
<Label variant="outline" color="blue">
|
|
207
|
+
label
|
|
208
|
+
</Label>
|
|
209
|
+
</FlexItem>
|
|
210
|
+
<FlexItem>
|
|
211
|
+
<Label variant="outline" color="blue">
|
|
212
|
+
label
|
|
213
|
+
</Label>
|
|
214
|
+
</FlexItem>
|
|
215
|
+
</Flex>
|
|
216
|
+
</FlexItem>
|
|
217
|
+
</Flex>
|
|
218
|
+
</DescriptionListDescription>
|
|
219
|
+
</DescriptionListGroup>
|
|
220
|
+
<DescriptionListGroup
|
|
221
|
+
style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
|
|
222
|
+
>
|
|
223
|
+
<DescriptionListTerm>Response</DescriptionListTerm>
|
|
224
|
+
<DescriptionListDescription>
|
|
225
|
+
<ExpandableSection
|
|
226
|
+
variant={ExpandableSectionVariant.truncate}
|
|
227
|
+
toggleTextExpanded="show less of response"
|
|
228
|
+
toggleTextCollapsed="show more of response"
|
|
229
|
+
onToggle={onToggle}
|
|
230
|
+
isExpanded={isExpanded}
|
|
231
|
+
style={
|
|
232
|
+
{
|
|
233
|
+
'--pf-v6-c-expandable-section__content--Opacity': '1',
|
|
234
|
+
'--pf-v6-c-expandable-section__content--PaddingInlineStart': 0,
|
|
235
|
+
'--pf-v6-c-expandable-section__content--TranslateY': 0,
|
|
236
|
+
'--pf-v6-c-expandable-section--m-expand-top__content--TranslateY': 0
|
|
237
|
+
} as any
|
|
238
|
+
}
|
|
239
|
+
>
|
|
240
|
+
Descriptive text about the tool response, including completion status, details on the data that
|
|
241
|
+
was processed, or anything else relevant to the use case.
|
|
242
|
+
</ExpandableSection>
|
|
243
|
+
</DescriptionListDescription>
|
|
244
|
+
</DescriptionListGroup>
|
|
245
|
+
</DescriptionList>
|
|
246
|
+
</>
|
|
247
|
+
)
|
|
248
|
+
}}
|
|
249
|
+
/>
|
|
250
|
+
</>
|
|
134
251
|
);
|
|
135
252
|
};
|
|
@@ -349,3 +349,35 @@ An attachment dropzone allows users to upload files via drag and drop.
|
|
|
349
349
|
```js file="./FileDropZone.tsx"
|
|
350
350
|
|
|
351
351
|
```
|
|
352
|
+
|
|
353
|
+
## Examples with Markdown
|
|
354
|
+
|
|
355
|
+
The ChatBot supports Markdown formatting in several message components, allowing you to display rich, formatted content. This is particularly useful when you need to include code snippets, lists, emphasis, or other formatted text. The following examples demonstrate different ways you can use Markdown in a few of the ChatBot components, but this is not an exhaustive list of all Markdown customizations you can make.
|
|
356
|
+
|
|
357
|
+
To enable Markdown rendering, use the appropriate Markdown flag prop (such as `isBodyMarkdown`, `isSubheadingMarkdown`, or `isExpandableContentMarkdown`) depending on the component and content you're formatting.
|
|
358
|
+
|
|
359
|
+
**Important:** When using Markdown in these components, set `shouldRetainStyles: true` to retain the styling of the context the Markdown is used in. This ensures that Markdown content maintains the proper font sizes, colors, and other styling properties of its parent component. For example, Markdown passed into a toggle will retain the ChatBot toggle styling, while Markdown in a card body will maintain the appropriate card body styling. Without this prop, the Markdown may override the contextual styles and create inconsistencies with the rest of the ChatBot interface.
|
|
360
|
+
|
|
361
|
+
### Tool calls with Markdown
|
|
362
|
+
|
|
363
|
+
When displaying tool call information, you can use Markdown in the expandable content to provide formatted details about what the tool is processing. This is useful for showing structured data, code snippets, or formatted lists.
|
|
364
|
+
|
|
365
|
+
```ts file="./MessageWithMarkdownToolCall.tsx"
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Deep thinking with Markdown
|
|
370
|
+
|
|
371
|
+
Deep thinking content can include Markdown formatting in both the subheading and body to better communicate the LLM's reasoning process. This allows you to emphasize key points, structure thought processes with lists, or include other formatting.
|
|
372
|
+
|
|
373
|
+
```ts file="./MessageWithMarkdownDeepThinking.tsx"
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Tool responses with Markdown
|
|
378
|
+
|
|
379
|
+
Tool response cards support Markdown in multiple areas, including the toggle content, subheading, and body. Use `shouldRetainStyles: true` along with the appropriate Markdown flag props to ensure proper formatting and spacing.
|
|
380
|
+
|
|
381
|
+
```ts file="./MessageWithMarkdownToolResponse.tsx"
|
|
382
|
+
|
|
383
|
+
```
|
|
@@ -52,6 +52,20 @@ import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
|
|
|
52
52
|
import OpenDrawerRightIcon from '@patternfly/react-icons/dist/esm/icons/open-drawer-right-icon';
|
|
53
53
|
import OutlinedWindowRestoreIcon from '@patternfly/react-icons/dist/esm/icons/outlined-window-restore-icon';
|
|
54
54
|
import { BarsIcon } from '@patternfly/react-icons/dist/esm/icons/bars-icon';
|
|
55
|
+
import { CopyIcon } from '@patternfly/react-icons/dist/esm/icons/copy-icon';
|
|
56
|
+
import { WrenchIcon } from '@patternfly/react-icons/dist/esm/icons/wrench-icon';
|
|
57
|
+
import {
|
|
58
|
+
Button,
|
|
59
|
+
DescriptionList,
|
|
60
|
+
DescriptionListDescription,
|
|
61
|
+
DescriptionListGroup,
|
|
62
|
+
DescriptionListTerm,
|
|
63
|
+
ExpandableSection,
|
|
64
|
+
ExpandableSectionVariant,
|
|
65
|
+
Flex,
|
|
66
|
+
FlexItem,
|
|
67
|
+
Label
|
|
68
|
+
} from '@patternfly/react-core';
|
|
55
69
|
import PFHorizontalLogoColor from '../UI/PF-HorizontalLogo-Color.svg';
|
|
56
70
|
import PFHorizontalLogoReverse from '../UI/PF-HorizontalLogo-Reverse.svg';
|
|
57
71
|
import PFIconLogoColor from '../UI/PF-IconLogo-Color.svg';
|
|
@@ -59,7 +73,7 @@ import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg';
|
|
|
59
73
|
import userAvatar from '../Messages/user_avatar.svg';
|
|
60
74
|
import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
|
|
61
75
|
import { getTrackingProviders } from "@patternfly/chatbot/dist/dynamic/tracking";
|
|
62
|
-
import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent } from 'react';
|
|
76
|
+
import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent, MouseEvent as ReactMouseEvent } from 'react';
|
|
63
77
|
import saveAs from 'file-saver';
|
|
64
78
|
|
|
65
79
|
### Basic ChatBot
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
2
3
|
import '@testing-library/jest-dom';
|
|
3
4
|
import DeepThinking from './DeepThinking';
|
|
4
5
|
|
|
@@ -58,4 +59,112 @@ describe('DeepThinking', () => {
|
|
|
58
59
|
const subheadingContainer = container.querySelector('.pf-chatbot__tool-response-subheading');
|
|
59
60
|
expect(subheadingContainer).toBeFalsy();
|
|
60
61
|
});
|
|
62
|
+
|
|
63
|
+
it('should pass through cardBodyProps', () => {
|
|
64
|
+
render(
|
|
65
|
+
<DeepThinking {...defaultProps} body="Thinking content" cardBodyProps={{ className: 'custom-card-body-class' }} />
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const cardBody = screen.getByText('Thinking content').closest('.pf-v6-c-card__body');
|
|
69
|
+
expect(cardBody).toHaveClass('custom-card-body-class');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('Renders expanded by default', () => {
|
|
73
|
+
render(<DeepThinking {...defaultProps} body="Thinking content" />);
|
|
74
|
+
|
|
75
|
+
expect(screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'true');
|
|
76
|
+
expect(screen.getByText('Thinking content')).toBeVisible();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('Renders collapsed when isDefaultExpanded is false', () => {
|
|
80
|
+
render(<DeepThinking isDefaultExpanded={false} {...defaultProps} body="Thinking content" />);
|
|
81
|
+
|
|
82
|
+
expect(screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'false');
|
|
83
|
+
expect(screen.getByText('Thinking content')).not.toBeVisible();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('expandableSectionProps.isExpanded overrides isDefaultExpanded', () => {
|
|
87
|
+
render(
|
|
88
|
+
<DeepThinking
|
|
89
|
+
{...defaultProps}
|
|
90
|
+
isDefaultExpanded={false}
|
|
91
|
+
body="Thinking content"
|
|
92
|
+
expandableSectionProps={{ isExpanded: true }}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'true');
|
|
97
|
+
expect(screen.getByText('Thinking content')).toBeVisible();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('expandableSectionProps.onToggle overrides internal onToggle behavior', async () => {
|
|
101
|
+
const user = userEvent.setup();
|
|
102
|
+
const customOnToggle = jest.fn();
|
|
103
|
+
|
|
104
|
+
render(
|
|
105
|
+
<DeepThinking
|
|
106
|
+
{...defaultProps}
|
|
107
|
+
isDefaultExpanded={false}
|
|
108
|
+
body="Thinking content"
|
|
109
|
+
expandableSectionProps={{ onToggle: customOnToggle }}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const toggleButton = screen.getByRole('button', { name: defaultProps.toggleContent });
|
|
114
|
+
expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
|
|
115
|
+
|
|
116
|
+
await user.click(toggleButton);
|
|
117
|
+
|
|
118
|
+
expect(customOnToggle).toHaveBeenCalled();
|
|
119
|
+
expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
|
|
120
|
+
expect(screen.getByText('Thinking content')).not.toBeVisible();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should render toggleContent as markdown when isToggleContentMarkdown is true', () => {
|
|
124
|
+
const toggleContent = '**Bold thinking**';
|
|
125
|
+
const { container } = render(<DeepThinking toggleContent={toggleContent} isToggleContentMarkdown />);
|
|
126
|
+
expect(container.querySelector('strong')).toBeTruthy();
|
|
127
|
+
expect(screen.getByText('Bold thinking')).toBeTruthy();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should not render toggleContent as markdown when isToggleContentMarkdown is false', () => {
|
|
131
|
+
const toggleContent = '**Bold thinking**';
|
|
132
|
+
const { container } = render(<DeepThinking toggleContent={toggleContent} />);
|
|
133
|
+
expect(container.querySelector('strong')).toBeFalsy();
|
|
134
|
+
expect(screen.getByText('**Bold thinking**')).toBeTruthy();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should render subheading as markdown when isSubheadingMarkdown is true', () => {
|
|
138
|
+
const subheading = '**Bold subheading**';
|
|
139
|
+
const { container } = render(<DeepThinking {...defaultProps} subheading={subheading} isSubheadingMarkdown />);
|
|
140
|
+
expect(container.querySelector('strong')).toBeTruthy();
|
|
141
|
+
expect(screen.getByText('Bold subheading')).toBeTruthy();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should not render subheading as markdown when isSubheadingMarkdown is false', () => {
|
|
145
|
+
const subheading = '**Bold subheading**';
|
|
146
|
+
render(<DeepThinking {...defaultProps} subheading={subheading} />);
|
|
147
|
+
expect(screen.getByText('**Bold subheading**')).toBeTruthy();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should render body as markdown when isBodyMarkdown is true', () => {
|
|
151
|
+
const body = '**Bold body**';
|
|
152
|
+
const { container } = render(<DeepThinking {...defaultProps} body={body} isBodyMarkdown />);
|
|
153
|
+
expect(container.querySelector('strong')).toBeTruthy();
|
|
154
|
+
expect(screen.getByText('Bold body')).toBeTruthy();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should not render body as markdown when isBodyMarkdown is false', () => {
|
|
158
|
+
const body = '**Bold body**';
|
|
159
|
+
render(<DeepThinking {...defaultProps} body={body} />);
|
|
160
|
+
expect(screen.getByText('**Bold body**')).toBeTruthy();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should pass markdownContentProps to MarkdownContent component', () => {
|
|
164
|
+
const body = '**Bold body**';
|
|
165
|
+
const { container } = render(
|
|
166
|
+
<DeepThinking {...defaultProps} body={body} isBodyMarkdown markdownContentProps={{ isPrimary: true }} />
|
|
167
|
+
);
|
|
168
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
169
|
+
});
|
|
61
170
|
});
|
|
@@ -10,10 +10,14 @@ import {
|
|
|
10
10
|
ExpandableSectionProps
|
|
11
11
|
} from '@patternfly/react-core';
|
|
12
12
|
import { useState, type FunctionComponent } from 'react';
|
|
13
|
+
import MarkdownContent from '../MarkdownContent';
|
|
14
|
+
import type { MarkdownContentProps } from '../MarkdownContent';
|
|
13
15
|
|
|
14
16
|
export interface DeepThinkingProps {
|
|
15
17
|
/** Toggle content shown for expandable section */
|
|
16
18
|
toggleContent: React.ReactNode;
|
|
19
|
+
/** Flag indicating whether the expandable content is expanded by default. */
|
|
20
|
+
isDefaultExpanded?: boolean;
|
|
17
21
|
/** Additional props passed to expandable section */
|
|
18
22
|
expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
|
|
19
23
|
/** Subheading rendered inside expandable section */
|
|
@@ -24,6 +28,16 @@ export interface DeepThinkingProps {
|
|
|
24
28
|
cardProps?: CardProps;
|
|
25
29
|
/** Additional props passed to main card body */
|
|
26
30
|
cardBodyProps?: CardBodyProps;
|
|
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
|
+
/** Props passed to MarkdownContent component when markdown is enabled */
|
|
38
|
+
markdownContentProps?: Omit<MarkdownContentProps, 'content'>;
|
|
39
|
+
/** Whether to retain styles in the MarkdownContent component. Defaults to false. */
|
|
40
|
+
shouldRetainStyles?: boolean;
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
export const DeepThinking: FunctionComponent<DeepThinkingProps> = ({
|
|
@@ -32,19 +46,54 @@ export const DeepThinking: FunctionComponent<DeepThinkingProps> = ({
|
|
|
32
46
|
expandableSectionProps,
|
|
33
47
|
subheading,
|
|
34
48
|
toggleContent,
|
|
35
|
-
|
|
49
|
+
isDefaultExpanded = true,
|
|
50
|
+
cardBodyProps,
|
|
51
|
+
isToggleContentMarkdown,
|
|
52
|
+
isSubheadingMarkdown,
|
|
53
|
+
isBodyMarkdown,
|
|
54
|
+
markdownContentProps,
|
|
55
|
+
shouldRetainStyles = false
|
|
36
56
|
}: DeepThinkingProps) => {
|
|
37
|
-
const [isExpanded, setIsExpanded] = useState(
|
|
57
|
+
const [isExpanded, setIsExpanded] = useState(isDefaultExpanded);
|
|
38
58
|
|
|
39
59
|
const onToggle = (_event: React.MouseEvent, isExpanded: boolean) => {
|
|
40
60
|
setIsExpanded(isExpanded);
|
|
41
61
|
};
|
|
42
62
|
|
|
63
|
+
const renderToggleContent = () => {
|
|
64
|
+
if (isToggleContentMarkdown && typeof toggleContent === 'string') {
|
|
65
|
+
return (
|
|
66
|
+
<MarkdownContent shouldRetainStyles={shouldRetainStyles} content={toggleContent} {...markdownContentProps} />
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return toggleContent;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const renderSubheading = () => {
|
|
73
|
+
if (!subheading) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
if (isSubheadingMarkdown) {
|
|
77
|
+
return <MarkdownContent shouldRetainStyles={shouldRetainStyles} content={subheading} {...markdownContentProps} />;
|
|
78
|
+
}
|
|
79
|
+
return subheading;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const renderBody = () => {
|
|
83
|
+
if (!body) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (isBodyMarkdown && typeof body === 'string') {
|
|
87
|
+
return <MarkdownContent shouldRetainStyles={shouldRetainStyles} content={body} {...markdownContentProps} />;
|
|
88
|
+
}
|
|
89
|
+
return body;
|
|
90
|
+
};
|
|
91
|
+
|
|
43
92
|
return (
|
|
44
93
|
<Card isCompact className="pf-chatbot__deep-thinking" {...cardProps}>
|
|
45
94
|
<CardBody {...cardBodyProps}>
|
|
46
95
|
<ExpandableSection
|
|
47
|
-
toggleContent={
|
|
96
|
+
toggleContent={renderToggleContent()}
|
|
48
97
|
onToggle={onToggle}
|
|
49
98
|
isExpanded={isExpanded}
|
|
50
99
|
isIndented
|
|
@@ -54,10 +103,10 @@ export const DeepThinking: FunctionComponent<DeepThinkingProps> = ({
|
|
|
54
103
|
<div className="pf-chatbot__deep-thinking-section">
|
|
55
104
|
{subheading && (
|
|
56
105
|
<div className="pf-chatbot__deep-thinking-subheading">
|
|
57
|
-
<span>{
|
|
106
|
+
<span>{renderSubheading()}</span>
|
|
58
107
|
</div>
|
|
59
108
|
)}
|
|
60
|
-
{body && <div className="pf-chatbot__deep-thinking-body">{
|
|
109
|
+
{body && <div className="pf-chatbot__deep-thinking-body">{renderBody()}</div>}
|
|
61
110
|
</div>
|
|
62
111
|
</ExpandableSection>
|
|
63
112
|
</CardBody>
|