@aws-amplify/ui-react-ai 0.0.0-storage-browser-alpha-5147028-20240909221818 → 0.0.0-storage-browser-alpha-b7f723c-20241001204651

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/esm/components/AIConversation/AIConversation.mjs +3 -1
  2. package/dist/esm/components/AIConversation/context/AttachmentContext.mjs +8 -0
  3. package/dist/esm/components/AIConversation/context/DisplayTextContext.mjs +9 -0
  4. package/dist/esm/components/AIConversation/context/ResponseComponentsContext.mjs +10 -2
  5. package/dist/esm/components/AIConversation/createAIConversation.mjs +3 -1
  6. package/dist/esm/components/AIConversation/createProvider.mjs +21 -11
  7. package/dist/esm/components/AIConversation/displayText.mjs +7 -0
  8. package/dist/esm/components/AIConversation/views/Controls/ActionsBarControl.mjs +5 -0
  9. package/dist/esm/components/AIConversation/views/Controls/AttachFileControl.mjs +18 -1
  10. package/dist/esm/components/AIConversation/views/Controls/AttachmentListControl.mjs +5 -0
  11. package/dist/esm/components/AIConversation/views/Controls/AvatarControl.mjs +5 -0
  12. package/dist/esm/components/AIConversation/views/Controls/FieldControl.mjs +10 -7
  13. package/dist/esm/components/AIConversation/views/Controls/MessagesControl.mjs +15 -6
  14. package/dist/esm/components/AIConversation/views/Controls/PromptControl.mjs +5 -1
  15. package/dist/esm/components/AIConversation/views/default/Form.mjs +26 -11
  16. package/dist/esm/components/AIConversation/views/default/MessageList.mjs +25 -3
  17. package/dist/index.js +366 -286
  18. package/dist/types/components/AIConversation/AIConversation.d.ts +5 -4
  19. package/dist/types/components/AIConversation/context/AttachmentContext.d.ts +6 -0
  20. package/dist/types/components/AIConversation/context/ControlsContext.d.ts +1 -0
  21. package/dist/types/components/AIConversation/context/DisplayTextContext.d.ts +5 -0
  22. package/dist/types/components/AIConversation/context/ResponseComponentsContext.d.ts +1 -0
  23. package/dist/types/components/AIConversation/context/index.d.ts +11 -7
  24. package/dist/types/components/AIConversation/createProvider.d.ts +2 -2
  25. package/dist/types/components/AIConversation/displayText.d.ts +2 -2
  26. package/dist/types/components/AIConversation/index.d.ts +2 -1
  27. package/dist/types/components/AIConversation/types.d.ts +1 -0
  28. package/dist/types/components/AIConversation/views/default/Form.d.ts +1 -1
  29. package/dist/types/index.d.ts +3 -3
  30. 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
- return (React__default.createElement(ResponseComponentsContext.Provider, { value: responseComponents }, children));
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 { ResponseComponentsProvider } from './context/ResponseComponentsContext.mjs';
10
- import { SendMessageContextProvider } from './context/SendMessageContext.mjs';
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(ConversationInputContextProvider, null,
21
- React__default.createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
22
- React__default.createElement(AvatarsProvider, { avatars: avatars },
23
- React__default.createElement(ActionsProvider, { actions: actions },
24
- React__default.createElement(MessageVariantProvider, { variant: variant },
25
- React__default.createElement(MessagesProvider, { messages: messages },
26
- React__default.createElement(LoadingContextProvider, { isLoading: isLoading }, children))))))))))));
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
 
@@ -0,0 +1,7 @@
1
+ import { formatDate } from './utils.mjs';
2
+
3
+ const defaultAIConversationDisplayTextEn = {
4
+ getMessageTimestampText: (date) => formatDate(date),
5
+ };
6
+
7
+ export { defaultAIConversationDisplayTextEn };
@@ -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, null,
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 { LoadingContext } from '../../context/LoadingContext.mjs';
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.from(Buffer.from(buffer)) },
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, formatDate } from '../../utils.mjs';
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 || !name) {
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
- return (React__default.createElement(Layout, null, messages?.map((message, index) => {
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, formatDate(new Date(message.createdAt)))),
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 { DropZone, View, Button, VisuallyHidden, TextAreaField } from '@aws-amplify/ui-react';
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
- const Form = ({ setInput, input, handleSubmit, }) => {
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(DropZone, { className: ComponentClassName.AIConversationFormDropzone, onDropComplete: ({ acceptedFiles }) => {
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 }, formatDate(new Date(message.createdAt)))));
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
- return (React.createElement(View, { className: ComponentClassName.AIConversationMessageList }, messages.map((message, i) => (React.createElement(Message, { key: `message-${i}`, message: message })))));
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 };