@patternfly/chatbot 6.4.0-prerelease.24 → 6.4.0-prerelease.25
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/Message/Message.d.ts +2 -0
- package/dist/cjs/Message/Message.js +6 -2
- package/dist/cjs/Message/Message.test.js +17 -2
- package/dist/esm/Message/Message.d.ts +2 -0
- package/dist/esm/Message/Message.js +6 -2
- package/dist/esm/Message/Message.test.js +17 -2
- package/package.json +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +1 -0
- package/src/Message/Message.test.tsx +25 -2
- package/src/Message/Message.tsx +9 -0
|
@@ -158,6 +158,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
158
158
|
remarkGfmProps?: Options;
|
|
159
159
|
/** Props for a tool call message */
|
|
160
160
|
toolCall?: ToolCallProps;
|
|
161
|
+
/** Whether user messages default to stripping out images in markdown */
|
|
162
|
+
hasNoImagesInUserMessages?: boolean;
|
|
161
163
|
}
|
|
162
164
|
export declare const MessageBase: FunctionComponent<MessageProps>;
|
|
163
165
|
declare const Message: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -55,7 +55,7 @@ const DeepThinking_1 = __importDefault(require("../DeepThinking"));
|
|
|
55
55
|
const SuperscriptMessage_1 = __importDefault(require("./SuperscriptMessage/SuperscriptMessage"));
|
|
56
56
|
const ToolCall_1 = __importDefault(require("../ToolCall"));
|
|
57
57
|
const MessageBase = (_a) => {
|
|
58
|
-
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, 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 } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "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"]);
|
|
58
|
+
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, 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 } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "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"]);
|
|
59
59
|
const [messageText, setMessageText] = (0, react_1.useState)(content);
|
|
60
60
|
(0, react_1.useEffect)(() => {
|
|
61
61
|
setMessageText(content);
|
|
@@ -77,6 +77,10 @@ const MessageBase = (_a) => {
|
|
|
77
77
|
// Keep timestamps consistent between Timestamp component and aria-label
|
|
78
78
|
const date = new Date();
|
|
79
79
|
const dateString = timestamp !== null && timestamp !== void 0 ? timestamp : `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
80
|
+
const disallowedElements = role === 'user' && hasNoImagesInUserMessages ? ['img'] : [];
|
|
81
|
+
if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
|
|
82
|
+
disallowedElements.push(...reactMarkdownProps.disallowedElements);
|
|
83
|
+
}
|
|
80
84
|
const handleMarkdown = () => {
|
|
81
85
|
if (isMarkdownDisabled) {
|
|
82
86
|
return ((0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.p }, props, { children: messageText })));
|
|
@@ -198,7 +202,7 @@ const MessageBase = (_a) => {
|
|
|
198
202
|
}
|
|
199
203
|
}, remarkPlugins: [[remark_gfm_1.default, Object.assign({}, remarkGfmProps)], ...additionalRemarkPlugins], rehypePlugins: rehypePlugins }, reactMarkdownProps, { remarkRehypeOptions: Object.assign({
|
|
200
204
|
// removes sr-only class from footnote labels applied by default
|
|
201
|
-
footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), children: messageText })));
|
|
205
|
+
footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), disallowedElements: disallowedElements, children: messageText })));
|
|
202
206
|
};
|
|
203
207
|
const renderMessage = () => {
|
|
204
208
|
if (isLoading) {
|
|
@@ -705,12 +705,16 @@ describe('Message', () => {
|
|
|
705
705
|
expect(react_2.screen.getByTestId('after-main-content')).toContainHTML('<strong>Bold after content</strong>');
|
|
706
706
|
expect(react_2.screen.getByTestId('end-main-content')).toContainHTML('<strong>Bold end content</strong>');
|
|
707
707
|
});
|
|
708
|
-
it('should handle image correctly', () => {
|
|
708
|
+
it('should handle image correctly for user', () => {
|
|
709
709
|
(0, react_2.render)((0, jsx_runtime_1.jsx)(Message_1.default, { avatar: "./img", role: "user", name: "User", content: IMAGE }));
|
|
710
|
+
expect(react_2.screen.queryByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeFalsy();
|
|
711
|
+
});
|
|
712
|
+
it('should handle image correctly for bot', () => {
|
|
713
|
+
(0, react_2.render)((0, jsx_runtime_1.jsx)(Message_1.default, { avatar: "./img", role: "bot", name: "Bot", content: IMAGE }));
|
|
710
714
|
expect(react_2.screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
711
715
|
});
|
|
712
716
|
it('inline image parent should have class pf-chatbot__message-and-actions', () => {
|
|
713
|
-
(0, react_2.render)((0, jsx_runtime_1.jsx)(Message_1.default, { avatar: "./img", role: "
|
|
717
|
+
(0, react_2.render)((0, jsx_runtime_1.jsx)(Message_1.default, { avatar: "./img", role: "bot", name: "Bot", content: INLINE_IMAGE }));
|
|
714
718
|
expect(react_2.screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
715
719
|
expect(react_2.screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i }).parentElement).toHaveClass('pf-chatbot__message-and-actions');
|
|
716
720
|
});
|
|
@@ -804,6 +808,17 @@ describe('Message', () => {
|
|
|
804
808
|
// code block isn't rendering
|
|
805
809
|
expect(react_2.screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
806
810
|
});
|
|
811
|
+
it('should disable images and additional tags for user messages', () => {
|
|
812
|
+
(0, react_2.render)((0, jsx_runtime_1.jsx)(Message_1.default, { avatar: "./img", role: "user", name: "User", content: `${IMAGE} ${CODE_MESSAGE}`, reactMarkdownProps: { disallowedElements: ['code'] } }));
|
|
813
|
+
expect(react_2.screen.getByText('Here is some YAML code:')).toBeTruthy();
|
|
814
|
+
// code block isn't rendering
|
|
815
|
+
expect(react_2.screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
816
|
+
expect(react_2.screen.queryByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeFalsy();
|
|
817
|
+
});
|
|
818
|
+
it('can override image tag removal default for user messages', () => {
|
|
819
|
+
(0, react_2.render)((0, jsx_runtime_1.jsx)(Message_1.default, { avatar: "./img", role: "user", name: "User", content: IMAGE, hasNoImagesInUserMessages: false }));
|
|
820
|
+
expect(react_2.screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
821
|
+
});
|
|
807
822
|
it('should render deep thinking section correctly', () => {
|
|
808
823
|
(0, react_2.render)((0, jsx_runtime_1.jsx)(Message_1.default, { avatar: "./img", role: "user", name: "User", content: "", deepThinking: DEEP_THINKING }));
|
|
809
824
|
expect(react_2.screen.getByRole('button', { name: /Show thinking/i })).toBeTruthy();
|
|
@@ -158,6 +158,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
158
158
|
remarkGfmProps?: Options;
|
|
159
159
|
/** Props for a tool call message */
|
|
160
160
|
toolCall?: ToolCallProps;
|
|
161
|
+
/** Whether user messages default to stripping out images in markdown */
|
|
162
|
+
hasNoImagesInUserMessages?: boolean;
|
|
161
163
|
}
|
|
162
164
|
export declare const MessageBase: FunctionComponent<MessageProps>;
|
|
163
165
|
declare const Message: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -49,7 +49,7 @@ import DeepThinking from '../DeepThinking';
|
|
|
49
49
|
import SuperscriptMessage from './SuperscriptMessage/SuperscriptMessage';
|
|
50
50
|
import ToolCall from '../ToolCall';
|
|
51
51
|
export const MessageBase = (_a) => {
|
|
52
|
-
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, 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 } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "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"]);
|
|
52
|
+
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, 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 } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "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"]);
|
|
53
53
|
const [messageText, setMessageText] = useState(content);
|
|
54
54
|
useEffect(() => {
|
|
55
55
|
setMessageText(content);
|
|
@@ -71,6 +71,10 @@ export const MessageBase = (_a) => {
|
|
|
71
71
|
// Keep timestamps consistent between Timestamp component and aria-label
|
|
72
72
|
const date = new Date();
|
|
73
73
|
const dateString = timestamp !== null && timestamp !== void 0 ? timestamp : `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
74
|
+
const disallowedElements = role === 'user' && hasNoImagesInUserMessages ? ['img'] : [];
|
|
75
|
+
if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
|
|
76
|
+
disallowedElements.push(...reactMarkdownProps.disallowedElements);
|
|
77
|
+
}
|
|
74
78
|
const handleMarkdown = () => {
|
|
75
79
|
if (isMarkdownDisabled) {
|
|
76
80
|
return (_jsx(TextMessage, Object.assign({ component: ContentVariants.p }, props, { children: messageText })));
|
|
@@ -192,7 +196,7 @@ export const MessageBase = (_a) => {
|
|
|
192
196
|
}
|
|
193
197
|
}, remarkPlugins: [[remarkGfm, Object.assign({}, remarkGfmProps)], ...additionalRemarkPlugins], rehypePlugins: rehypePlugins }, reactMarkdownProps, { remarkRehypeOptions: Object.assign({
|
|
194
198
|
// removes sr-only class from footnote labels applied by default
|
|
195
|
-
footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), children: messageText })));
|
|
199
|
+
footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), disallowedElements: disallowedElements, children: messageText })));
|
|
196
200
|
};
|
|
197
201
|
const renderMessage = () => {
|
|
198
202
|
if (isLoading) {
|
|
@@ -700,12 +700,16 @@ describe('Message', () => {
|
|
|
700
700
|
expect(screen.getByTestId('after-main-content')).toContainHTML('<strong>Bold after content</strong>');
|
|
701
701
|
expect(screen.getByTestId('end-main-content')).toContainHTML('<strong>Bold end content</strong>');
|
|
702
702
|
});
|
|
703
|
-
it('should handle image correctly', () => {
|
|
703
|
+
it('should handle image correctly for user', () => {
|
|
704
704
|
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: IMAGE }));
|
|
705
|
+
expect(screen.queryByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeFalsy();
|
|
706
|
+
});
|
|
707
|
+
it('should handle image correctly for bot', () => {
|
|
708
|
+
render(_jsx(Message, { avatar: "./img", role: "bot", name: "Bot", content: IMAGE }));
|
|
705
709
|
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
706
710
|
});
|
|
707
711
|
it('inline image parent should have class pf-chatbot__message-and-actions', () => {
|
|
708
|
-
render(_jsx(Message, { avatar: "./img", role: "
|
|
712
|
+
render(_jsx(Message, { avatar: "./img", role: "bot", name: "Bot", content: INLINE_IMAGE }));
|
|
709
713
|
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
710
714
|
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i }).parentElement).toHaveClass('pf-chatbot__message-and-actions');
|
|
711
715
|
});
|
|
@@ -799,6 +803,17 @@ describe('Message', () => {
|
|
|
799
803
|
// code block isn't rendering
|
|
800
804
|
expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
801
805
|
});
|
|
806
|
+
it('should disable images and additional tags for user messages', () => {
|
|
807
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: `${IMAGE} ${CODE_MESSAGE}`, reactMarkdownProps: { disallowedElements: ['code'] } }));
|
|
808
|
+
expect(screen.getByText('Here is some YAML code:')).toBeTruthy();
|
|
809
|
+
// code block isn't rendering
|
|
810
|
+
expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
811
|
+
expect(screen.queryByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeFalsy();
|
|
812
|
+
});
|
|
813
|
+
it('can override image tag removal default for user messages', () => {
|
|
814
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: IMAGE, hasNoImagesInUserMessages: false }));
|
|
815
|
+
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
816
|
+
});
|
|
802
817
|
it('should render deep thinking section correctly', () => {
|
|
803
818
|
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "", deepThinking: DEEP_THINKING }));
|
|
804
819
|
expect(screen.getByRole('button', { name: /Show thinking/i })).toBeTruthy();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/chatbot",
|
|
3
|
-
"version": "6.4.0-prerelease.
|
|
3
|
+
"version": "6.4.0-prerelease.25",
|
|
4
4
|
"description": "This library provides React components based on PatternFly 6 that can be used to build chatbots.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -919,12 +919,16 @@ describe('Message', () => {
|
|
|
919
919
|
expect(screen.getByTestId('after-main-content')).toContainHTML('<strong>Bold after content</strong>');
|
|
920
920
|
expect(screen.getByTestId('end-main-content')).toContainHTML('<strong>Bold end content</strong>');
|
|
921
921
|
});
|
|
922
|
-
it('should handle image correctly', () => {
|
|
922
|
+
it('should handle image correctly for user', () => {
|
|
923
923
|
render(<Message avatar="./img" role="user" name="User" content={IMAGE} />);
|
|
924
|
+
expect(screen.queryByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeFalsy();
|
|
925
|
+
});
|
|
926
|
+
it('should handle image correctly for bot', () => {
|
|
927
|
+
render(<Message avatar="./img" role="bot" name="Bot" content={IMAGE} />);
|
|
924
928
|
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
925
929
|
});
|
|
926
930
|
it('inline image parent should have class pf-chatbot__message-and-actions', () => {
|
|
927
|
-
render(<Message avatar="./img" role="
|
|
931
|
+
render(<Message avatar="./img" role="bot" name="Bot" content={INLINE_IMAGE} />);
|
|
928
932
|
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
929
933
|
expect(
|
|
930
934
|
screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i }).parentElement
|
|
@@ -1046,6 +1050,25 @@ describe('Message', () => {
|
|
|
1046
1050
|
// code block isn't rendering
|
|
1047
1051
|
expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
1048
1052
|
});
|
|
1053
|
+
it('should disable images and additional tags for user messages', () => {
|
|
1054
|
+
render(
|
|
1055
|
+
<Message
|
|
1056
|
+
avatar="./img"
|
|
1057
|
+
role="user"
|
|
1058
|
+
name="User"
|
|
1059
|
+
content={`${IMAGE} ${CODE_MESSAGE}`}
|
|
1060
|
+
reactMarkdownProps={{ disallowedElements: ['code'] }}
|
|
1061
|
+
/>
|
|
1062
|
+
);
|
|
1063
|
+
expect(screen.getByText('Here is some YAML code:')).toBeTruthy();
|
|
1064
|
+
// code block isn't rendering
|
|
1065
|
+
expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
1066
|
+
expect(screen.queryByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeFalsy();
|
|
1067
|
+
});
|
|
1068
|
+
it('can override image tag removal default for user messages', () => {
|
|
1069
|
+
render(<Message avatar="./img" role="user" name="User" content={IMAGE} hasNoImagesInUserMessages={false} />);
|
|
1070
|
+
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
|
1071
|
+
});
|
|
1049
1072
|
it('should render deep thinking section correctly', () => {
|
|
1050
1073
|
render(<Message avatar="./img" role="user" name="User" content="" deepThinking={DEEP_THINKING} />);
|
|
1051
1074
|
expect(screen.getByRole('button', { name: /Show thinking/i })).toBeTruthy();
|
package/src/Message/Message.tsx
CHANGED
|
@@ -203,6 +203,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
203
203
|
remarkGfmProps?: Options;
|
|
204
204
|
/** Props for a tool call message */
|
|
205
205
|
toolCall?: ToolCallProps;
|
|
206
|
+
/** Whether user messages default to stripping out images in markdown */
|
|
207
|
+
hasNoImagesInUserMessages?: boolean;
|
|
206
208
|
}
|
|
207
209
|
|
|
208
210
|
export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
@@ -249,6 +251,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
249
251
|
deepThinking,
|
|
250
252
|
remarkGfmProps,
|
|
251
253
|
toolCall,
|
|
254
|
+
hasNoImagesInUserMessages = true,
|
|
252
255
|
...props
|
|
253
256
|
}: MessageProps) => {
|
|
254
257
|
const [messageText, setMessageText] = useState(content);
|
|
@@ -275,6 +278,11 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
275
278
|
const date = new Date();
|
|
276
279
|
const dateString = timestamp ?? `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
277
280
|
|
|
281
|
+
const disallowedElements = role === 'user' && hasNoImagesInUserMessages ? ['img'] : [];
|
|
282
|
+
if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
|
|
283
|
+
disallowedElements.push(...reactMarkdownProps.disallowedElements);
|
|
284
|
+
}
|
|
285
|
+
|
|
278
286
|
const handleMarkdown = () => {
|
|
279
287
|
if (isMarkdownDisabled) {
|
|
280
288
|
return (
|
|
@@ -415,6 +423,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
|
|
|
415
423
|
footnoteLabelProperties: { className: [''] },
|
|
416
424
|
...reactMarkdownProps?.remarkRehypeOptions
|
|
417
425
|
}}
|
|
426
|
+
disallowedElements={disallowedElements}
|
|
418
427
|
>
|
|
419
428
|
{messageText}
|
|
420
429
|
</Markdown>
|