@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/src/Message/Message.tsx
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
271
|
-
|
|
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
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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) {
|
|
@@ -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 = ({
|
|
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={
|
|
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={
|
|
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>
|
|
@@ -181,4 +181,95 @@ describe('ToolCall', () => {
|
|
|
181
181
|
render(<ToolCall {...defaultProps} cardFooterProps={{ id: 'card-footer-test-id' }} />);
|
|
182
182
|
expect(screen.getByRole('button', { name: 'Run tool' }).closest('#card-footer-test-id')).toBeVisible();
|
|
183
183
|
});
|
|
184
|
+
|
|
185
|
+
it('Renders collapsed by default when expandableContent is provided', () => {
|
|
186
|
+
render(<ToolCall {...defaultProps} expandableContent="Expandable Content" />);
|
|
187
|
+
|
|
188
|
+
expect(screen.getByRole('button', { name: defaultProps.titleText })).toHaveAttribute('aria-expanded', 'false');
|
|
189
|
+
expect(screen.queryByText('Expandable Content')).not.toBeVisible();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('Renders expanded when isDefaultExpanded is true', () => {
|
|
193
|
+
render(<ToolCall {...defaultProps} isDefaultExpanded expandableContent="Expandable Content" />);
|
|
194
|
+
|
|
195
|
+
expect(screen.getByRole('button', { name: defaultProps.titleText })).toHaveAttribute('aria-expanded', 'true');
|
|
196
|
+
expect(screen.getByText('Expandable Content')).toBeVisible();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('expandableSectionProps.isExpanded overrides isDefaultExpanded', () => {
|
|
200
|
+
render(
|
|
201
|
+
<ToolCall
|
|
202
|
+
{...defaultProps}
|
|
203
|
+
isDefaultExpanded={false}
|
|
204
|
+
expandableContent="Expandable Content"
|
|
205
|
+
expandableSectionProps={{ isExpanded: true }}
|
|
206
|
+
/>
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
expect(screen.getByRole('button', { name: defaultProps.titleText })).toHaveAttribute('aria-expanded', 'true');
|
|
210
|
+
expect(screen.getByText('Expandable Content')).toBeVisible();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('expandableSectionProps.onToggle overrides internal onToggle behavior', async () => {
|
|
214
|
+
const user = userEvent.setup();
|
|
215
|
+
const customOnToggle = jest.fn();
|
|
216
|
+
|
|
217
|
+
render(
|
|
218
|
+
<ToolCall
|
|
219
|
+
{...defaultProps}
|
|
220
|
+
isDefaultExpanded={false}
|
|
221
|
+
expandableContent="Expandable Content"
|
|
222
|
+
expandableSectionProps={{ onToggle: customOnToggle }}
|
|
223
|
+
/>
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const toggleButton = screen.getByRole('button', { name: defaultProps.titleText });
|
|
227
|
+
expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
|
|
228
|
+
|
|
229
|
+
await user.click(toggleButton);
|
|
230
|
+
|
|
231
|
+
expect(customOnToggle).toHaveBeenCalledTimes(1);
|
|
232
|
+
expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
|
|
233
|
+
expect(screen.queryByText('Expandable Content')).not.toBeVisible();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should render titleText as markdown when isTitleMarkdown is true', () => {
|
|
237
|
+
const titleText = '**Bold title**';
|
|
238
|
+
const { container } = render(<ToolCall titleText={titleText} isTitleMarkdown />);
|
|
239
|
+
expect(container.querySelector('strong')).toBeTruthy();
|
|
240
|
+
expect(screen.getByText('Bold title')).toBeTruthy();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('should not render titleText as markdown when isTitleMarkdown is false', () => {
|
|
244
|
+
const titleText = '**Bold title**';
|
|
245
|
+
render(<ToolCall titleText={titleText} />);
|
|
246
|
+
expect(screen.getByText('**Bold title**')).toBeTruthy();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should render expandableContent as markdown when isExpandableContentMarkdown is true', async () => {
|
|
250
|
+
const user = userEvent.setup();
|
|
251
|
+
const expandableContent = '**Bold expandable content**';
|
|
252
|
+
const { container } = render(
|
|
253
|
+
<ToolCall {...defaultProps} expandableContent={expandableContent} isExpandableContentMarkdown />
|
|
254
|
+
);
|
|
255
|
+
await user.click(screen.getByRole('button', { name: defaultProps.titleText }));
|
|
256
|
+
expect(container.querySelector('strong')).toBeTruthy();
|
|
257
|
+
expect(screen.getByText('Bold expandable content')).toBeTruthy();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should not render expandableContent as markdown when isExpandableContentMarkdown is false', async () => {
|
|
261
|
+
const user = userEvent.setup();
|
|
262
|
+
const expandableContent = '**Bold expandable content**';
|
|
263
|
+
render(<ToolCall {...defaultProps} expandableContent={expandableContent} />);
|
|
264
|
+
await user.click(screen.getByRole('button', { name: defaultProps.titleText }));
|
|
265
|
+
expect(screen.getByText('**Bold expandable content**')).toBeTruthy();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should pass markdownContentProps to MarkdownContent component', () => {
|
|
269
|
+
const titleText = '**Bold title**';
|
|
270
|
+
const { container } = render(
|
|
271
|
+
<ToolCall titleText={titleText} isTitleMarkdown markdownContentProps={{ isPrimary: true }} />
|
|
272
|
+
);
|
|
273
|
+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
|
|
274
|
+
});
|
|
184
275
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FunctionComponent } from 'react';
|
|
1
|
+
import { useState, type FunctionComponent } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ActionList,
|
|
4
4
|
ActionListProps,
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
Spinner,
|
|
20
20
|
SpinnerProps
|
|
21
21
|
} from '@patternfly/react-core';
|
|
22
|
+
import MarkdownContent from '../MarkdownContent';
|
|
23
|
+
import type { MarkdownContentProps } from '../MarkdownContent';
|
|
22
24
|
|
|
23
25
|
export interface ToolCallProps {
|
|
24
26
|
/** Title text for the tool call. */
|
|
@@ -31,6 +33,8 @@ export interface ToolCallProps {
|
|
|
31
33
|
spinnerProps?: SpinnerProps;
|
|
32
34
|
/** Content to render within an expandable section. */
|
|
33
35
|
expandableContent?: React.ReactNode;
|
|
36
|
+
/** Flag indicating whether the expandable content is expanded by default. */
|
|
37
|
+
isDefaultExpanded?: boolean;
|
|
34
38
|
/** Text content for the "run" action button. */
|
|
35
39
|
runButtonText?: string;
|
|
36
40
|
/** Additional props for the "run" action button. */
|
|
@@ -59,6 +63,14 @@ export interface ToolCallProps {
|
|
|
59
63
|
cardFooterProps?: CardFooterProps;
|
|
60
64
|
/** Additional props for the expandable section when expandableContent is passed. */
|
|
61
65
|
expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
|
|
66
|
+
/** Whether to enable markdown rendering for titleText. When true, titleText will be parsed as markdown. */
|
|
67
|
+
isTitleMarkdown?: boolean;
|
|
68
|
+
/** Whether to enable markdown rendering for expandableContent. When true and expandableContent is a string, it will be parsed as markdown. */
|
|
69
|
+
isExpandableContentMarkdown?: boolean;
|
|
70
|
+
/** Props passed to MarkdownContent component when markdown is enabled */
|
|
71
|
+
markdownContentProps?: Omit<MarkdownContentProps, 'content'>;
|
|
72
|
+
/** Whether to retain styles in the MarkdownContent component. Defaults to false. */
|
|
73
|
+
shouldRetainStyles?: boolean;
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
export const ToolCall: FunctionComponent<ToolCallProps> = ({
|
|
@@ -66,6 +78,7 @@ export const ToolCall: FunctionComponent<ToolCallProps> = ({
|
|
|
66
78
|
loadingText,
|
|
67
79
|
isLoading,
|
|
68
80
|
expandableContent,
|
|
81
|
+
isDefaultExpanded = false,
|
|
69
82
|
runButtonText = 'Run tool',
|
|
70
83
|
runButtonProps,
|
|
71
84
|
runActionItemProps,
|
|
@@ -80,8 +93,25 @@ export const ToolCall: FunctionComponent<ToolCallProps> = ({
|
|
|
80
93
|
cardBodyProps,
|
|
81
94
|
cardFooterProps,
|
|
82
95
|
expandableSectionProps,
|
|
83
|
-
spinnerProps
|
|
96
|
+
spinnerProps,
|
|
97
|
+
isTitleMarkdown,
|
|
98
|
+
isExpandableContentMarkdown,
|
|
99
|
+
markdownContentProps,
|
|
100
|
+
shouldRetainStyles = false
|
|
84
101
|
}: ToolCallProps) => {
|
|
102
|
+
const [isExpanded, setIsExpanded] = useState(isDefaultExpanded);
|
|
103
|
+
|
|
104
|
+
const onToggle = (_event: React.MouseEvent, isExpanded: boolean) => {
|
|
105
|
+
setIsExpanded(isExpanded);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const renderTitle = () => {
|
|
109
|
+
if (isTitleMarkdown) {
|
|
110
|
+
return <MarkdownContent shouldRetainStyles={shouldRetainStyles} content={titleText} {...markdownContentProps} />;
|
|
111
|
+
}
|
|
112
|
+
return titleText;
|
|
113
|
+
};
|
|
114
|
+
|
|
85
115
|
const titleContent = (
|
|
86
116
|
<span className={`pf-chatbot__tool-call-title-content`}>
|
|
87
117
|
{isLoading ? (
|
|
@@ -90,10 +120,23 @@ export const ToolCall: FunctionComponent<ToolCallProps> = ({
|
|
|
90
120
|
{<span className="pf-chatbot__tool-call-title-text">{loadingText}</span>}
|
|
91
121
|
</>
|
|
92
122
|
) : (
|
|
93
|
-
<span className="pf-chatbot__tool-call-title-text">{
|
|
123
|
+
<span className="pf-chatbot__tool-call-title-text">{renderTitle()}</span>
|
|
94
124
|
)}
|
|
95
125
|
</span>
|
|
96
126
|
);
|
|
127
|
+
|
|
128
|
+
const renderExpandableContent = () => {
|
|
129
|
+
if (isExpandableContentMarkdown && typeof expandableContent === 'string') {
|
|
130
|
+
return (
|
|
131
|
+
<MarkdownContent
|
|
132
|
+
shouldRetainStyles={shouldRetainStyles}
|
|
133
|
+
content={expandableContent}
|
|
134
|
+
{...markdownContentProps}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return expandableContent;
|
|
139
|
+
};
|
|
97
140
|
const defaultActions = (
|
|
98
141
|
<>
|
|
99
142
|
<ActionListItem {...actionListItemProps} {...cancelActionItemProps}>
|
|
@@ -124,10 +167,12 @@ export const ToolCall: FunctionComponent<ToolCallProps> = ({
|
|
|
124
167
|
<ExpandableSection
|
|
125
168
|
className="pf-chatbot__tool-call-expandable-section"
|
|
126
169
|
toggleContent={titleContent}
|
|
170
|
+
onToggle={onToggle}
|
|
171
|
+
isExpanded={isExpanded}
|
|
127
172
|
isIndented
|
|
128
173
|
{...expandableSectionProps}
|
|
129
174
|
>
|
|
130
|
-
{
|
|
175
|
+
{renderExpandableContent()}
|
|
131
176
|
</ExpandableSection>
|
|
132
177
|
) : (
|
|
133
178
|
titleContent
|
|
@@ -34,3 +34,13 @@
|
|
|
34
34
|
--pf-v6-c-divider--Color: var(--pf-t--global--border--color--default);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
.pf-chatbot__tool-response-expandable-section .pf-v6-c-expandable-section__toggle .pf-m-markdown {
|
|
39
|
+
padding: inherit;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.pf-chatbot__tool-response {
|
|
43
|
+
.pf-chatbot__message-image {
|
|
44
|
+
max-width: 100%;
|
|
45
|
+
}
|
|
46
|
+
}
|