@aws-amplify/ui-react-ai 0.0.0-storage-browser-alpha-5147028-20240909221818 → 0.0.0-storage-browser-alpha-4edafc6-20241001200620
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/esm/components/AIConversation/AIConversation.mjs +3 -1
- package/dist/esm/components/AIConversation/context/AttachmentContext.mjs +8 -0
- package/dist/esm/components/AIConversation/context/DisplayTextContext.mjs +9 -0
- package/dist/esm/components/AIConversation/context/ResponseComponentsContext.mjs +10 -2
- package/dist/esm/components/AIConversation/createAIConversation.mjs +3 -1
- package/dist/esm/components/AIConversation/createProvider.mjs +21 -11
- package/dist/esm/components/AIConversation/displayText.mjs +7 -0
- package/dist/esm/components/AIConversation/views/Controls/ActionsBarControl.mjs +5 -0
- package/dist/esm/components/AIConversation/views/Controls/AttachFileControl.mjs +18 -1
- package/dist/esm/components/AIConversation/views/Controls/AttachmentListControl.mjs +5 -0
- package/dist/esm/components/AIConversation/views/Controls/AvatarControl.mjs +5 -0
- package/dist/esm/components/AIConversation/views/Controls/FieldControl.mjs +10 -7
- package/dist/esm/components/AIConversation/views/Controls/MessagesControl.mjs +15 -6
- package/dist/esm/components/AIConversation/views/Controls/PromptControl.mjs +5 -1
- package/dist/esm/components/AIConversation/views/default/Form.mjs +26 -11
- package/dist/esm/components/AIConversation/views/default/MessageList.mjs +25 -3
- package/dist/index.js +366 -286
- package/dist/types/components/AIConversation/AIConversation.d.ts +5 -4
- package/dist/types/components/AIConversation/context/AttachmentContext.d.ts +6 -0
- package/dist/types/components/AIConversation/context/ControlsContext.d.ts +1 -0
- package/dist/types/components/AIConversation/context/DisplayTextContext.d.ts +5 -0
- package/dist/types/components/AIConversation/context/ResponseComponentsContext.d.ts +1 -0
- package/dist/types/components/AIConversation/context/index.d.ts +11 -7
- package/dist/types/components/AIConversation/createProvider.d.ts +2 -2
- package/dist/types/components/AIConversation/displayText.d.ts +2 -2
- package/dist/types/components/AIConversation/index.d.ts +2 -1
- package/dist/types/components/AIConversation/types.d.ts +1 -0
- package/dist/types/components/AIConversation/views/default/Form.d.ts +1 -1
- package/dist/types/index.d.ts +3 -3
- package/package.json +7 -7
|
@@ -14,7 +14,7 @@ import { PromptList } from './views/default/PromptList.mjs';
|
|
|
14
14
|
import { ComponentClassName } from '@aws-amplify/ui';
|
|
15
15
|
import createProvider from './createProvider.mjs';
|
|
16
16
|
|
|
17
|
-
function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, }) {
|
|
17
|
+
function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, displayText, allowAttachments, }) {
|
|
18
18
|
const icons = useIcons('aiConversation');
|
|
19
19
|
const defaultAvatars = {
|
|
20
20
|
ai: {
|
|
@@ -42,6 +42,7 @@ function AIConversationBase({ actions, avatars, controls, handleSendMessage, mes
|
|
|
42
42
|
Form,
|
|
43
43
|
...controls,
|
|
44
44
|
},
|
|
45
|
+
displayText,
|
|
45
46
|
});
|
|
46
47
|
const providerProps = {
|
|
47
48
|
messages,
|
|
@@ -51,6 +52,7 @@ function AIConversationBase({ actions, avatars, controls, handleSendMessage, mes
|
|
|
51
52
|
...avatars,
|
|
52
53
|
},
|
|
53
54
|
isLoading,
|
|
55
|
+
allowAttachments,
|
|
54
56
|
};
|
|
55
57
|
return (React.createElement(Provider, { ...providerProps },
|
|
56
58
|
React.createElement(Flex, { className: ComponentClassName.AIConversation },
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
const AttachmentContext = React.createContext(false);
|
|
4
|
+
const AttachmentProvider = ({ children, allowAttachments, }) => {
|
|
5
|
+
return (React.createElement(AttachmentContext.Provider, { value: allowAttachments ?? false }, children));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export { AttachmentContext, AttachmentProvider };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContextUtilities } from '@aws-amplify/ui-react-core';
|
|
2
|
+
import { defaultAIConversationDisplayTextEn } from '../displayText.mjs';
|
|
3
|
+
|
|
4
|
+
const { ConversationDisplayTextContext, ConversationDisplayTextProvider, useConversationDisplayText, } = createContextUtilities({
|
|
5
|
+
contextName: 'ConversationDisplayText',
|
|
6
|
+
defaultValue: defaultAIConversationDisplayTextEn,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export { ConversationDisplayTextContext, ConversationDisplayTextProvider, useConversationDisplayText };
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
|
|
3
|
+
const RESPONSE_COMPONENT_PREFIX = 'AMPLIFY_UI_';
|
|
3
4
|
const ResponseComponentsContext = React__default.createContext(undefined);
|
|
5
|
+
const prependResponseComponents = (responseComponents) => {
|
|
6
|
+
if (!responseComponents)
|
|
7
|
+
return responseComponents;
|
|
8
|
+
return Object.keys(responseComponents).reduce((prev, key) => ((prev[`${RESPONSE_COMPONENT_PREFIX}${key}`] = responseComponents[key]),
|
|
9
|
+
prev), {});
|
|
10
|
+
};
|
|
4
11
|
const ResponseComponentsProvider = ({ children, responseComponents, }) => {
|
|
5
|
-
|
|
12
|
+
const _responseComponents = React__default.useMemo(() => prependResponseComponents(responseComponents), [responseComponents]);
|
|
13
|
+
return (React__default.createElement(ResponseComponentsContext.Provider, { value: _responseComponents }, children));
|
|
6
14
|
};
|
|
7
15
|
const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
8
16
|
if (!responseComponents) {
|
|
@@ -32,4 +40,4 @@ const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
|
32
40
|
return { tools };
|
|
33
41
|
};
|
|
34
42
|
|
|
35
|
-
export { ResponseComponentsContext, ResponseComponentsProvider, convertResponseComponentsToToolConfiguration };
|
|
43
|
+
export { RESPONSE_COMPONENT_PREFIX, ResponseComponentsContext, ResponseComponentsProvider, convertResponseComponentsToToolConfiguration };
|
|
@@ -12,7 +12,7 @@ import createProvider from './createProvider.mjs';
|
|
|
12
12
|
* @experimental
|
|
13
13
|
*/
|
|
14
14
|
function createAIConversation(input = {}) {
|
|
15
|
-
const { elements, suggestedPrompts, actions, responseComponents, variant, controls, } = input;
|
|
15
|
+
const { elements, suggestedPrompts, actions, responseComponents, variant, controls, displayText, allowAttachments, } = input;
|
|
16
16
|
const Provider = createProvider({
|
|
17
17
|
elements,
|
|
18
18
|
actions,
|
|
@@ -20,6 +20,8 @@ function createAIConversation(input = {}) {
|
|
|
20
20
|
responseComponents,
|
|
21
21
|
variant,
|
|
22
22
|
controls,
|
|
23
|
+
displayText,
|
|
24
|
+
allowAttachments,
|
|
23
25
|
});
|
|
24
26
|
function AIConversation(props) {
|
|
25
27
|
const { messages, avatars, handleSendMessage, isLoading } = props;
|
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { ElementsProvider } from '@aws-amplify/ui-react-core/elements';
|
|
3
|
+
import { defaultAIConversationDisplayTextEn } from './displayText.mjs';
|
|
3
4
|
import { ActionsProvider } from './context/ActionsContext.mjs';
|
|
4
5
|
import { AvatarsProvider } from './context/AvatarsContext.mjs';
|
|
5
6
|
import { ConversationInputContextProvider } from './context/ConversationInputContext.mjs';
|
|
6
7
|
import { MessagesProvider } from './context/MessagesContext.mjs';
|
|
7
|
-
import { MessageVariantProvider } from './context/MessageVariantContext.mjs';
|
|
8
8
|
import { SuggestedPromptProvider } from './context/SuggestedPromptsContext.mjs';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { MessageVariantProvider } from './context/MessageVariantContext.mjs';
|
|
10
|
+
import { ConversationDisplayTextProvider } from './context/DisplayTextContext.mjs';
|
|
11
11
|
import { ControlsProvider } from './context/ControlsContext.mjs';
|
|
12
12
|
import { LoadingContextProvider } from './context/LoadingContext.mjs';
|
|
13
|
+
import { ResponseComponentsProvider } from './context/ResponseComponentsContext.mjs';
|
|
14
|
+
import { SendMessageContextProvider } from './context/SendMessageContext.mjs';
|
|
15
|
+
import './context/elements/definitions.mjs';
|
|
16
|
+
import { AttachmentProvider } from './context/AttachmentContext.mjs';
|
|
13
17
|
|
|
14
|
-
function createProvider({ elements, actions, suggestedPrompts, responseComponents, variant, controls, }) {
|
|
18
|
+
function createProvider({ elements, actions, suggestedPrompts, responseComponents, variant, controls, displayText, allowAttachments, }) {
|
|
15
19
|
return function Provider({ children, messages, avatars, handleSendMessage, isLoading, }) {
|
|
20
|
+
const _displayText = {
|
|
21
|
+
...defaultAIConversationDisplayTextEn,
|
|
22
|
+
...displayText,
|
|
23
|
+
};
|
|
16
24
|
return (React__default.createElement(ElementsProvider, { elements: elements },
|
|
17
25
|
React__default.createElement(ControlsProvider, { controls: controls },
|
|
18
26
|
React__default.createElement(SuggestedPromptProvider, { suggestedPrompts: suggestedPrompts },
|
|
19
27
|
React__default.createElement(ResponseComponentsProvider, { responseComponents: responseComponents },
|
|
20
|
-
React__default.createElement(
|
|
21
|
-
React__default.createElement(
|
|
22
|
-
React__default.createElement(
|
|
23
|
-
React__default.createElement(
|
|
24
|
-
React__default.createElement(
|
|
25
|
-
React__default.createElement(
|
|
26
|
-
React__default.createElement(
|
|
28
|
+
React__default.createElement(AttachmentProvider, { allowAttachments: allowAttachments },
|
|
29
|
+
React__default.createElement(ConversationDisplayTextProvider, { ..._displayText },
|
|
30
|
+
React__default.createElement(ConversationInputContextProvider, null,
|
|
31
|
+
React__default.createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
|
|
32
|
+
React__default.createElement(AvatarsProvider, { avatars: avatars },
|
|
33
|
+
React__default.createElement(ActionsProvider, { actions: actions },
|
|
34
|
+
React__default.createElement(MessageVariantProvider, { variant: variant },
|
|
35
|
+
React__default.createElement(MessagesProvider, { messages: messages },
|
|
36
|
+
React__default.createElement(LoadingContextProvider, { isLoading: isLoading }, children))))))))))))));
|
|
27
37
|
};
|
|
28
38
|
}
|
|
29
39
|
|
|
@@ -6,6 +6,11 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
6
6
|
import '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
|
|
11
16
|
const { Button, Span, View } = AIConversationElements;
|
|
@@ -6,7 +6,13 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
15
|
+
import { useDropZone } from '@aws-amplify/ui-react-core';
|
|
10
16
|
|
|
11
17
|
const { Button, Icon, View } = AIConversationElements;
|
|
12
18
|
const ATTACH_FILE_BLOCK = 'ai-attach-file';
|
|
@@ -30,6 +36,17 @@ const AttachFileButton = withBaseElementProps(Button, {
|
|
|
30
36
|
const AttachFileControl = () => {
|
|
31
37
|
const hiddenInput = React__default.useRef(null);
|
|
32
38
|
const { setInput } = React__default.useContext(ConversationInputContext);
|
|
39
|
+
const { dragState, ...dropHandlers } = useDropZone({
|
|
40
|
+
acceptedFileTypes: ['.jpeg'],
|
|
41
|
+
onDropComplete: ({ acceptedFiles }) => {
|
|
42
|
+
if (acceptedFiles && acceptedFiles?.length > 0 && setInput) {
|
|
43
|
+
setInput((prevInput) => ({
|
|
44
|
+
...prevInput,
|
|
45
|
+
files: [...(prevInput?.files ?? []), ...acceptedFiles],
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
33
50
|
function handleButtonClick() {
|
|
34
51
|
if (hiddenInput.current) {
|
|
35
52
|
hiddenInput.current.click();
|
|
@@ -47,7 +64,7 @@ const AttachFileControl = () => {
|
|
|
47
64
|
});
|
|
48
65
|
}
|
|
49
66
|
}
|
|
50
|
-
return (React__default.createElement(AttachFileContainer,
|
|
67
|
+
return (React__default.createElement(AttachFileContainer, { ...dropHandlers },
|
|
51
68
|
React__default.createElement(AttachFileButton, { onClick: handleButtonClick },
|
|
52
69
|
React__default.createElement(AttachFileIcon, null)),
|
|
53
70
|
React__default.createElement(VisuallyHidden, null,
|
|
@@ -6,6 +6,11 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
|
|
11
16
|
const { Button, Icon, ListItem, UnorderedList: ListElement, Span, Text, View, } = AIConversationElements;
|
|
@@ -6,6 +6,11 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
6
6
|
import { RoleContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
|
|
11
16
|
const { Icon, Span, Text, View } = AIConversationElements;
|
|
@@ -6,14 +6,16 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import { MessagesContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
11
|
+
import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
12
|
+
import { ResponseComponentsContext, convertResponseComponentsToToolConfiguration } from '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import { SendMessageContext } from '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
import { AttachFileControl } from './AttachFileControl.mjs';
|
|
11
16
|
import { AttachmentListControl } from './AttachmentListControl.mjs';
|
|
12
|
-
import { SendMessageContext } from '../../context/SendMessageContext.mjs';
|
|
13
|
-
import { ResponseComponentsContext, convertResponseComponentsToToolConfiguration } from '../../context/ResponseComponentsContext.mjs';
|
|
14
|
-
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
15
17
|
import { getImageTypeFromMimeType } from '../../utils.mjs';
|
|
16
|
-
import {
|
|
18
|
+
import { AttachmentContext } from '../../context/AttachmentContext.mjs';
|
|
17
19
|
|
|
18
20
|
const { Button, Icon, Label: LabelElement, TextArea, View, } = AIConversationElements;
|
|
19
21
|
const FIELD_BLOCK = 'ai-field';
|
|
@@ -98,6 +100,7 @@ const InputContainer = withBaseElementProps(View, {
|
|
|
98
100
|
const FieldControl = () => {
|
|
99
101
|
const { input, setInput } = React__default.useContext(ConversationInputContext);
|
|
100
102
|
const handleSendMessage = React__default.useContext(SendMessageContext);
|
|
103
|
+
const allowAttachments = React__default.useContext(AttachmentContext);
|
|
101
104
|
const ref = React__default.useRef(null);
|
|
102
105
|
const responseComponents = React__default.useContext(ResponseComponentsContext);
|
|
103
106
|
const controls = React__default.useContext(ControlsContext);
|
|
@@ -116,7 +119,7 @@ const FieldControl = () => {
|
|
|
116
119
|
const fileContent = {
|
|
117
120
|
image: {
|
|
118
121
|
format: getImageTypeFromMimeType(file.type),
|
|
119
|
-
source: { bytes: Uint8Array
|
|
122
|
+
source: { bytes: new Uint8Array(buffer) },
|
|
120
123
|
},
|
|
121
124
|
};
|
|
122
125
|
submittedContent.push(fileContent);
|
|
@@ -146,10 +149,10 @@ const FieldControl = () => {
|
|
|
146
149
|
}
|
|
147
150
|
};
|
|
148
151
|
if (controls?.Form) {
|
|
149
|
-
return (React__default.createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput }));
|
|
152
|
+
return (React__default.createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, allowAttachments: allowAttachments }));
|
|
150
153
|
}
|
|
151
154
|
return (React__default.createElement("form", { className: `${FIELD_BLOCK}__form`, onSubmit: handleSubmit, method: "post", ref: ref },
|
|
152
|
-
React__default.createElement(AttachFileControl, null),
|
|
155
|
+
allowAttachments ? React__default.createElement(AttachFileControl, null) : null,
|
|
153
156
|
React__default.createElement(InputContainer, null,
|
|
154
157
|
React__default.createElement(VisuallyHidden, null,
|
|
155
158
|
React__default.createElement(Label, null)),
|
|
@@ -6,12 +6,15 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
6
6
|
import { RoleContext, MessagesContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import { MessageVariantContext } from '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import { useConversationDisplayText } from '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import { ResponseComponentsContext, RESPONSE_COMPONENT_PREFIX } from '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
|
-
import { convertBufferToBase64
|
|
15
|
+
import { convertBufferToBase64 } from '../../utils.mjs';
|
|
11
16
|
import { ActionsBarControl } from './ActionsBarControl.mjs';
|
|
12
17
|
import { AvatarControl } from './AvatarControl.mjs';
|
|
13
|
-
import { ResponseComponentsContext } from '../../context/ResponseComponentsContext.mjs';
|
|
14
|
-
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
15
18
|
|
|
16
19
|
const { Image, Span, Text, View } = AIConversationElements;
|
|
17
20
|
const MESSAGES_BLOCK = 'ai-messages';
|
|
@@ -43,7 +46,9 @@ const MessageControl = ({ message }) => {
|
|
|
43
46
|
else if (content.toolUse) {
|
|
44
47
|
// For now tool use is limited to custom response components
|
|
45
48
|
const { name, input } = content.toolUse;
|
|
46
|
-
if (!responseComponents ||
|
|
49
|
+
if (!responseComponents ||
|
|
50
|
+
!name ||
|
|
51
|
+
!name.startsWith(RESPONSE_COMPONENT_PREFIX)) {
|
|
47
52
|
return;
|
|
48
53
|
}
|
|
49
54
|
else {
|
|
@@ -81,6 +86,7 @@ const Layout = React__default.forwardRef(function Layout(props, ref) {
|
|
|
81
86
|
const MessagesControl = ({ renderMessage }) => {
|
|
82
87
|
const messages = React__default.useContext(MessagesContext);
|
|
83
88
|
const controls = React__default.useContext(ControlsContext);
|
|
89
|
+
const { getMessageTimestampText } = useConversationDisplayText();
|
|
84
90
|
const messagesRef = React__default.useRef([]);
|
|
85
91
|
const [focusedItemIndex, setFocusedItemIndex] = React__default.useState(messages ? messages.length - 1 : 0);
|
|
86
92
|
const handleFocus = (index) => setFocusedItemIndex(index);
|
|
@@ -113,13 +119,16 @@ const MessagesControl = ({ renderMessage }) => {
|
|
|
113
119
|
if (controls?.MessageList) {
|
|
114
120
|
return React__default.createElement(controls.MessageList, { messages: messages });
|
|
115
121
|
}
|
|
116
|
-
|
|
122
|
+
const messagesWithRenderableContent = messages?.filter((message) => message.content.some((content) => content.image ??
|
|
123
|
+
content.text ??
|
|
124
|
+
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
125
|
+
return (React__default.createElement(Layout, null, messagesWithRenderableContent?.map((message, index) => {
|
|
117
126
|
return renderMessage ? (renderMessage(message)) : (React__default.createElement(RoleContext.Provider, { value: message.role, key: `message-${index}` },
|
|
118
127
|
React__default.createElement(MessageContainer, { "data-testid": `message`, key: `message-${index}`, tabIndex: focusedItemIndex === index ? 0 : -1, onFocus: () => handleFocus(index), onKeyDown: (event) => onKeyDown(index, event), ref: (el) => (messagesRef.current[index] = el) },
|
|
119
128
|
React__default.createElement(HeaderContainer, null,
|
|
120
129
|
React__default.createElement(AvatarControl, null),
|
|
121
130
|
React__default.createElement(Separator, null),
|
|
122
|
-
React__default.createElement(Timestamp, null,
|
|
131
|
+
React__default.createElement(Timestamp, null, getMessageTimestampText(new Date(message.createdAt)))),
|
|
123
132
|
React__default.createElement(MessageControl, { message: message }),
|
|
124
133
|
message.role === 'assistant' ? (React__default.createElement(ActionsBarControl, { message: message, focusable: focusedItemIndex === index })) : null)));
|
|
125
134
|
})));
|
|
@@ -6,9 +6,13 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import { MessagesContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import { SuggestedPromptsContext } from '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
import { classNames } from '@aws-amplify/ui';
|
|
11
|
-
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
12
16
|
|
|
13
17
|
const { View, Button, Text, Heading, Icon } = AIConversationElements;
|
|
14
18
|
const PROMPT_BLOCK = 'ai-prompts';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { View, Button, VisuallyHidden, TextAreaField, DropZone } from '@aws-amplify/ui-react';
|
|
3
3
|
import { useIcons, IconSend, IconAttach } from '@aws-amplify/ui-react/internal';
|
|
4
4
|
import { ComponentClassName } from '@aws-amplify/ui';
|
|
5
5
|
import { Attachments } from './Attachments.mjs';
|
|
@@ -8,22 +8,37 @@ import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
|
8
8
|
function isHTMLFormElement(target) {
|
|
9
9
|
return 'form' in target;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Will conditionally render the DropZone if allowAttachments
|
|
13
|
+
* is true
|
|
14
|
+
*/
|
|
15
|
+
const FormWrapper = ({ children, allowAttachments, setInput, }) => {
|
|
16
|
+
if (allowAttachments) {
|
|
17
|
+
return (React.createElement(DropZone, { className: ComponentClassName.AIConversationFormDropzone, onDropComplete: ({ acceptedFiles }) => {
|
|
18
|
+
setInput?.((prevInput) => ({
|
|
19
|
+
...prevInput,
|
|
20
|
+
files: [...(prevInput?.files ?? []), ...acceptedFiles],
|
|
21
|
+
}));
|
|
22
|
+
} }, children));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return children;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const Form = ({ setInput, input, handleSubmit, allowAttachments, }) => {
|
|
12
29
|
const icons = useIcons('aiConversation');
|
|
13
30
|
const sendIcon = icons?.send ?? React.createElement(IconSend, null);
|
|
14
31
|
const attachIcon = icons?.attach ?? React.createElement(IconAttach, null);
|
|
15
32
|
const hiddenInput = React.useRef(null);
|
|
16
33
|
const isLoading = React.useContext(LoadingContext);
|
|
17
34
|
const isInputEmpty = !input?.text?.length && !input?.files?.length;
|
|
18
|
-
return (React.createElement(
|
|
19
|
-
setInput((prevInput) => ({
|
|
20
|
-
...prevInput,
|
|
21
|
-
files: [...(prevInput?.files ?? []), ...acceptedFiles],
|
|
22
|
-
}));
|
|
23
|
-
} },
|
|
35
|
+
return (React.createElement(FormWrapper, { allowAttachments: allowAttachments, setInput: setInput },
|
|
24
36
|
React.createElement(View, { as: "form", className: ComponentClassName.AIConversationForm, onSubmit: handleSubmit },
|
|
25
|
-
React.createElement(Button, { className: ComponentClassName.AIConversationFormAttach, onClick: () => {
|
|
37
|
+
allowAttachments ? (React.createElement(Button, { className: ComponentClassName.AIConversationFormAttach, onClick: () => {
|
|
26
38
|
hiddenInput?.current?.click();
|
|
39
|
+
if (hiddenInput?.current) {
|
|
40
|
+
hiddenInput.current.value = '';
|
|
41
|
+
}
|
|
27
42
|
} },
|
|
28
43
|
React.createElement("span", null, attachIcon),
|
|
29
44
|
React.createElement(VisuallyHidden, null,
|
|
@@ -36,8 +51,8 @@ const Form = ({ setInput, input, handleSubmit, }) => {
|
|
|
36
51
|
...prevValue,
|
|
37
52
|
files: [...(prevValue?.files ?? []), ...Array.from(files)],
|
|
38
53
|
}));
|
|
39
|
-
}, multiple: true, accept: "*" }))),
|
|
40
|
-
React.createElement(TextAreaField, { className: ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', onKeyDown: (e) => {
|
|
54
|
+
}, multiple: true, accept: "*", "data-testid": "hidden-file-input" })))) : null,
|
|
55
|
+
React.createElement(TextAreaField, { className: ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', testId: "text-input", onKeyDown: (e) => {
|
|
41
56
|
// Submit on enter key if shift is not pressed also
|
|
42
57
|
const shouldSubmit = !e.shiftKey && e.key === 'Enter';
|
|
43
58
|
if (shouldSubmit && isHTMLFormElement(e.target)) {
|
|
@@ -7,19 +7,35 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
7
7
|
import { RoleContext } from '../../context/MessagesContext.mjs';
|
|
8
8
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
9
9
|
import { MessageVariantContext } from '../../context/MessageVariantContext.mjs';
|
|
10
|
+
import { useConversationDisplayText } from '../../context/DisplayTextContext.mjs';
|
|
11
|
+
import '../../context/ControlsContext.mjs';
|
|
12
|
+
import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
13
|
+
import { RESPONSE_COMPONENT_PREFIX } from '../../context/ResponseComponentsContext.mjs';
|
|
14
|
+
import '../../context/SendMessageContext.mjs';
|
|
10
15
|
import '../../context/elements/definitions.mjs';
|
|
11
|
-
import { formatDate } from '../../utils.mjs';
|
|
12
16
|
import { ComponentClassName, classNames, classNameModifier } from '@aws-amplify/ui';
|
|
13
17
|
|
|
14
18
|
const MessageMeta = ({ message }) => {
|
|
15
19
|
// need to pass this in as props in order for it to be overridable
|
|
16
20
|
const avatars = React.useContext(AvatarsContext);
|
|
17
21
|
const role = React.useContext(RoleContext);
|
|
22
|
+
const { getMessageTimestampText } = useConversationDisplayText();
|
|
18
23
|
// maybe rename 'avatar' to something else
|
|
19
24
|
const avatar = role === 'assistant' ? avatars?.ai : avatars?.user;
|
|
20
25
|
return (React.createElement(View, { className: ComponentClassName.AIConversationMessageSender },
|
|
21
26
|
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username),
|
|
22
|
-
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderTimestamp },
|
|
27
|
+
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderTimestamp }, getMessageTimestampText(new Date(message.createdAt)))));
|
|
28
|
+
};
|
|
29
|
+
const LoadingMessage = () => {
|
|
30
|
+
const avatars = React.useContext(AvatarsContext);
|
|
31
|
+
const variant = React.useContext(MessageVariantContext);
|
|
32
|
+
const avatar = avatars?.ai;
|
|
33
|
+
return (React.createElement(View, { className: classNames(ComponentClassName.AIConversationMessage, classNameModifier(ComponentClassName.AIConversationMessage, variant), classNameModifier(ComponentClassName.AIConversationMessage, 'assistant')) },
|
|
34
|
+
React.createElement(View, { className: ComponentClassName.AIConversationMessageAvatar },
|
|
35
|
+
React.createElement(Avatar, { isLoading: true }, avatar?.avatar)),
|
|
36
|
+
React.createElement(View, { className: ComponentClassName.AIConversationMessageBody },
|
|
37
|
+
React.createElement(View, { className: ComponentClassName.AIConversationMessageSender },
|
|
38
|
+
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username)))));
|
|
23
39
|
};
|
|
24
40
|
const Message = ({ message }) => {
|
|
25
41
|
const avatars = React.useContext(AvatarsContext);
|
|
@@ -35,7 +51,13 @@ const Message = ({ message }) => {
|
|
|
35
51
|
React.createElement(MessageControl, { message: message }))))));
|
|
36
52
|
};
|
|
37
53
|
const MessageList = ({ messages, }) => {
|
|
38
|
-
|
|
54
|
+
const isLoading = React.useContext(LoadingContext);
|
|
55
|
+
const messagesWithRenderableContent = messages?.filter((message) => message.content.some((content) => content.image ??
|
|
56
|
+
content.text ??
|
|
57
|
+
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
58
|
+
return (React.createElement(View, { className: ComponentClassName.AIConversationMessageList },
|
|
59
|
+
messagesWithRenderableContent.map((message, i) => (React.createElement(Message, { key: `message-${i}`, message: message }))),
|
|
60
|
+
isLoading ? React.createElement(LoadingMessage, null) : null));
|
|
39
61
|
};
|
|
40
62
|
|
|
41
63
|
export { MessageList };
|