@aws-amplify/ui-react-ai 1.0.0 → 1.2.0
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/AIConversationProvider.mjs +2 -2
- package/dist/esm/components/AIConversation/context/AttachmentContext.mjs +12 -3
- package/dist/esm/components/AIConversation/context/ConversationInputContext.mjs +2 -1
- package/dist/esm/components/AIConversation/context/elements/IconElement.mjs +2 -2
- package/dist/esm/components/AIConversation/context/elements/definitions.mjs +12 -12
- package/dist/esm/components/AIConversation/displayText.mjs +6 -0
- package/dist/esm/components/AIConversation/utils.mjs +42 -13
- package/dist/esm/components/AIConversation/views/Controls/FormControl.mjs +35 -6
- package/dist/esm/components/AIConversation/views/Controls/MessagesControl.mjs +3 -1
- package/dist/esm/components/AIConversation/views/default/Form.mjs +10 -16
- package/dist/esm/hooks/useAIConversation.mjs +1 -1
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +121 -51
- package/dist/types/components/AIConversation/AIConversationProvider.d.ts +1 -1
- package/dist/types/components/AIConversation/context/AIContextContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/ActionsContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/AttachmentContext.d.ts +5 -5
- package/dist/types/components/AIConversation/context/AvatarsContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/ControlsContext.d.ts +5 -4
- package/dist/types/components/AIConversation/context/ConversationInputContext.d.ts +5 -3
- package/dist/types/components/AIConversation/context/DisplayTextContext.d.ts +1 -2
- package/dist/types/components/AIConversation/context/FallbackComponentContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/LoadingContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/MessageRenderContext.d.ts +1 -2
- package/dist/types/components/AIConversation/context/MessageVariantContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/MessagesContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/ResponseComponentsContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/SendMessageContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/SuggestedPromptsContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/WelcomeMessageContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/elements/IconElement.d.ts +2 -2
- package/dist/types/components/AIConversation/context/elements/definitions.d.ts +11 -12
- package/dist/types/components/AIConversation/context/index.d.ts +2 -2
- package/dist/types/components/AIConversation/displayText.d.ts +2 -0
- package/dist/types/components/AIConversation/types.d.ts +6 -4
- package/dist/types/components/AIConversation/utils.d.ts +10 -0
- package/dist/types/components/AIConversation/views/Controls/AttachmentListControl.d.ts +5 -5
- package/dist/types/components/AIConversation/views/Controls/DefaultMessageControl.d.ts +2 -2
- package/dist/types/components/AIConversation/views/Controls/MessagesControl.d.ts +3 -3
- package/dist/types/components/AIConversation/views/default/Attachments.d.ts +4 -4
- package/dist/types/types.d.ts +0 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +8 -8
|
@@ -18,7 +18,7 @@ import { WelcomeMessageProvider } from './context/WelcomeMessageContext.mjs';
|
|
|
18
18
|
import { FallbackComponentProvider } from './context/FallbackComponentContext.mjs';
|
|
19
19
|
import './context/elements/definitions.mjs';
|
|
20
20
|
|
|
21
|
-
const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }) => {
|
|
21
|
+
const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, maxAttachmentSize, maxAttachments, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }) => {
|
|
22
22
|
const _displayText = {
|
|
23
23
|
...defaultAIConversationDisplayTextEn,
|
|
24
24
|
...displayText,
|
|
@@ -29,7 +29,7 @@ const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars,
|
|
|
29
29
|
React__default.createElement(FallbackComponentProvider, { FallbackComponent: FallbackResponseComponent },
|
|
30
30
|
React__default.createElement(MessageRendererProvider, { ...messageRenderer },
|
|
31
31
|
React__default.createElement(ResponseComponentsProvider, { responseComponents: responseComponents },
|
|
32
|
-
React__default.createElement(AttachmentProvider, { allowAttachments: allowAttachments },
|
|
32
|
+
React__default.createElement(AttachmentProvider, { allowAttachments: allowAttachments, maxAttachmentSize: maxAttachmentSize, maxAttachments: maxAttachments },
|
|
33
33
|
React__default.createElement(ConversationDisplayTextProvider, { ..._displayText },
|
|
34
34
|
React__default.createElement(ConversationInputContextProvider, null,
|
|
35
35
|
React__default.createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
const AttachmentContext = React.createContext(
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const AttachmentContext = React.createContext({
|
|
4
|
+
allowAttachments: false,
|
|
5
|
+
// We save attachments as base64 strings into dynamodb for conversation history
|
|
6
|
+
// DynamoDB has a max size of 400kb for records
|
|
7
|
+
// This can be overridden so cutsomers could provide a lower number
|
|
8
|
+
// or a higher number if in the future we support larger sizes.
|
|
9
|
+
maxAttachmentSize: 400000,
|
|
10
|
+
maxAttachments: 20,
|
|
11
|
+
});
|
|
12
|
+
const AttachmentProvider = ({ children, allowAttachments = false, maxAttachmentSize = 400000, maxAttachments = 20, }) => {
|
|
13
|
+
const providerValue = React.useMemo(() => ({ maxAttachmentSize, maxAttachments, allowAttachments }), [maxAttachmentSize, maxAttachments, allowAttachments]);
|
|
14
|
+
return (React.createElement(AttachmentContext.Provider, { value: providerValue }, children));
|
|
6
15
|
};
|
|
7
16
|
|
|
8
17
|
export { AttachmentContext, AttachmentProvider };
|
|
@@ -3,7 +3,8 @@ import React__default from 'react';
|
|
|
3
3
|
const ConversationInputContext = React__default.createContext({});
|
|
4
4
|
const ConversationInputContextProvider = ({ children, }) => {
|
|
5
5
|
const [input, setInput] = React__default.useState();
|
|
6
|
-
const
|
|
6
|
+
const [error, setError] = React__default.useState();
|
|
7
|
+
const providerValue = React__default.useMemo(() => ({ input, setInput, error, setError }), [input, setInput, error, setError]);
|
|
7
8
|
return (React__default.createElement(ConversationInputContext.Provider, { value: providerValue }, children));
|
|
8
9
|
};
|
|
9
10
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineBaseElementWithRef, withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
|
|
2
2
|
import React__default from 'react';
|
|
3
3
|
|
|
4
4
|
const DEFAULT_ICON_PATHS = {
|
|
@@ -17,7 +17,7 @@ const DEFAULT_ICON_ATTRIBUTES = {
|
|
|
17
17
|
fill: 'none',
|
|
18
18
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
19
19
|
};
|
|
20
|
-
const BaseIconElement =
|
|
20
|
+
const BaseIconElement = defineBaseElementWithRef({
|
|
21
21
|
type: 'svg',
|
|
22
22
|
displayName: 'Icon',
|
|
23
23
|
});
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineBaseElementWithRef } from '@aws-amplify/ui-react-core/elements';
|
|
2
2
|
import { IconElement } from './IconElement.mjs';
|
|
3
3
|
|
|
4
|
-
const LabelElement =
|
|
4
|
+
const LabelElement = defineBaseElementWithRef({
|
|
5
5
|
type: 'label',
|
|
6
6
|
displayName: 'Label',
|
|
7
7
|
});
|
|
8
|
-
const TextElement =
|
|
8
|
+
const TextElement = defineBaseElementWithRef({
|
|
9
9
|
type: 'p',
|
|
10
10
|
displayName: 'Text',
|
|
11
11
|
});
|
|
12
|
-
const UnorderedListElement =
|
|
12
|
+
const UnorderedListElement = defineBaseElementWithRef({
|
|
13
13
|
type: 'ul',
|
|
14
14
|
displayName: 'UnorderedList',
|
|
15
15
|
});
|
|
16
|
-
const ListItemElement =
|
|
16
|
+
const ListItemElement = defineBaseElementWithRef({
|
|
17
17
|
type: 'li',
|
|
18
18
|
displayName: 'ListItem',
|
|
19
19
|
});
|
|
20
|
-
const HeadingElement =
|
|
20
|
+
const HeadingElement = defineBaseElementWithRef({
|
|
21
21
|
type: 'h2',
|
|
22
22
|
displayName: 'Title',
|
|
23
23
|
});
|
|
24
|
-
const ImageElement =
|
|
24
|
+
const ImageElement = defineBaseElementWithRef({
|
|
25
25
|
type: 'img',
|
|
26
26
|
displayName: 'Image',
|
|
27
27
|
});
|
|
28
|
-
const InputElement =
|
|
28
|
+
const InputElement = defineBaseElementWithRef({
|
|
29
29
|
type: 'input',
|
|
30
30
|
displayName: 'Input',
|
|
31
31
|
});
|
|
32
|
-
const ButtonElement =
|
|
33
|
-
const ViewElement =
|
|
32
|
+
const ButtonElement = defineBaseElementWithRef({ type: 'button', displayName: 'Button' });
|
|
33
|
+
const ViewElement = defineBaseElementWithRef({
|
|
34
34
|
type: 'div',
|
|
35
35
|
displayName: 'View',
|
|
36
36
|
});
|
|
37
|
-
const SpanElement =
|
|
37
|
+
const SpanElement = defineBaseElementWithRef({
|
|
38
38
|
type: 'span',
|
|
39
39
|
displayName: 'Span',
|
|
40
40
|
});
|
|
41
|
-
const TextAreaElement =
|
|
41
|
+
const TextAreaElement = defineBaseElementWithRef({
|
|
42
42
|
type: 'textarea',
|
|
43
43
|
displayName: 'TextArea',
|
|
44
44
|
});
|
|
@@ -2,6 +2,12 @@ import { formatDate } from './utils.mjs';
|
|
|
2
2
|
|
|
3
3
|
const defaultAIConversationDisplayTextEn = {
|
|
4
4
|
getMessageTimestampText: (date) => formatDate(date),
|
|
5
|
+
getMaxAttachmentErrorText(count) {
|
|
6
|
+
return `Cannot choose more than ${count} ${count === 1 ? 'file' : 'files'}. `;
|
|
7
|
+
},
|
|
8
|
+
getAttachmentSizeErrorText(sizeText) {
|
|
9
|
+
return `File size must be below ${sizeText}.`;
|
|
10
|
+
},
|
|
5
11
|
};
|
|
6
12
|
|
|
7
13
|
export { defaultAIConversationDisplayTextEn };
|
|
@@ -12,28 +12,57 @@ function formatDate(date) {
|
|
|
12
12
|
return `${dateString} at ${timeString}`;
|
|
13
13
|
}
|
|
14
14
|
function arrayBufferToBase64(buffer) {
|
|
15
|
-
let binary = '';
|
|
16
|
-
const bytes = new Uint8Array(buffer);
|
|
17
|
-
const len = bytes.byteLength;
|
|
18
|
-
for (let i = 0; i < len; i++) {
|
|
19
|
-
binary += String.fromCharCode(bytes[i]);
|
|
20
|
-
}
|
|
21
|
-
return window.btoa(binary);
|
|
22
|
-
}
|
|
23
|
-
function convertBufferToBase64(buffer, format) {
|
|
24
|
-
let base64string = '';
|
|
25
15
|
// Use node-based buffer if available
|
|
26
16
|
// fall back on browser if not
|
|
27
17
|
if (typeof Buffer !== 'undefined') {
|
|
28
|
-
|
|
18
|
+
return Buffer.from(new Uint8Array(buffer)).toString('base64');
|
|
29
19
|
}
|
|
30
20
|
else {
|
|
31
|
-
|
|
21
|
+
let binary = '';
|
|
22
|
+
const bytes = new Uint8Array(buffer);
|
|
23
|
+
const len = bytes.byteLength;
|
|
24
|
+
for (let i = 0; i < len; i++) {
|
|
25
|
+
binary += String.fromCharCode(bytes[i]);
|
|
26
|
+
}
|
|
27
|
+
return window.btoa(binary);
|
|
32
28
|
}
|
|
29
|
+
}
|
|
30
|
+
function convertBufferToBase64(buffer, format) {
|
|
31
|
+
const base64string = arrayBufferToBase64(buffer);
|
|
33
32
|
return `data:image/${format};base64,${base64string}`;
|
|
34
33
|
}
|
|
35
34
|
function getImageTypeFromMimeType(mimeType) {
|
|
36
35
|
return mimeType.split('/')[1];
|
|
37
36
|
}
|
|
37
|
+
async function attachmentsValidator({ files, maxAttachments, maxAttachmentSize, }) {
|
|
38
|
+
const acceptedFiles = [];
|
|
39
|
+
const rejectedFiles = [];
|
|
40
|
+
let hasMaxSizeError = false;
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
43
|
+
const base64 = arrayBufferToBase64(arrayBuffer);
|
|
44
|
+
if (base64.length < maxAttachmentSize) {
|
|
45
|
+
acceptedFiles.push(file);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
rejectedFiles.push(file);
|
|
49
|
+
hasMaxSizeError = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (acceptedFiles.length > maxAttachments) {
|
|
53
|
+
return {
|
|
54
|
+
acceptedFiles: acceptedFiles.slice(0, maxAttachments),
|
|
55
|
+
rejectedFiles: [...acceptedFiles.slice(maxAttachments), ...rejectedFiles],
|
|
56
|
+
hasMaxAttachmentsError: true,
|
|
57
|
+
hasMaxAttachmentSizeError: hasMaxSizeError,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
acceptedFiles,
|
|
62
|
+
rejectedFiles,
|
|
63
|
+
hasMaxAttachmentsError: false,
|
|
64
|
+
hasMaxAttachmentSizeError: hasMaxSizeError,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
38
67
|
|
|
39
|
-
export { convertBufferToBase64, formatDate, getImageTypeFromMimeType };
|
|
68
|
+
export { attachmentsValidator, convertBufferToBase64, formatDate, getImageTypeFromMimeType };
|
|
@@ -7,7 +7,7 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
7
7
|
import { MessagesContext } from '../../context/MessagesContext.mjs';
|
|
8
8
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
9
9
|
import '../../context/MessageVariantContext.mjs';
|
|
10
|
-
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { useConversationDisplayText } from '../../context/DisplayTextContext.mjs';
|
|
11
11
|
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
12
12
|
import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
13
13
|
import { ResponseComponentsContext, convertResponseComponentsToToolConfiguration } from '../../context/ResponseComponentsContext.mjs';
|
|
@@ -19,8 +19,8 @@ import '../../context/FallbackComponentContext.mjs';
|
|
|
19
19
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
20
20
|
import { AttachFileControl } from './AttachFileControl.mjs';
|
|
21
21
|
import { AttachmentListControl } from './AttachmentListControl.mjs';
|
|
22
|
-
import { getImageTypeFromMimeType } from '../../utils.mjs';
|
|
23
|
-
import { isFunction } from '@aws-amplify/ui';
|
|
22
|
+
import { attachmentsValidator, getImageTypeFromMimeType } from '../../utils.mjs';
|
|
23
|
+
import { humanFileSize, isFunction } from '@aws-amplify/ui';
|
|
24
24
|
|
|
25
25
|
const { Button, Icon, Label: LabelElement, TextArea, View, } = AIConversationElements;
|
|
26
26
|
const FIELD_BLOCK = 'ai-field';
|
|
@@ -103,9 +103,10 @@ const InputContainer = withBaseElementProps(View, {
|
|
|
103
103
|
className: `${FIELD_BLOCK}__input-container`,
|
|
104
104
|
});
|
|
105
105
|
const FormControl = () => {
|
|
106
|
-
const { input, setInput } = React__default.useContext(ConversationInputContext);
|
|
106
|
+
const { input, setInput, error, setError } = React__default.useContext(ConversationInputContext);
|
|
107
107
|
const handleSendMessage = React__default.useContext(SendMessageContext);
|
|
108
|
-
const allowAttachments = React__default.useContext(AttachmentContext);
|
|
108
|
+
const { allowAttachments, maxAttachmentSize, maxAttachments } = React__default.useContext(AttachmentContext);
|
|
109
|
+
const displayText = useConversationDisplayText();
|
|
109
110
|
const responseComponents = React__default.useContext(ResponseComponentsContext);
|
|
110
111
|
const isLoading = React__default.useContext(LoadingContext);
|
|
111
112
|
const aiContext = React__default.useContext(AIContextContext);
|
|
@@ -157,8 +158,36 @@ const FormControl = () => {
|
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
};
|
|
161
|
+
const onValidate = React__default.useCallback(async (files) => {
|
|
162
|
+
const previousFiles = input?.files ?? [];
|
|
163
|
+
const { acceptedFiles, hasMaxAttachmentsError, hasMaxAttachmentSizeError, } = await attachmentsValidator({
|
|
164
|
+
files: [...files, ...previousFiles],
|
|
165
|
+
maxAttachments,
|
|
166
|
+
maxAttachmentSize,
|
|
167
|
+
});
|
|
168
|
+
if (hasMaxAttachmentsError || hasMaxAttachmentSizeError) {
|
|
169
|
+
const errors = [];
|
|
170
|
+
if (hasMaxAttachmentsError) {
|
|
171
|
+
errors.push(displayText.getMaxAttachmentErrorText(maxAttachments));
|
|
172
|
+
}
|
|
173
|
+
if (hasMaxAttachmentSizeError) {
|
|
174
|
+
errors.push(displayText.getAttachmentSizeErrorText(
|
|
175
|
+
// base64 size is about 137% that of the file size
|
|
176
|
+
// https://en.wikipedia.org/wiki/Base64#MIME
|
|
177
|
+
humanFileSize((maxAttachmentSize - 814) / 1.37, true)));
|
|
178
|
+
}
|
|
179
|
+
setError?.(errors.join(' '));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
setError?.(undefined);
|
|
183
|
+
}
|
|
184
|
+
setInput?.((prevValue) => ({
|
|
185
|
+
...prevValue,
|
|
186
|
+
files: acceptedFiles,
|
|
187
|
+
}));
|
|
188
|
+
}, [setInput, input, displayText, maxAttachmentSize, maxAttachments, setError]);
|
|
160
189
|
if (controls?.Form) {
|
|
161
|
-
return (React__default.createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, allowAttachments: allowAttachments, isLoading: isLoading }));
|
|
190
|
+
return (React__default.createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, onValidate: onValidate, allowAttachments: allowAttachments, isLoading: isLoading, error: error, setError: setError }));
|
|
162
191
|
}
|
|
163
192
|
return (React__default.createElement("form", { className: `${FIELD_BLOCK}__form`, onSubmit: handleSubmit, method: "post", ref: ref },
|
|
164
193
|
allowAttachments ? React__default.createElement(AttachFileControl, null) : null,
|
|
@@ -126,7 +126,9 @@ const MessagesControl = () => {
|
|
|
126
126
|
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
127
127
|
return (React__default.createElement(Layout, null, messagesWithRenderableContent?.map((message, index) => {
|
|
128
128
|
return (React__default.createElement(RoleContext.Provider, { value: message.role, key: `message-${index}` },
|
|
129
|
-
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) =>
|
|
129
|
+
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) => {
|
|
130
|
+
messagesRef.current[index] = el;
|
|
131
|
+
} },
|
|
130
132
|
React__default.createElement(HeaderContainer, null,
|
|
131
133
|
React__default.createElement(AvatarControl, null),
|
|
132
134
|
React__default.createElement(Timestamp, null, getMessageTimestampText(new Date(message.createdAt)))),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { View, Button, VisuallyHidden, TextAreaField, DropZone } from '@aws-amplify/ui-react';
|
|
2
|
+
import { View, Button, VisuallyHidden, TextAreaField, Message, 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';
|
|
@@ -11,27 +11,24 @@ function isHTMLFormElement(target) {
|
|
|
11
11
|
* Will conditionally render the DropZone if allowAttachments
|
|
12
12
|
* is true
|
|
13
13
|
*/
|
|
14
|
-
const FormWrapper = ({ children, allowAttachments,
|
|
14
|
+
const FormWrapper = ({ children, allowAttachments, onValidate, }) => {
|
|
15
15
|
if (allowAttachments) {
|
|
16
16
|
return (React.createElement(DropZone, { className: ComponentClassName.AIConversationFormDropzone, onDropComplete: ({ acceptedFiles }) => {
|
|
17
|
-
|
|
18
|
-
...prevInput,
|
|
19
|
-
files: [...(prevInput?.files ?? []), ...acceptedFiles],
|
|
20
|
-
}));
|
|
17
|
+
onValidate(acceptedFiles);
|
|
21
18
|
} }, children));
|
|
22
19
|
}
|
|
23
20
|
else {
|
|
24
21
|
return children;
|
|
25
22
|
}
|
|
26
23
|
};
|
|
27
|
-
const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) => {
|
|
24
|
+
const Form = ({ setInput, input, handleSubmit, allowAttachments, onValidate, isLoading, error, }) => {
|
|
28
25
|
const icons = useIcons('aiConversation');
|
|
29
26
|
const sendIcon = icons?.send ?? React.createElement(IconSend, null);
|
|
30
27
|
const attachIcon = icons?.attach ?? React.createElement(IconAttach, null);
|
|
31
28
|
const hiddenInput = React.useRef(null);
|
|
32
29
|
const [composing, setComposing] = React.useState(false);
|
|
33
30
|
const isInputEmpty = !input?.text?.length && !input?.files?.length;
|
|
34
|
-
return (React.createElement(FormWrapper, {
|
|
31
|
+
return (React.createElement(FormWrapper, { onValidate: onValidate, allowAttachments: allowAttachments },
|
|
35
32
|
React.createElement(View, { as: "form", className: ComponentClassName.AIConversationForm, onSubmit: handleSubmit },
|
|
36
33
|
allowAttachments ? (React.createElement(Button, { className: ComponentClassName.AIConversationFormAttach, onClick: () => {
|
|
37
34
|
hiddenInput?.current?.click();
|
|
@@ -42,15 +39,11 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) =
|
|
|
42
39
|
React.createElement("span", null, attachIcon),
|
|
43
40
|
React.createElement(VisuallyHidden, null,
|
|
44
41
|
React.createElement("input", { type: "file", tabIndex: -1, ref: hiddenInput, onChange: (e) => {
|
|
45
|
-
|
|
46
|
-
if (!files || files.length === 0) {
|
|
42
|
+
if (!e.target.files || e.target.files.length === 0) {
|
|
47
43
|
return;
|
|
48
44
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
files: [...(prevValue?.files ?? []), ...Array.from(files)],
|
|
52
|
-
}));
|
|
53
|
-
}, multiple: true, accept: "*", "data-testid": "hidden-file-input" })))) : null,
|
|
45
|
+
onValidate(Array.from(e.target.files));
|
|
46
|
+
}, multiple: true, accept: ".jpeg,.png,.webp,.gif", "data-testid": "hidden-file-input" })))) : null,
|
|
54
47
|
React.createElement(TextAreaField, { className: ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', testId: "text-input", onCompositionStart: () => setComposing(true), onCompositionEnd: () => setComposing(false), onKeyDown: (e) => {
|
|
55
48
|
// Submit on enter key if shift is not pressed also
|
|
56
49
|
const shouldSubmit = !e.shiftKey && e.key === 'Enter' && !composing;
|
|
@@ -59,7 +52,7 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) =
|
|
|
59
52
|
e.preventDefault();
|
|
60
53
|
}
|
|
61
54
|
}, onChange: (e) => {
|
|
62
|
-
setInput((prevValue) => ({
|
|
55
|
+
setInput?.((prevValue) => ({
|
|
63
56
|
...prevValue,
|
|
64
57
|
text: e.target.value,
|
|
65
58
|
}));
|
|
@@ -69,6 +62,7 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) =
|
|
|
69
62
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
70
63
|
isDisabled: isLoading || isInputEmpty },
|
|
71
64
|
React.createElement("span", null, sendIcon))),
|
|
65
|
+
error ? (React.createElement(Message, { className: ComponentClassName.AIConversationFormError, variation: "plain", colorTheme: "warning" }, error)) : null,
|
|
72
66
|
React.createElement(Attachments, { setInput: setInput, files: input?.files })));
|
|
73
67
|
};
|
|
74
68
|
|
|
@@ -22,7 +22,7 @@ function createUseAIConversation(client) {
|
|
|
22
22
|
const clientRoute = client.conversations[routeName];
|
|
23
23
|
// We need to keep track of the stream events as the come in
|
|
24
24
|
// for an assistant message, but don't need to keep them in state
|
|
25
|
-
const contentBlocksRef = React__default.useRef();
|
|
25
|
+
const contentBlocksRef = React__default.useRef(undefined);
|
|
26
26
|
// Using this hook without an existing conversation id means
|
|
27
27
|
// it will create a new conversation when it is executed
|
|
28
28
|
// we don't want to create 2 conversations
|
package/dist/esm/version.mjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -47,7 +47,8 @@ const AvatarsProvider = ({ children, avatars, }) => {
|
|
|
47
47
|
const ConversationInputContext = React__namespace["default"].createContext({});
|
|
48
48
|
const ConversationInputContextProvider = ({ children, }) => {
|
|
49
49
|
const [input, setInput] = React__namespace["default"].useState();
|
|
50
|
-
const
|
|
50
|
+
const [error, setError] = React__namespace["default"].useState();
|
|
51
|
+
const providerValue = React__namespace["default"].useMemo(() => ({ input, setInput, error, setError }), [input, setInput, error, setError]);
|
|
51
52
|
return (React__namespace["default"].createElement(ConversationInputContext.Provider, { value: providerValue }, children));
|
|
52
53
|
};
|
|
53
54
|
|
|
@@ -82,32 +83,67 @@ function formatDate(date) {
|
|
|
82
83
|
return `${dateString} at ${timeString}`;
|
|
83
84
|
}
|
|
84
85
|
function arrayBufferToBase64(buffer) {
|
|
85
|
-
let binary = '';
|
|
86
|
-
const bytes = new Uint8Array(buffer);
|
|
87
|
-
const len = bytes.byteLength;
|
|
88
|
-
for (let i = 0; i < len; i++) {
|
|
89
|
-
binary += String.fromCharCode(bytes[i]);
|
|
90
|
-
}
|
|
91
|
-
return window.btoa(binary);
|
|
92
|
-
}
|
|
93
|
-
function convertBufferToBase64(buffer, format) {
|
|
94
|
-
let base64string = '';
|
|
95
86
|
// Use node-based buffer if available
|
|
96
87
|
// fall back on browser if not
|
|
97
88
|
if (typeof Buffer !== 'undefined') {
|
|
98
|
-
|
|
89
|
+
return Buffer.from(new Uint8Array(buffer)).toString('base64');
|
|
99
90
|
}
|
|
100
91
|
else {
|
|
101
|
-
|
|
92
|
+
let binary = '';
|
|
93
|
+
const bytes = new Uint8Array(buffer);
|
|
94
|
+
const len = bytes.byteLength;
|
|
95
|
+
for (let i = 0; i < len; i++) {
|
|
96
|
+
binary += String.fromCharCode(bytes[i]);
|
|
97
|
+
}
|
|
98
|
+
return window.btoa(binary);
|
|
102
99
|
}
|
|
100
|
+
}
|
|
101
|
+
function convertBufferToBase64(buffer, format) {
|
|
102
|
+
const base64string = arrayBufferToBase64(buffer);
|
|
103
103
|
return `data:image/${format};base64,${base64string}`;
|
|
104
104
|
}
|
|
105
105
|
function getImageTypeFromMimeType(mimeType) {
|
|
106
106
|
return mimeType.split('/')[1];
|
|
107
107
|
}
|
|
108
|
+
async function attachmentsValidator({ files, maxAttachments, maxAttachmentSize, }) {
|
|
109
|
+
const acceptedFiles = [];
|
|
110
|
+
const rejectedFiles = [];
|
|
111
|
+
let hasMaxSizeError = false;
|
|
112
|
+
for (const file of files) {
|
|
113
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
114
|
+
const base64 = arrayBufferToBase64(arrayBuffer);
|
|
115
|
+
if (base64.length < maxAttachmentSize) {
|
|
116
|
+
acceptedFiles.push(file);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
rejectedFiles.push(file);
|
|
120
|
+
hasMaxSizeError = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (acceptedFiles.length > maxAttachments) {
|
|
124
|
+
return {
|
|
125
|
+
acceptedFiles: acceptedFiles.slice(0, maxAttachments),
|
|
126
|
+
rejectedFiles: [...acceptedFiles.slice(maxAttachments), ...rejectedFiles],
|
|
127
|
+
hasMaxAttachmentsError: true,
|
|
128
|
+
hasMaxAttachmentSizeError: hasMaxSizeError,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
acceptedFiles,
|
|
133
|
+
rejectedFiles,
|
|
134
|
+
hasMaxAttachmentsError: false,
|
|
135
|
+
hasMaxAttachmentSizeError: hasMaxSizeError,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
108
138
|
|
|
109
139
|
const defaultAIConversationDisplayTextEn = {
|
|
110
140
|
getMessageTimestampText: (date) => formatDate(date),
|
|
141
|
+
getMaxAttachmentErrorText(count) {
|
|
142
|
+
return `Cannot choose more than ${count} ${count === 1 ? 'file' : 'files'}. `;
|
|
143
|
+
},
|
|
144
|
+
getAttachmentSizeErrorText(sizeText) {
|
|
145
|
+
return `File size must be below ${sizeText}.`;
|
|
146
|
+
},
|
|
111
147
|
};
|
|
112
148
|
|
|
113
149
|
const { ConversationDisplayTextContext, ConversationDisplayTextProvider, useConversationDisplayText, } = uiReactCore.createContextUtilities({
|
|
@@ -180,9 +216,18 @@ const { MessageRendererContext, MessageRendererProvider, useMessageRenderer, } =
|
|
|
180
216
|
errorMessage: '`useMessageRenderer` must be used with an AIConversation component',
|
|
181
217
|
});
|
|
182
218
|
|
|
183
|
-
const AttachmentContext = React__namespace.createContext(
|
|
184
|
-
|
|
185
|
-
|
|
219
|
+
const AttachmentContext = React__namespace.createContext({
|
|
220
|
+
allowAttachments: false,
|
|
221
|
+
// We save attachments as base64 strings into dynamodb for conversation history
|
|
222
|
+
// DynamoDB has a max size of 400kb for records
|
|
223
|
+
// This can be overridden so cutsomers could provide a lower number
|
|
224
|
+
// or a higher number if in the future we support larger sizes.
|
|
225
|
+
maxAttachmentSize: 400000,
|
|
226
|
+
maxAttachments: 20,
|
|
227
|
+
});
|
|
228
|
+
const AttachmentProvider = ({ children, allowAttachments = false, maxAttachmentSize = 400000, maxAttachments = 20, }) => {
|
|
229
|
+
const providerValue = React__namespace.useMemo(() => ({ maxAttachmentSize, maxAttachments, allowAttachments }), [maxAttachmentSize, maxAttachments, allowAttachments]);
|
|
230
|
+
return (React__namespace.createElement(AttachmentContext.Provider, { value: providerValue }, children));
|
|
186
231
|
};
|
|
187
232
|
|
|
188
233
|
const WelcomeMessageContext = React__namespace.createContext(undefined);
|
|
@@ -211,7 +256,7 @@ const DEFAULT_ICON_ATTRIBUTES = {
|
|
|
211
256
|
fill: 'none',
|
|
212
257
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
213
258
|
};
|
|
214
|
-
const BaseIconElement = elements.
|
|
259
|
+
const BaseIconElement = elements.defineBaseElementWithRef({
|
|
215
260
|
type: 'svg',
|
|
216
261
|
displayName: 'Icon',
|
|
217
262
|
});
|
|
@@ -227,44 +272,44 @@ const getIconProps = ({ variant, ...props }) => {
|
|
|
227
272
|
};
|
|
228
273
|
const IconElement = elements.withBaseElementProps(BaseIconElement, getIconProps);
|
|
229
274
|
|
|
230
|
-
const LabelElement$1 = elements.
|
|
275
|
+
const LabelElement$1 = elements.defineBaseElementWithRef({
|
|
231
276
|
type: 'label',
|
|
232
277
|
displayName: 'Label',
|
|
233
278
|
});
|
|
234
|
-
const TextElement = elements.
|
|
279
|
+
const TextElement = elements.defineBaseElementWithRef({
|
|
235
280
|
type: 'p',
|
|
236
281
|
displayName: 'Text',
|
|
237
282
|
});
|
|
238
|
-
const UnorderedListElement = elements.
|
|
283
|
+
const UnorderedListElement = elements.defineBaseElementWithRef({
|
|
239
284
|
type: 'ul',
|
|
240
285
|
displayName: 'UnorderedList',
|
|
241
286
|
});
|
|
242
|
-
const ListItemElement = elements.
|
|
287
|
+
const ListItemElement = elements.defineBaseElementWithRef({
|
|
243
288
|
type: 'li',
|
|
244
289
|
displayName: 'ListItem',
|
|
245
290
|
});
|
|
246
|
-
const HeadingElement = elements.
|
|
291
|
+
const HeadingElement = elements.defineBaseElementWithRef({
|
|
247
292
|
type: 'h2',
|
|
248
293
|
displayName: 'Title',
|
|
249
294
|
});
|
|
250
|
-
const ImageElement = elements.
|
|
295
|
+
const ImageElement = elements.defineBaseElementWithRef({
|
|
251
296
|
type: 'img',
|
|
252
297
|
displayName: 'Image',
|
|
253
298
|
});
|
|
254
|
-
const InputElement = elements.
|
|
299
|
+
const InputElement = elements.defineBaseElementWithRef({
|
|
255
300
|
type: 'input',
|
|
256
301
|
displayName: 'Input',
|
|
257
302
|
});
|
|
258
|
-
const ButtonElement = elements.
|
|
259
|
-
const ViewElement = elements.
|
|
303
|
+
const ButtonElement = elements.defineBaseElementWithRef({ type: 'button', displayName: 'Button' });
|
|
304
|
+
const ViewElement = elements.defineBaseElementWithRef({
|
|
260
305
|
type: 'div',
|
|
261
306
|
displayName: 'View',
|
|
262
307
|
});
|
|
263
|
-
const SpanElement = elements.
|
|
308
|
+
const SpanElement = elements.defineBaseElementWithRef({
|
|
264
309
|
type: 'span',
|
|
265
310
|
displayName: 'Span',
|
|
266
311
|
});
|
|
267
|
-
const TextAreaElement = elements.
|
|
312
|
+
const TextAreaElement = elements.defineBaseElementWithRef({
|
|
268
313
|
type: 'textarea',
|
|
269
314
|
displayName: 'TextArea',
|
|
270
315
|
});
|
|
@@ -561,9 +606,10 @@ const InputContainer = elements.withBaseElementProps(View$2, {
|
|
|
561
606
|
className: `${FIELD_BLOCK}__input-container`,
|
|
562
607
|
});
|
|
563
608
|
const FormControl = () => {
|
|
564
|
-
const { input, setInput } = React__namespace["default"].useContext(ConversationInputContext);
|
|
609
|
+
const { input, setInput, error, setError } = React__namespace["default"].useContext(ConversationInputContext);
|
|
565
610
|
const handleSendMessage = React__namespace["default"].useContext(SendMessageContext);
|
|
566
|
-
const allowAttachments = React__namespace["default"].useContext(AttachmentContext);
|
|
611
|
+
const { allowAttachments, maxAttachmentSize, maxAttachments } = React__namespace["default"].useContext(AttachmentContext);
|
|
612
|
+
const displayText = useConversationDisplayText();
|
|
567
613
|
const responseComponents = React__namespace["default"].useContext(ResponseComponentsContext);
|
|
568
614
|
const isLoading = React__namespace["default"].useContext(LoadingContext);
|
|
569
615
|
const aiContext = React__namespace["default"].useContext(AIContextContext);
|
|
@@ -615,8 +661,36 @@ const FormControl = () => {
|
|
|
615
661
|
}
|
|
616
662
|
}
|
|
617
663
|
};
|
|
664
|
+
const onValidate = React__namespace["default"].useCallback(async (files) => {
|
|
665
|
+
const previousFiles = input?.files ?? [];
|
|
666
|
+
const { acceptedFiles, hasMaxAttachmentsError, hasMaxAttachmentSizeError, } = await attachmentsValidator({
|
|
667
|
+
files: [...files, ...previousFiles],
|
|
668
|
+
maxAttachments,
|
|
669
|
+
maxAttachmentSize,
|
|
670
|
+
});
|
|
671
|
+
if (hasMaxAttachmentsError || hasMaxAttachmentSizeError) {
|
|
672
|
+
const errors = [];
|
|
673
|
+
if (hasMaxAttachmentsError) {
|
|
674
|
+
errors.push(displayText.getMaxAttachmentErrorText(maxAttachments));
|
|
675
|
+
}
|
|
676
|
+
if (hasMaxAttachmentSizeError) {
|
|
677
|
+
errors.push(displayText.getAttachmentSizeErrorText(
|
|
678
|
+
// base64 size is about 137% that of the file size
|
|
679
|
+
// https://en.wikipedia.org/wiki/Base64#MIME
|
|
680
|
+
ui.humanFileSize((maxAttachmentSize - 814) / 1.37, true)));
|
|
681
|
+
}
|
|
682
|
+
setError?.(errors.join(' '));
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
setError?.(undefined);
|
|
686
|
+
}
|
|
687
|
+
setInput?.((prevValue) => ({
|
|
688
|
+
...prevValue,
|
|
689
|
+
files: acceptedFiles,
|
|
690
|
+
}));
|
|
691
|
+
}, [setInput, input, displayText, maxAttachmentSize, maxAttachments, setError]);
|
|
618
692
|
if (controls?.Form) {
|
|
619
|
-
return (React__namespace["default"].createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, allowAttachments: allowAttachments, isLoading: isLoading }));
|
|
693
|
+
return (React__namespace["default"].createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, onValidate: onValidate, allowAttachments: allowAttachments, isLoading: isLoading, error: error, setError: setError }));
|
|
620
694
|
}
|
|
621
695
|
return (React__namespace["default"].createElement("form", { className: `${FIELD_BLOCK}__form`, onSubmit: handleSubmit, method: "post", ref: ref },
|
|
622
696
|
allowAttachments ? React__namespace["default"].createElement(AttachFileControl, null) : null,
|
|
@@ -738,7 +812,9 @@ const MessagesControl = () => {
|
|
|
738
812
|
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
739
813
|
return (React__namespace["default"].createElement(Layout, null, messagesWithRenderableContent?.map((message, index) => {
|
|
740
814
|
return (React__namespace["default"].createElement(RoleContext.Provider, { value: message.role, key: `message-${index}` },
|
|
741
|
-
React__namespace["default"].createElement(MessageContainer, { "data-testid": `message`, key: `message-${index}`, tabIndex: focusedItemIndex === index ? 0 : -1, onFocus: () => handleFocus(index), onKeyDown: (event) => onKeyDown(index, event), ref: (el) =>
|
|
815
|
+
React__namespace["default"].createElement(MessageContainer, { "data-testid": `message`, key: `message-${index}`, tabIndex: focusedItemIndex === index ? 0 : -1, onFocus: () => handleFocus(index), onKeyDown: (event) => onKeyDown(index, event), ref: (el) => {
|
|
816
|
+
messagesRef.current[index] = el;
|
|
817
|
+
} },
|
|
742
818
|
React__namespace["default"].createElement(HeaderContainer, null,
|
|
743
819
|
React__namespace["default"].createElement(AvatarControl, null),
|
|
744
820
|
React__namespace["default"].createElement(Timestamp, null, getMessageTimestampText(new Date(message.createdAt)))),
|
|
@@ -795,7 +871,7 @@ PromptControl.Container = Container;
|
|
|
795
871
|
PromptControl.PromptGroup = PromptGroup;
|
|
796
872
|
PromptControl.PromptCard = PromptCard;
|
|
797
873
|
|
|
798
|
-
const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }) => {
|
|
874
|
+
const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, maxAttachmentSize, maxAttachments, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }) => {
|
|
799
875
|
const _displayText = {
|
|
800
876
|
...defaultAIConversationDisplayTextEn,
|
|
801
877
|
...displayText,
|
|
@@ -806,7 +882,7 @@ const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars,
|
|
|
806
882
|
React__namespace["default"].createElement(FallbackComponentProvider, { FallbackComponent: FallbackResponseComponent },
|
|
807
883
|
React__namespace["default"].createElement(MessageRendererProvider, { ...messageRenderer },
|
|
808
884
|
React__namespace["default"].createElement(ResponseComponentsProvider, { responseComponents: responseComponents },
|
|
809
|
-
React__namespace["default"].createElement(AttachmentProvider, { allowAttachments: allowAttachments },
|
|
885
|
+
React__namespace["default"].createElement(AttachmentProvider, { allowAttachments: allowAttachments, maxAttachmentSize: maxAttachmentSize, maxAttachments: maxAttachments },
|
|
810
886
|
React__namespace["default"].createElement(ConversationDisplayTextProvider, { ..._displayText },
|
|
811
887
|
React__namespace["default"].createElement(ConversationInputContextProvider, null,
|
|
812
888
|
React__namespace["default"].createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
|
|
@@ -950,27 +1026,24 @@ function isHTMLFormElement(target) {
|
|
|
950
1026
|
* Will conditionally render the DropZone if allowAttachments
|
|
951
1027
|
* is true
|
|
952
1028
|
*/
|
|
953
|
-
const FormWrapper = ({ children, allowAttachments,
|
|
1029
|
+
const FormWrapper = ({ children, allowAttachments, onValidate, }) => {
|
|
954
1030
|
if (allowAttachments) {
|
|
955
1031
|
return (React__namespace.createElement(uiReact.DropZone, { className: ui.ComponentClassName.AIConversationFormDropzone, onDropComplete: ({ acceptedFiles }) => {
|
|
956
|
-
|
|
957
|
-
...prevInput,
|
|
958
|
-
files: [...(prevInput?.files ?? []), ...acceptedFiles],
|
|
959
|
-
}));
|
|
1032
|
+
onValidate(acceptedFiles);
|
|
960
1033
|
} }, children));
|
|
961
1034
|
}
|
|
962
1035
|
else {
|
|
963
1036
|
return children;
|
|
964
1037
|
}
|
|
965
1038
|
};
|
|
966
|
-
const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) => {
|
|
1039
|
+
const Form = ({ setInput, input, handleSubmit, allowAttachments, onValidate, isLoading, error, }) => {
|
|
967
1040
|
const icons = internal.useIcons('aiConversation');
|
|
968
1041
|
const sendIcon = icons?.send ?? React__namespace.createElement(internal.IconSend, null);
|
|
969
1042
|
const attachIcon = icons?.attach ?? React__namespace.createElement(internal.IconAttach, null);
|
|
970
1043
|
const hiddenInput = React__namespace.useRef(null);
|
|
971
1044
|
const [composing, setComposing] = React__namespace.useState(false);
|
|
972
1045
|
const isInputEmpty = !input?.text?.length && !input?.files?.length;
|
|
973
|
-
return (React__namespace.createElement(FormWrapper, {
|
|
1046
|
+
return (React__namespace.createElement(FormWrapper, { onValidate: onValidate, allowAttachments: allowAttachments },
|
|
974
1047
|
React__namespace.createElement(uiReact.View, { as: "form", className: ui.ComponentClassName.AIConversationForm, onSubmit: handleSubmit },
|
|
975
1048
|
allowAttachments ? (React__namespace.createElement(uiReact.Button, { className: ui.ComponentClassName.AIConversationFormAttach, onClick: () => {
|
|
976
1049
|
hiddenInput?.current?.click();
|
|
@@ -981,15 +1054,11 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) =
|
|
|
981
1054
|
React__namespace.createElement("span", null, attachIcon),
|
|
982
1055
|
React__namespace.createElement(uiReact.VisuallyHidden, null,
|
|
983
1056
|
React__namespace.createElement("input", { type: "file", tabIndex: -1, ref: hiddenInput, onChange: (e) => {
|
|
984
|
-
|
|
985
|
-
if (!files || files.length === 0) {
|
|
1057
|
+
if (!e.target.files || e.target.files.length === 0) {
|
|
986
1058
|
return;
|
|
987
1059
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
files: [...(prevValue?.files ?? []), ...Array.from(files)],
|
|
991
|
-
}));
|
|
992
|
-
}, multiple: true, accept: "*", "data-testid": "hidden-file-input" })))) : null,
|
|
1060
|
+
onValidate(Array.from(e.target.files));
|
|
1061
|
+
}, multiple: true, accept: ".jpeg,.png,.webp,.gif", "data-testid": "hidden-file-input" })))) : null,
|
|
993
1062
|
React__namespace.createElement(uiReact.TextAreaField, { className: ui.ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', testId: "text-input", onCompositionStart: () => setComposing(true), onCompositionEnd: () => setComposing(false), onKeyDown: (e) => {
|
|
994
1063
|
// Submit on enter key if shift is not pressed also
|
|
995
1064
|
const shouldSubmit = !e.shiftKey && e.key === 'Enter' && !composing;
|
|
@@ -998,7 +1067,7 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) =
|
|
|
998
1067
|
e.preventDefault();
|
|
999
1068
|
}
|
|
1000
1069
|
}, onChange: (e) => {
|
|
1001
|
-
setInput((prevValue) => ({
|
|
1070
|
+
setInput?.((prevValue) => ({
|
|
1002
1071
|
...prevValue,
|
|
1003
1072
|
text: e.target.value,
|
|
1004
1073
|
}));
|
|
@@ -1008,6 +1077,7 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) =
|
|
|
1008
1077
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
1009
1078
|
isDisabled: isLoading || isInputEmpty },
|
|
1010
1079
|
React__namespace.createElement("span", null, sendIcon))),
|
|
1080
|
+
error ? (React__namespace.createElement(uiReact.Message, { className: ui.ComponentClassName.AIConversationFormError, variation: "plain", colorTheme: "warning" }, error)) : null,
|
|
1011
1081
|
React__namespace.createElement(Attachments, { setInput: setInput, files: input?.files })));
|
|
1012
1082
|
};
|
|
1013
1083
|
|
|
@@ -1022,7 +1092,7 @@ const PromptList = ({ setInput, suggestedPrompts = [], }) => {
|
|
|
1022
1092
|
})));
|
|
1023
1093
|
};
|
|
1024
1094
|
|
|
1025
|
-
const VERSION = '1.
|
|
1095
|
+
const VERSION = '1.2.0';
|
|
1026
1096
|
|
|
1027
1097
|
function AIConversationBase({ avatars, controls, ...rest }) {
|
|
1028
1098
|
uiReactCore.useSetUserAgent({
|
|
@@ -1164,7 +1234,7 @@ function createUseAIConversation(client) {
|
|
|
1164
1234
|
const clientRoute = client.conversations[routeName];
|
|
1165
1235
|
// We need to keep track of the stream events as the come in
|
|
1166
1236
|
// for an assistant message, but don't need to keep them in state
|
|
1167
|
-
const contentBlocksRef = React__namespace["default"].useRef();
|
|
1237
|
+
const contentBlocksRef = React__namespace["default"].useRef(undefined);
|
|
1168
1238
|
// Using this hook without an existing conversation id means
|
|
1169
1239
|
// it will create a new conversation when it is executed
|
|
1170
1240
|
// we don't want to create 2 conversations
|
|
@@ -3,4 +3,4 @@ import { AIConversationInput, AIConversationProps } from './types';
|
|
|
3
3
|
export interface AIConversationProviderProps extends AIConversationInput, AIConversationProps {
|
|
4
4
|
children?: React.ReactNode;
|
|
5
5
|
}
|
|
6
|
-
export declare const AIConversationProvider: ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }: AIConversationProviderProps) => React.JSX.Element;
|
|
6
|
+
export declare const AIConversationProvider: ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, maxAttachmentSize, maxAttachments, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }: AIConversationProviderProps) => React.JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}) => JSX.Element;
|
|
2
|
+
import { AIConversationInput } from '../types';
|
|
3
|
+
export interface AttachmentContextProps extends Pick<AIConversationInput, 'allowAttachments' | 'maxAttachments' | 'maxAttachmentSize'> {
|
|
4
|
+
}
|
|
5
|
+
export declare const AttachmentContext: React.Context<Required<AttachmentContextProps>>;
|
|
6
|
+
export declare const AttachmentProvider: ({ children, allowAttachments, maxAttachmentSize, maxAttachments, }: React.PropsWithChildren<AttachmentContextProps>) => React.JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ConversationInputContextProps } from './ConversationInputContext';
|
|
3
3
|
import { SuggestedPrompt } from '../types';
|
|
4
4
|
import { ConversationMessage } from '../../../types';
|
|
5
5
|
export interface ControlsContextProps {
|
|
@@ -7,17 +7,18 @@ export interface ControlsContextProps {
|
|
|
7
7
|
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
|
8
8
|
allowAttachments?: boolean;
|
|
9
9
|
isLoading?: boolean;
|
|
10
|
-
|
|
10
|
+
onValidate: (files: File[]) => Promise<void>;
|
|
11
|
+
} & ConversationInputContextProps>;
|
|
11
12
|
MessageList?: React.ComponentType<{
|
|
12
13
|
messages: ConversationMessage[];
|
|
13
14
|
}>;
|
|
14
15
|
PromptList?: React.ComponentType<{
|
|
15
16
|
suggestedPrompts?: SuggestedPrompt[];
|
|
16
|
-
setInput:
|
|
17
|
+
setInput: ConversationInputContextProps['setInput'];
|
|
17
18
|
}>;
|
|
18
19
|
}
|
|
19
20
|
export declare const ControlsContext: React.Context<ControlsContextProps | undefined>;
|
|
20
21
|
export declare const ControlsProvider: ({ children, controls, }: {
|
|
21
22
|
children?: React.ReactNode;
|
|
22
23
|
controls?: ControlsContextProps | undefined;
|
|
23
|
-
}) => JSX.Element;
|
|
24
|
+
}) => React.JSX.Element;
|
|
@@ -3,11 +3,13 @@ export interface ConversationInput {
|
|
|
3
3
|
text?: string;
|
|
4
4
|
files?: File[];
|
|
5
5
|
}
|
|
6
|
-
export interface
|
|
6
|
+
export interface ConversationInputContextProps {
|
|
7
7
|
input?: ConversationInput;
|
|
8
8
|
setInput?: React.Dispatch<React.SetStateAction<ConversationInput | undefined>>;
|
|
9
|
+
error?: string;
|
|
10
|
+
setError?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
|
9
11
|
}
|
|
10
|
-
export declare const ConversationInputContext: React.Context<
|
|
12
|
+
export declare const ConversationInputContext: React.Context<ConversationInputContextProps>;
|
|
11
13
|
export declare const ConversationInputContextProvider: ({ children, }: {
|
|
12
14
|
children?: React.ReactNode;
|
|
13
|
-
}) => JSX.Element;
|
|
15
|
+
}) => React.JSX.Element;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import { ConversationDisplayText } from '../displayText';
|
|
3
|
-
export declare const ConversationDisplayTextContext: import("react").Context<Required<ConversationDisplayText
|
|
2
|
+
export declare const ConversationDisplayTextContext: import("react").Context<Required<ConversationDisplayText>>, ConversationDisplayTextProvider: import("react").ComponentType<import("react").PropsWithChildren<Required<ConversationDisplayText>>>, useConversationDisplayText: (params?: {
|
|
4
3
|
errorMessage?: string | undefined;
|
|
5
4
|
} | undefined) => Required<ConversationDisplayText>;
|
|
@@ -4,4 +4,4 @@ export declare const FallbackComponentContext: React.Context<React.ComponentType
|
|
|
4
4
|
export declare const FallbackComponentProvider: ({ children, FallbackComponent, }: {
|
|
5
5
|
children?: React.ReactNode;
|
|
6
6
|
FallbackComponent?: AIConversationInput['FallbackResponseComponent'];
|
|
7
|
-
}) => JSX.Element;
|
|
7
|
+
}) => React.JSX.Element;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import { MessageRenderer } from '../types';
|
|
3
|
-
export declare const MessageRendererContext: import("react").Context<MessageRenderer
|
|
2
|
+
export declare const MessageRendererContext: import("react").Context<MessageRenderer>, MessageRendererProvider: import("react").ComponentType<import("react").PropsWithChildren<MessageRenderer>>, useMessageRenderer: (params?: {
|
|
4
3
|
errorMessage?: string | undefined;
|
|
5
4
|
} | undefined) => MessageRenderer;
|
|
@@ -6,5 +6,5 @@ export declare const RoleContext: React.Context<"user" | "assistant" | undefined
|
|
|
6
6
|
export declare const MessagesProvider: ({ children, messages, }: {
|
|
7
7
|
children?: React.ReactNode;
|
|
8
8
|
messages: ConversationMessage[];
|
|
9
|
-
}) => JSX.Element;
|
|
9
|
+
}) => React.JSX.Element;
|
|
10
10
|
export {};
|
|
@@ -7,6 +7,6 @@ export declare const prependResponseComponents: (responseComponents?: ResponseCo
|
|
|
7
7
|
export declare const ResponseComponentsProvider: ({ children, responseComponents, }: {
|
|
8
8
|
children?: React.ReactNode;
|
|
9
9
|
responseComponents?: ResponseComponents | undefined;
|
|
10
|
-
}) => JSX.Element;
|
|
10
|
+
}) => React.JSX.Element;
|
|
11
11
|
export declare const convertResponseComponentsToToolConfiguration: (responseComponents?: ResponseComponents) => ToolConfiguration | undefined;
|
|
12
12
|
export {};
|
|
@@ -4,4 +4,4 @@ export declare const SendMessageContext: React.Context<SendMessage | undefined>;
|
|
|
4
4
|
export declare const SendMessageContextProvider: ({ children, handleSendMessage, }: {
|
|
5
5
|
children?: React.ReactNode;
|
|
6
6
|
handleSendMessage: SendMessage;
|
|
7
|
-
}) => JSX.Element;
|
|
7
|
+
}) => React.JSX.Element;
|
|
@@ -5,5 +5,5 @@ export declare const SuggestedPromptsContext: React.Context<SuggestedPromptsCont
|
|
|
5
5
|
export declare const SuggestedPromptProvider: ({ children, suggestedPrompts, }: {
|
|
6
6
|
children?: React.ReactNode;
|
|
7
7
|
suggestedPrompts?: SuggestedPrompt[] | undefined;
|
|
8
|
-
}) => JSX.Element;
|
|
8
|
+
}) => React.JSX.Element;
|
|
9
9
|
export {};
|
|
@@ -4,5 +4,5 @@ export declare const WelcomeMessageContext: React.Context<WelcomeMessageContextP
|
|
|
4
4
|
export declare const WelcomeMessageProvider: ({ children, welcomeMessage, }: {
|
|
5
5
|
children?: React.ReactNode;
|
|
6
6
|
welcomeMessage?: React.ReactNode;
|
|
7
|
-
}) => JSX.Element;
|
|
7
|
+
}) => React.JSX.Element;
|
|
8
8
|
export {};
|
|
@@ -2,5 +2,5 @@ import React from 'react';
|
|
|
2
2
|
export type IconElementProps = React.ComponentProps<typeof BaseIconElement>;
|
|
3
3
|
export type IconVariant = 'attach' | 'close' | 'image' | 'send-message' | 'user-avatar';
|
|
4
4
|
export declare const DEFAULT_ICON_PATHS: Record<IconVariant, string>;
|
|
5
|
-
export declare const BaseIconElement: import("@aws-amplify/ui-react-core/
|
|
6
|
-
export declare const IconElement: import("@aws-amplify/ui-react-core/
|
|
5
|
+
export declare const BaseIconElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<never, IconVariant, React.SVGProps<SVGSVGElement>>, SVGSVGElement>;
|
|
6
|
+
export declare const IconElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<Omit<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<never, IconVariant, React.SVGProps<SVGSVGElement>>, "ref"> & React.RefAttributes<SVGSVGElement>, SVGSVGElement>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
1
|
import { IconElement } from './IconElement';
|
|
3
2
|
export interface AIConversationElements {
|
|
4
3
|
Button: typeof ButtonElement;
|
|
@@ -14,22 +13,22 @@ export interface AIConversationElements {
|
|
|
14
13
|
UnorderedList: typeof UnorderedListElement;
|
|
15
14
|
View: typeof ViewElement;
|
|
16
15
|
}
|
|
17
|
-
export declare const LabelElement: import("@aws-amplify/ui-react-core/
|
|
18
|
-
export declare const TextElement: import("@aws-amplify/ui-react-core/
|
|
19
|
-
export declare const UnorderedListElement: import("@aws-amplify/ui-react-core/
|
|
20
|
-
export declare const ListItemElement: import("@aws-amplify/ui-react-core/
|
|
21
|
-
export declare const HeadingElement: import("@aws-amplify/ui-react-core/
|
|
16
|
+
export declare const LabelElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<"htmlFor", string, import("react").DetailedHTMLProps<import("react").LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>>, HTMLLabelElement>;
|
|
17
|
+
export declare const TextElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<never, string, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>>, HTMLParagraphElement>;
|
|
18
|
+
export declare const UnorderedListElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<never, string, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLUListElement>, HTMLUListElement>>, HTMLUListElement>;
|
|
19
|
+
export declare const ListItemElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<never, string, import("react").DetailedHTMLProps<import("react").LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>>, HTMLLIElement>;
|
|
20
|
+
export declare const HeadingElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<never, string, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>, HTMLHeadingElement>;
|
|
22
21
|
export type IconElementProps = React.ComponentProps<typeof IconElement>;
|
|
23
22
|
type ImageElementProps = 'src' | 'alt';
|
|
24
|
-
export declare const ImageElement: import("@aws-amplify/ui-react-core/
|
|
25
|
-
export declare const InputElement: import("@aws-amplify/ui-react-core/
|
|
23
|
+
export declare const ImageElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<ImageElementProps, string, import("react").DetailedHTMLProps<import("react").ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>>, HTMLImageElement>;
|
|
24
|
+
export declare const InputElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<"type", string, import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>>, HTMLInputElement>;
|
|
26
25
|
type ButtonElementProps = 'disabled' | 'onClick' | 'type' | 'tabIndex';
|
|
27
26
|
type ButtonElementVariant = 'attach' | 'remove' | 'send-message';
|
|
28
|
-
export declare const ButtonElement: import("@aws-amplify/ui-react-core/
|
|
27
|
+
export declare const ButtonElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<ButtonElementProps, ButtonElementVariant, import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>>, HTMLButtonElement>;
|
|
29
28
|
type ViewElementProps = 'onFocus' | 'tabIndex' | 'onKeyDown';
|
|
30
|
-
export declare const ViewElement: import("@aws-amplify/ui-react-core/
|
|
31
|
-
export declare const SpanElement: import("@aws-amplify/ui-react-core/
|
|
29
|
+
export declare const ViewElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<ViewElementProps, string, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>>, HTMLDivElement>;
|
|
30
|
+
export declare const SpanElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<never, string, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>>, HTMLSpanElement>;
|
|
32
31
|
type TextAreaElementProps = 'id' | 'name' | 'onChange' | 'placeholder' | 'autoFocus' | 'onCompositionStart' | 'onCompositionEnd' | 'onKeyDown';
|
|
33
|
-
export declare const TextAreaElement: import("@aws-amplify/ui-react-core/
|
|
32
|
+
export declare const TextAreaElement: import("@aws-amplify/ui-react-core/elements").BaseElementWithRef<import("@aws-amplify/ui-react-core/elements").BaseElementWithRefProps<TextAreaElementProps, string, import("react").DetailedHTMLProps<import("react").TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>>, HTMLTextAreaElement>;
|
|
34
33
|
export declare const AIConversationElements: AIConversationElements;
|
|
35
34
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { AIContextContext, AIContextProvider } from './AIContextContext';
|
|
2
2
|
export { ActionsContext, ActionsProvider } from './ActionsContext';
|
|
3
3
|
export { AvatarsContext, AvatarsProvider } from './AvatarsContext';
|
|
4
|
-
export { ConversationInputContext, ConversationInput, ConversationInputContextProvider, } from './ConversationInputContext';
|
|
4
|
+
export { ConversationInputContextProps, ConversationInputContext, ConversationInput, ConversationInputContextProvider, } from './ConversationInputContext';
|
|
5
5
|
export { MessagesContext, RoleContext, MessagesProvider, } from './MessagesContext';
|
|
6
6
|
export { SuggestedPromptsContext, SuggestedPromptProvider, } from './SuggestedPromptsContext';
|
|
7
7
|
export { MessageVariantContext, MessageVariantProvider, } from './MessageVariantContext';
|
|
@@ -11,7 +11,7 @@ export { LoadingContextProvider } from './LoadingContext';
|
|
|
11
11
|
export { ResponseComponentsProvider, RESPONSE_COMPONENT_PREFIX, } from './ResponseComponentsContext';
|
|
12
12
|
export { SendMessageContextProvider } from './SendMessageContext';
|
|
13
13
|
export { MessageRendererProvider, MessageRendererContext, useMessageRenderer, } from './MessageRenderContext';
|
|
14
|
-
export { AttachmentProvider, AttachmentContext } from './AttachmentContext';
|
|
14
|
+
export { AttachmentProvider, AttachmentContext, AttachmentContextProps, } from './AttachmentContext';
|
|
15
15
|
export { WelcomeMessageContext, WelcomeMessageProvider, } from './WelcomeMessageContext';
|
|
16
16
|
export { FallbackComponentContext, FallbackComponentProvider, } from './FallbackComponentContext';
|
|
17
17
|
export * from './elements';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DisplayTextTemplate } from '@aws-amplify/ui';
|
|
2
2
|
export type ConversationDisplayText = {
|
|
3
3
|
getMessageTimestampText?: (date: Date) => string;
|
|
4
|
+
getMaxAttachmentErrorText?: (count: number) => string;
|
|
5
|
+
getAttachmentSizeErrorText?: (sizeText: string) => string;
|
|
4
6
|
};
|
|
5
7
|
export declare const defaultAIConversationDisplayTextEn: Required<AIConversationDisplayText>;
|
|
6
8
|
export type AIConversationDisplayText = DisplayTextTemplate<ConversationDisplayText>;
|
|
@@ -22,6 +22,8 @@ export interface AIConversationInput {
|
|
|
22
22
|
variant?: MessageVariant;
|
|
23
23
|
controls?: ControlsContextProps;
|
|
24
24
|
allowAttachments?: boolean;
|
|
25
|
+
maxAttachments?: number;
|
|
26
|
+
maxAttachmentSize?: number;
|
|
25
27
|
messageRenderer?: MessageRenderer;
|
|
26
28
|
}
|
|
27
29
|
export interface AIConversationProps {
|
|
@@ -32,10 +34,10 @@ export interface AIConversationProps {
|
|
|
32
34
|
aiContext?: () => object;
|
|
33
35
|
}
|
|
34
36
|
export interface AIConversation<PropsType extends AIConversationProps = AIConversationProps> {
|
|
35
|
-
(props: PropsType): JSX.Element;
|
|
36
|
-
DefaultMessage: () => JSX.Element | undefined;
|
|
37
|
-
Messages: () => JSX.Element;
|
|
38
|
-
Form: () => JSX.Element;
|
|
37
|
+
(props: PropsType): React.JSX.Element;
|
|
38
|
+
DefaultMessage: () => React.JSX.Element | undefined;
|
|
39
|
+
Messages: () => React.JSX.Element;
|
|
40
|
+
Form: () => React.JSX.Element;
|
|
39
41
|
Provider: (props: AIConversationProviderProps) => React.JSX.Element;
|
|
40
42
|
}
|
|
41
43
|
export type MessageVariant = 'bubble' | 'default';
|
|
@@ -2,3 +2,13 @@ import { ImageContentBlock } from '../../types';
|
|
|
2
2
|
export declare function formatDate(date: Date): string;
|
|
3
3
|
export declare function convertBufferToBase64(buffer: ArrayBuffer, format: ImageContentBlock['format']): string;
|
|
4
4
|
export declare function getImageTypeFromMimeType(mimeType: string): 'png' | 'jpeg' | 'gif' | 'webp';
|
|
5
|
+
export declare function attachmentsValidator({ files, maxAttachments, maxAttachmentSize, }: {
|
|
6
|
+
files: File[];
|
|
7
|
+
maxAttachments: number;
|
|
8
|
+
maxAttachmentSize: number;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
acceptedFiles: File[];
|
|
11
|
+
rejectedFiles: File[];
|
|
12
|
+
hasMaxAttachmentSizeError: boolean;
|
|
13
|
+
hasMaxAttachmentsError: boolean;
|
|
14
|
+
}>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { AIConversationElements } from '../../context/elements';
|
|
3
3
|
export declare const RemoveButtonControl: RemoveButtonControl;
|
|
4
4
|
interface RemoveButtonControl<T extends Partial<AIConversationElements> = AIConversationElements> {
|
|
5
5
|
(props: {
|
|
6
6
|
onRemove: () => void;
|
|
7
|
-
}): JSX.Element;
|
|
7
|
+
}): React.JSX.Element;
|
|
8
8
|
Button: T['Button'];
|
|
9
9
|
Icon: T['Icon'];
|
|
10
10
|
}
|
|
@@ -13,7 +13,7 @@ interface TextControl<T extends Partial<AIConversationElements> = AIConversation
|
|
|
13
13
|
(props: {
|
|
14
14
|
fileName: string;
|
|
15
15
|
fileSize: number;
|
|
16
|
-
}): JSX.Element;
|
|
16
|
+
}): React.JSX.Element;
|
|
17
17
|
Container: T['View'];
|
|
18
18
|
FileName: T['Text'];
|
|
19
19
|
FileSize: T['Text'];
|
|
@@ -24,7 +24,7 @@ interface AttachmentControl<T extends Partial<AIConversationElements> = AIConver
|
|
|
24
24
|
(props: {
|
|
25
25
|
image: File;
|
|
26
26
|
onRemove: () => void;
|
|
27
|
-
}): JSX.Element;
|
|
27
|
+
}): React.JSX.Element;
|
|
28
28
|
Container: T['ListItem'];
|
|
29
29
|
ImageIcon: T['Icon'];
|
|
30
30
|
RemoveButton: RemoveButtonControl<T>;
|
|
@@ -32,7 +32,7 @@ interface AttachmentControl<T extends Partial<AIConversationElements> = AIConver
|
|
|
32
32
|
}
|
|
33
33
|
export declare const AttachmentListControl: AttachmentListControl;
|
|
34
34
|
export interface AttachmentListControl<T extends Partial<AIConversationElements> = AIConversationElements> {
|
|
35
|
-
(): JSX.Element;
|
|
35
|
+
(): React.JSX.Element;
|
|
36
36
|
List: T['UnorderedList'];
|
|
37
37
|
Item: AttachmentControl<T>;
|
|
38
38
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const DefaultMessageControl: () => JSX.Element | undefined;
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export declare const DefaultMessageControl: () => React.JSX.Element | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { AIConversationElements } from '../../context/elements';
|
|
3
3
|
import { ActionsBarControl } from './ActionsBarControl';
|
|
4
4
|
import { AvatarControl } from './AvatarControl';
|
|
@@ -7,11 +7,11 @@ export declare const MessageControl: MessageControl;
|
|
|
7
7
|
interface MessageControl {
|
|
8
8
|
(props: {
|
|
9
9
|
message: ConversationMessage;
|
|
10
|
-
}): JSX.Element;
|
|
10
|
+
}): React.JSX.Element;
|
|
11
11
|
}
|
|
12
12
|
export declare const MessagesControl: MessagesControl;
|
|
13
13
|
export interface MessagesControl {
|
|
14
|
-
(): JSX.Element;
|
|
14
|
+
(): React.JSX.Element;
|
|
15
15
|
ActionsBar: ActionsBarControl;
|
|
16
16
|
Avatar: AvatarControl;
|
|
17
17
|
Container: AIConversationElements['View'];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ConversationInputContextProps } from '../../context';
|
|
3
3
|
export declare const Attachments: ({ files, setInput, }: {
|
|
4
4
|
files?: File[] | undefined;
|
|
5
|
-
setInput:
|
|
6
|
-
}) => JSX.Element | null;
|
|
5
|
+
setInput: ConversationInputContextProps['setInput'];
|
|
6
|
+
}) => React.JSX.Element | null;
|
package/dist/types/types.d.ts
CHANGED
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.2.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/ui-react-ai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/esm/index.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -42,15 +42,15 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@aws-amplify/api-graphql": "
|
|
46
|
-
"aws-amplify": "^6.
|
|
47
|
-
"react": "^16.14
|
|
48
|
-
"react-dom": "^16.14
|
|
45
|
+
"@aws-amplify/api-graphql": "unstable",
|
|
46
|
+
"aws-amplify": "^6.9.0",
|
|
47
|
+
"react": "^16.14 || ^17 || ^18 || ^19",
|
|
48
|
+
"react-dom": "^16.14 || ^17 || ^18 || ^19"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@aws-amplify/ui": "^6.
|
|
52
|
-
"@aws-amplify/ui-react": "^6.
|
|
53
|
-
"@aws-amplify/ui-react-core": "^3.0
|
|
51
|
+
"@aws-amplify/ui": "^6.7.1",
|
|
52
|
+
"@aws-amplify/ui-react": "^6.8.0",
|
|
53
|
+
"@aws-amplify/ui-react-core": "^3.2.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/jest-when": "^3.5.0",
|