@automattic/jetpack-ai-client 0.1.1 → 0.1.3
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/CHANGELOG.md +34 -0
- package/README.md +18 -96
- package/index.ts +5 -0
- package/package.json +12 -9
- package/src/ask-question/index.ts +0 -4
- package/src/components/ai-control/Readme.md +29 -0
- package/src/components/ai-control/index.tsx +101 -60
- package/src/components/ai-control/message.tsx +86 -0
- package/src/components/ai-control/stories/index.stories.tsx +27 -1
- package/src/components/ai-control/style.scss +58 -12
- package/src/components/ai-status-indicator/Readme.md +11 -0
- package/src/components/ai-status-indicator/index.tsx +43 -0
- package/src/components/ai-status-indicator/stories/index.mdx +25 -0
- package/src/components/ai-status-indicator/stories/index.stories.tsx +84 -0
- package/src/components/ai-status-indicator/style.scss +50 -0
- package/src/data-flow/Readme.md +2 -0
- package/src/data-flow/context.tsx +4 -2
- package/src/data-flow/use-ai-context.ts +18 -3
- package/src/hooks/use-ai-suggestions/Readme.md +1 -1
- package/src/hooks/use-ai-suggestions/index.ts +15 -11
- package/src/suggestions-event-source/index.ts +48 -2
- package/src/types.ts +29 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.3] - 2023-08-09
|
|
9
|
+
### Added
|
|
10
|
+
- AI Client: Introduce disabled prop in AI Control. [#32326]
|
|
11
|
+
- AI Control: Add guideline message. [#32358]
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- AI Client: handle token fetching errors by dispatching an event from the SuggestionsEventSource class. [#32350]
|
|
15
|
+
- AI Client: tweak layout and styles to make AI Control mobile friendly. [#32362]
|
|
16
|
+
- AI Control: clean up props. [#32360]
|
|
17
|
+
- Updated package dependencies. [#32166]
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- AI Client: fix TS type definition issue [#32330]
|
|
21
|
+
|
|
22
|
+
## [0.1.2] - 2023-08-07
|
|
23
|
+
### Added
|
|
24
|
+
- AI Assistant: Add options parameter to request function on useAiSuggestions hook [#32198]
|
|
25
|
+
- AI Client: add @wordpress/compose dependency [#32228]
|
|
26
|
+
- AI Client: Add clear button in AI Control component [#32274]
|
|
27
|
+
- AI Client: Add keyboard shortcut to AIControl [#32239]
|
|
28
|
+
- AI Client: add onError() response support [#32223]
|
|
29
|
+
- AI Client: Export types [#32209]
|
|
30
|
+
- AI Client: Start supporting request options on requestSuggestion callback. [#32303]
|
|
31
|
+
- AI Control: introduce AiStatusIndicator component [#32258]
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
- AI Client: complete/update/improve doc [#32311]
|
|
35
|
+
- AI Client: rename the prop name of the requesting state of the AiStatusIndicator component [#32279]
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- AI Client: Fix wrong disabled state condition. [#32210]
|
|
39
|
+
|
|
8
40
|
## [0.1.1] - 2023-08-01
|
|
9
41
|
### Added
|
|
10
42
|
- Add AI Client icon components [#32079]
|
|
@@ -36,4 +68,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
36
68
|
- Updated package dependencies. [#31659]
|
|
37
69
|
- Updated package dependencies. [#31785]
|
|
38
70
|
|
|
71
|
+
[0.1.3]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.2...v0.1.3
|
|
72
|
+
[0.1.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.1...v0.1.2
|
|
39
73
|
[0.1.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.0...v0.1.1
|
package/README.md
CHANGED
|
@@ -10,118 +10,40 @@ To install the Jetpack AI Client, clone the repository to your local machine and
|
|
|
10
10
|
npm install @automattic/jetpack-ai-client
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Libraries & Components
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### Requesting
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const MyComp = ( props ) => {
|
|
21
|
-
const [ completion, setCompletion ] = useState( '' );
|
|
22
|
-
|
|
23
|
-
const newHaiku = async () => {
|
|
24
|
-
const eventSource = await requestCompletion( 'Write a haiku about WordPress' );
|
|
25
|
-
eventSource.addEventListener( 'suggestion', answer => setCompletion( answer.detail ) );
|
|
26
|
-
eventSource.addEventListener( 'done', event => {
|
|
27
|
-
console.log( "Full completion", event.detail );
|
|
28
|
-
} );
|
|
29
|
-
|
|
30
|
-
eventSource.addEventListener( 'suggestion', event => {
|
|
31
|
-
console.log( "Received so far", event.detail );
|
|
32
|
-
} );
|
|
33
|
-
|
|
34
|
-
eventSource.addEventListener( 'error_quota_exceeded', event => {
|
|
35
|
-
console.log( "You reached the AI query quota for your current plan.", event.detail );
|
|
36
|
-
} );
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<div>
|
|
41
|
-
<div> { completion } </div>
|
|
42
|
-
<button onClick={ newHaiku }>Get new Haiku</button>
|
|
43
|
-
</div>
|
|
44
|
-
)
|
|
45
|
-
};
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Requesting a Completion from the Jetpack AI API
|
|
49
|
-
|
|
50
|
-
You can request a completion from the Jetpack AI API using the `requestCompletion` function. This function takes a prompt and optionally a post ID as parameters and returns an instance of `SuggestionsEventSource`.
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
import { requestCompletion } from '@automattic/jetpack-ai-client';
|
|
54
|
-
|
|
55
|
-
// postId is the post where the request is being triggered
|
|
56
|
-
// It's only used for loggin purposes and can be omitted.
|
|
57
|
-
const postId = 123;
|
|
58
|
-
const eventSource = requestCompletion( 'A haiku', postId ))
|
|
59
|
-
|
|
60
|
-
eventSource.addEventListener('done', event => {
|
|
61
|
-
console.log( "Full completion", event.detail );
|
|
62
|
-
} );
|
|
63
|
-
|
|
64
|
-
eventSource.addEventListener('suggestion', event => {
|
|
65
|
-
console.log( "Received so far", event.detail );
|
|
66
|
-
} );
|
|
67
|
-
```
|
|
17
|
+
#### [askQuestion](./src/ask-question/Readme.md) helper
|
|
18
|
+
Async function that sends a question and optional configurations, retrieves a JWT token, and returns a [SuggestionsEventSource](./src/suggestions-event-source/Readme.md) instance.
|
|
68
19
|
|
|
69
|
-
|
|
20
|
+
#### [SuggestionsEventSouce](./src/suggestions-event-source/Readme.md) Class
|
|
70
21
|
|
|
71
|
-
|
|
22
|
+
Class that connects to an AI model to receive and emit suggestion streams, using EventTarget for handling data chunks.
|
|
72
23
|
|
|
73
|
-
|
|
74
|
-
import { requestImages } from '@automattic/jetpack-ai-client';
|
|
75
|
-
|
|
76
|
-
requestImages( 'a flower', postId )
|
|
77
|
-
.then( images => {
|
|
78
|
-
document.getElementById("imgid").src= image[0]
|
|
79
|
-
} )
|
|
80
|
-
.catch( error => {
|
|
81
|
-
// Handle the error
|
|
82
|
-
} );
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Using the SuggestionsEventSource Class
|
|
86
|
-
|
|
87
|
-
The `SuggestionsEventSource` class is a wrapper around `EventSource` that emits events for each chunk of data received, when the stream is closed, and when a full suggestion has been received.
|
|
88
|
-
|
|
89
|
-
You shouldn't need to instantiate this class. You get one of these by calling `requestCompletion()`.
|
|
24
|
+
#### [useAiSuggestions](./src/hooks/use-ai-suggestions/Readme.md) hook
|
|
90
25
|
|
|
91
|
-
|
|
92
|
-
import { requestCompletion } from '@automattic/jetpack-ai-client';
|
|
26
|
+
A custom React hook that obtains suggestions from an AI by hitting a specific query endpoint.
|
|
93
27
|
|
|
94
|
-
|
|
28
|
+
#### [JWT](./src/jwt/Readme.md) helper
|
|
95
29
|
|
|
96
|
-
|
|
97
|
-
console.log( "Full completion", event.detail );
|
|
98
|
-
} );
|
|
30
|
+
Library to manage JWT tokens for Jetpack AI across various site types, handling acquisition, caching in localStorage, expiration, and customization of request options.
|
|
99
31
|
|
|
100
|
-
|
|
101
|
-
console.log( "Received so far", event.detail );
|
|
102
|
-
} );
|
|
103
|
-
```
|
|
32
|
+
#### [Data Flow](./src/data-flow/Readme.md) implementation
|
|
104
33
|
|
|
105
|
-
|
|
34
|
+
Data Flow offers a streamlined way to manage an AI Assistant's state and functionality within a React app, using React context, HOCs, and custom hooks to handle suggestions, errors, and requests.
|
|
106
35
|
|
|
107
|
-
|
|
36
|
+
### [Components](./src/components/)
|
|
108
37
|
|
|
109
|
-
|
|
38
|
+
#### [AIControl](./src/components/ai-control/Readme.md)
|
|
110
39
|
|
|
111
|
-
|
|
112
|
-
import { requestCompletionAuthToken } from '@automattic/jetpack-ai-client';
|
|
113
|
-
|
|
114
|
-
requestCompletionAuthToken()
|
|
115
|
-
.then(tokenData => {
|
|
116
|
-
// Do something with the token data
|
|
117
|
-
})
|
|
118
|
-
.catch(error => {
|
|
119
|
-
// Handle the error
|
|
120
|
-
});
|
|
121
|
-
```
|
|
40
|
+
#### [AiStatusIndicator](./src/components/ai-status-indicator/)
|
|
122
41
|
|
|
42
|
+
### [Icons](./src/icons/Readme.md)
|
|
123
43
|
## Contribute
|
|
124
44
|
|
|
45
|
+
React components useful for when you need to create some UI implementations.
|
|
46
|
+
|
|
125
47
|
We welcome contributions from the community. Please submit your pull requests on the GitHub repository.
|
|
126
48
|
|
|
127
49
|
## Get Help
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-ai-client",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"description": "A JS client for consuming Jetpack AI services",
|
|
6
6
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"type": "module",
|
|
23
23
|
"devDependencies": {
|
|
24
|
+
"@storybook/addon-actions": "7.1.0",
|
|
25
|
+
"@storybook/blocks": "7.1.0",
|
|
24
26
|
"@storybook/react": "7.1.0",
|
|
25
27
|
"jest": "^29.6.2",
|
|
26
28
|
"jest-environment-jsdom": "29.5.0",
|
|
@@ -31,15 +33,16 @@
|
|
|
31
33
|
},
|
|
32
34
|
"dependencies": {
|
|
33
35
|
"@automattic/jetpack-connection": "workspace:*",
|
|
34
|
-
"@automattic/jetpack-shared-extension-utils": "^0.
|
|
36
|
+
"@automattic/jetpack-shared-extension-utils": "^0.11.1",
|
|
35
37
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
36
|
-
"@wordpress/api-fetch": "6.
|
|
37
|
-
"@wordpress/block-editor": "12.
|
|
38
|
-
"@wordpress/components": "25.
|
|
39
|
-
"@wordpress/
|
|
40
|
-
"@wordpress/
|
|
41
|
-
"@wordpress/
|
|
42
|
-
"@wordpress/
|
|
38
|
+
"@wordpress/api-fetch": "6.35.0",
|
|
39
|
+
"@wordpress/block-editor": "12.6.0",
|
|
40
|
+
"@wordpress/components": "25.4.0",
|
|
41
|
+
"@wordpress/compose": "6.15.0",
|
|
42
|
+
"@wordpress/data": "9.8.0",
|
|
43
|
+
"@wordpress/element": "5.15.0",
|
|
44
|
+
"@wordpress/i18n": "4.38.0",
|
|
45
|
+
"@wordpress/icons": "9.29.0",
|
|
43
46
|
"classnames": "2.3.2",
|
|
44
47
|
"debug": "4.3.4",
|
|
45
48
|
"react": "18.2.0",
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import debugFactory from 'debug';
|
|
5
|
-
import requestJwt from '../jwt';
|
|
6
5
|
import SuggestionsEventSource from '../suggestions-event-source';
|
|
7
6
|
/*
|
|
8
7
|
* Types & constants
|
|
@@ -62,11 +61,8 @@ export default async function askQuestion(
|
|
|
62
61
|
): Promise< SuggestionsEventSource > {
|
|
63
62
|
debug( 'Asking question: %o. options: %o', question, { postId, fromCache, feature, functions } );
|
|
64
63
|
|
|
65
|
-
const { token } = await requestJwt();
|
|
66
|
-
|
|
67
64
|
return new SuggestionsEventSource( {
|
|
68
65
|
question,
|
|
69
|
-
token,
|
|
70
66
|
options: { postId, feature, fromCache, functions },
|
|
71
67
|
} );
|
|
72
68
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
### AIControl
|
|
2
|
+
|
|
3
|
+
#### Properties
|
|
4
|
+
|
|
5
|
+
- `disabled` (**boolean**) (Optional): Disables the ai control. Default value is `false`.
|
|
6
|
+
- `value` (**string**): Current input value. Default value is `''`.
|
|
7
|
+
- `placeholder` (**string**) (Optional): Placeholder text for the input field. Default value is `''`.
|
|
8
|
+
- `showAccept` (**boolean**) (Optional): Determines if the accept button is shown. Default value is `false`.
|
|
9
|
+
- `acceptLabel` (**string**) (Optional): Label text for the accept button. Default value is `'Accept'`.
|
|
10
|
+
- `showButtonsLabel` (**boolean**) (Optional): Determines if button labels are shown. Default value is `true`.
|
|
11
|
+
- `isOpaque` (**boolean**) (Optional): Controls the opacity of the component. Default value is `false`.
|
|
12
|
+
- `state` (**RequestingStateProp**) (Optional): Determines the state of the component. Default value is `'init'`.
|
|
13
|
+
- `onChange` (**Function**) (Optional): Handler for input change. Default action is no operation.
|
|
14
|
+
- `onSend` (**Function**) (Optional): Handler to send a request. Default action is no operation.
|
|
15
|
+
- `onStop` (**Function**) (Optional): Handler to stop a request. Default action is no operation.
|
|
16
|
+
- `onAccept` (**Function**) (Optional): Handler to accept the input. Default action is no operation.
|
|
17
|
+
|
|
18
|
+
#### Example Usage
|
|
19
|
+
|
|
20
|
+
```jsx
|
|
21
|
+
<AIControl
|
|
22
|
+
value="Type here"
|
|
23
|
+
placeholder="Placeholder text"
|
|
24
|
+
onChange={ handleChange }
|
|
25
|
+
onSend={ handleSend }
|
|
26
|
+
onStop={ handleStop }
|
|
27
|
+
onAccept={ handleAccept }
|
|
28
|
+
/>
|
|
29
|
+
```
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { PlainText } from '@wordpress/block-editor';
|
|
5
|
-
import { Button
|
|
5
|
+
import { Button } from '@wordpress/components';
|
|
6
|
+
import { useKeyboardShortcut } from '@wordpress/compose';
|
|
6
7
|
import { useRef } from '@wordpress/element';
|
|
7
8
|
import { __ } from '@wordpress/i18n';
|
|
8
9
|
import { Icon, closeSmall, check, arrowUp } from '@wordpress/icons';
|
|
@@ -10,8 +11,13 @@ import classNames from 'classnames';
|
|
|
10
11
|
/**
|
|
11
12
|
* Internal dependencies
|
|
12
13
|
*/
|
|
13
|
-
import { aiAssistantIcon } from '../../icons';
|
|
14
14
|
import './style.scss';
|
|
15
|
+
import AiStatusIndicator from '../ai-status-indicator';
|
|
16
|
+
import { GuidelineMessage } from './message';
|
|
17
|
+
/**
|
|
18
|
+
* Types
|
|
19
|
+
*/
|
|
20
|
+
import type { RequestingStateProp } from '../../types';
|
|
15
21
|
|
|
16
22
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
17
23
|
const noop = () => {};
|
|
@@ -20,13 +26,14 @@ const noop = () => {};
|
|
|
20
26
|
* AI Control component.
|
|
21
27
|
*
|
|
22
28
|
* @param {object} props - component props
|
|
23
|
-
* @param {boolean} props.
|
|
29
|
+
* @param {boolean} props.disabled - is disabled
|
|
24
30
|
* @param {string} props.value - input value
|
|
25
31
|
* @param {string} props.placeholder - input placeholder
|
|
26
32
|
* @param {boolean} props.showAccept - show accept button
|
|
27
33
|
* @param {string} props.acceptLabel - accept button label
|
|
28
34
|
* @param {boolean} props.showButtonsLabel - show buttons label
|
|
29
35
|
* @param {boolean} props.isOpaque - is opaque
|
|
36
|
+
* @param {string} props.state - requesting state
|
|
30
37
|
* @param {Function} props.onChange - input change handler
|
|
31
38
|
* @param {Function} props.onSend - send request handler
|
|
32
39
|
* @param {Function} props.onStop - stop request handler
|
|
@@ -34,31 +41,62 @@ const noop = () => {};
|
|
|
34
41
|
* @returns {object} - AI Control component
|
|
35
42
|
*/
|
|
36
43
|
export default function AIControl( {
|
|
37
|
-
|
|
44
|
+
disabled = false,
|
|
38
45
|
value = '',
|
|
39
46
|
placeholder = '',
|
|
40
47
|
showAccept = false,
|
|
41
48
|
acceptLabel = __( 'Accept', 'jetpack-ai-client' ),
|
|
42
49
|
showButtonsLabel = true,
|
|
43
50
|
isOpaque = false,
|
|
51
|
+
state = 'init',
|
|
44
52
|
onChange = noop,
|
|
45
53
|
onSend = noop,
|
|
46
54
|
onStop = noop,
|
|
47
55
|
onAccept = noop,
|
|
48
56
|
}: {
|
|
49
|
-
|
|
57
|
+
disabled?: boolean;
|
|
50
58
|
value: string;
|
|
51
59
|
placeholder?: string;
|
|
52
60
|
showAccept?: boolean;
|
|
53
61
|
acceptLabel?: string;
|
|
54
62
|
showButtonsLabel?: boolean;
|
|
55
63
|
isOpaque?: boolean;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
state?: RequestingStateProp;
|
|
65
|
+
onChange?: ( newValue: string ) => void;
|
|
66
|
+
onSend?: ( currentValue: string ) => void;
|
|
67
|
+
onStop?: () => void;
|
|
68
|
+
onAccept?: () => void;
|
|
60
69
|
} ) {
|
|
61
70
|
const promptUserInputRef = useRef( null );
|
|
71
|
+
const loading = state === 'requesting' || state === 'suggesting';
|
|
72
|
+
const showGuideLine = ! ( loading || disabled || value?.length || isOpaque );
|
|
73
|
+
|
|
74
|
+
useKeyboardShortcut(
|
|
75
|
+
'mod+enter',
|
|
76
|
+
() => {
|
|
77
|
+
if ( showAccept ) {
|
|
78
|
+
onAccept?.();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
target: promptUserInputRef,
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
useKeyboardShortcut(
|
|
87
|
+
'enter',
|
|
88
|
+
e => {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
onSend?.( value );
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
target: promptUserInputRef,
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const actionButtonClasses = classNames( 'jetpack-components-ai-control__controls-prompt_button', {
|
|
98
|
+
'has-label': showButtonsLabel,
|
|
99
|
+
} );
|
|
62
100
|
|
|
63
101
|
return (
|
|
64
102
|
<div className="jetpack-components-ai-control__container">
|
|
@@ -67,64 +105,67 @@ export default function AIControl( {
|
|
|
67
105
|
'is-opaque': isOpaque,
|
|
68
106
|
} ) }
|
|
69
107
|
>
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
108
|
+
<AiStatusIndicator state={ state } />
|
|
109
|
+
|
|
110
|
+
<div className="jetpack-components-ai-control__input-wrapper">
|
|
111
|
+
<PlainText
|
|
112
|
+
value={ value }
|
|
113
|
+
onChange={ onChange }
|
|
114
|
+
placeholder={ placeholder }
|
|
115
|
+
className="jetpack-components-ai-control__input"
|
|
116
|
+
disabled={ loading || disabled }
|
|
117
|
+
ref={ promptUserInputRef }
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{ value?.length > 0 && (
|
|
122
|
+
<Button
|
|
123
|
+
icon={ closeSmall }
|
|
124
|
+
className="jetpack-components-ai-control__clear"
|
|
125
|
+
onClick={ () => onChange( '' ) }
|
|
126
|
+
/>
|
|
127
|
+
) }
|
|
128
|
+
|
|
129
|
+
<div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
|
|
130
|
+
{ ! loading ? (
|
|
131
|
+
<Button
|
|
132
|
+
className={ actionButtonClasses }
|
|
133
|
+
onClick={ () => onSend( value ) }
|
|
134
|
+
isSmall={ true }
|
|
135
|
+
disabled={ ! value?.length || disabled }
|
|
136
|
+
label={ __( 'Send request', 'jetpack-ai-client' ) }
|
|
137
|
+
>
|
|
138
|
+
<Icon icon={ arrowUp } />
|
|
139
|
+
{ showButtonsLabel && __( 'Send', 'jetpack-ai-client' ) }
|
|
140
|
+
</Button>
|
|
73
141
|
) : (
|
|
74
|
-
<
|
|
142
|
+
<Button
|
|
143
|
+
className={ actionButtonClasses }
|
|
144
|
+
onClick={ onStop }
|
|
145
|
+
isSmall={ true }
|
|
146
|
+
label={ __( 'Stop request', 'jetpack-ai-client' ) }
|
|
147
|
+
>
|
|
148
|
+
<Icon icon={ closeSmall } />
|
|
149
|
+
{ showButtonsLabel && __( 'Stop', 'jetpack-ai-client' ) }
|
|
150
|
+
</Button>
|
|
75
151
|
) }
|
|
76
152
|
</div>
|
|
77
153
|
|
|
78
|
-
|
|
79
|
-
value={ value }
|
|
80
|
-
onChange={ onChange }
|
|
81
|
-
placeholder={ placeholder }
|
|
82
|
-
className="jetpack-components-ai-control__input"
|
|
83
|
-
disabled={ loading }
|
|
84
|
-
ref={ promptUserInputRef }
|
|
85
|
-
/>
|
|
86
|
-
|
|
87
|
-
<div className="jetpack-components-ai-control__controls">
|
|
154
|
+
{ showAccept && (
|
|
88
155
|
<div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{ showButtonsLabel && __( 'Send', 'jetpack-ai-client' ) }
|
|
99
|
-
</Button>
|
|
100
|
-
) : (
|
|
101
|
-
<Button
|
|
102
|
-
className="jetpack-components-ai-control__controls-prompt_button"
|
|
103
|
-
onClick={ onStop }
|
|
104
|
-
isSmall={ true }
|
|
105
|
-
label={ __( 'Stop request', 'jetpack-ai-client' ) }
|
|
106
|
-
>
|
|
107
|
-
<Icon icon={ closeSmall } />
|
|
108
|
-
{ showButtonsLabel && __( 'Stop', 'jetpack-ai-client' ) }
|
|
109
|
-
</Button>
|
|
110
|
-
) }
|
|
156
|
+
<Button
|
|
157
|
+
className={ actionButtonClasses }
|
|
158
|
+
onClick={ onAccept }
|
|
159
|
+
isSmall={ true }
|
|
160
|
+
label={ acceptLabel }
|
|
161
|
+
>
|
|
162
|
+
<Icon icon={ check } />
|
|
163
|
+
{ showButtonsLabel && acceptLabel }
|
|
164
|
+
</Button>
|
|
111
165
|
</div>
|
|
112
|
-
|
|
113
|
-
{ showAccept && (
|
|
114
|
-
<div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
|
|
115
|
-
<Button
|
|
116
|
-
className="jetpack-components-ai-control__controls-prompt_button"
|
|
117
|
-
onClick={ onAccept }
|
|
118
|
-
isSmall={ true }
|
|
119
|
-
label={ acceptLabel }
|
|
120
|
-
>
|
|
121
|
-
<Icon icon={ check } />
|
|
122
|
-
{ acceptLabel }
|
|
123
|
-
</Button>
|
|
124
|
-
</div>
|
|
125
|
-
) }
|
|
126
|
-
</div>
|
|
166
|
+
) }
|
|
127
167
|
</div>
|
|
168
|
+
{ showGuideLine && <GuidelineMessage /> }
|
|
128
169
|
</div>
|
|
129
170
|
);
|
|
130
171
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { ExternalLink } from '@wordpress/components';
|
|
5
|
+
import { createInterpolateElement } from '@wordpress/element';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
import {
|
|
8
|
+
Icon,
|
|
9
|
+
warning,
|
|
10
|
+
info,
|
|
11
|
+
cancelCircleFilled as error,
|
|
12
|
+
check as success,
|
|
13
|
+
} from '@wordpress/icons';
|
|
14
|
+
/**
|
|
15
|
+
* Types
|
|
16
|
+
*/
|
|
17
|
+
import type React from 'react';
|
|
18
|
+
|
|
19
|
+
import './style.scss';
|
|
20
|
+
|
|
21
|
+
export const MESSAGE_SEVERITY_WARNING = 'warning';
|
|
22
|
+
export const MESSAGE_SEVERITY_ERROR = 'error';
|
|
23
|
+
export const MESSAGE_SEVERITY_SUCCESS = 'success';
|
|
24
|
+
export const MESSAGE_SEVERITY_INFO = 'info';
|
|
25
|
+
|
|
26
|
+
const messageSeverityTypes = [
|
|
27
|
+
MESSAGE_SEVERITY_WARNING,
|
|
28
|
+
MESSAGE_SEVERITY_ERROR,
|
|
29
|
+
MESSAGE_SEVERITY_SUCCESS,
|
|
30
|
+
MESSAGE_SEVERITY_INFO,
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
export type MessageSeverityProp = ( typeof messageSeverityTypes )[ number ] | null;
|
|
34
|
+
|
|
35
|
+
export type MessageProps = {
|
|
36
|
+
icon?: React.ReactNode;
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
severity: MessageSeverityProp;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const messageIconsMap = {
|
|
42
|
+
[ MESSAGE_SEVERITY_WARNING ]: warning,
|
|
43
|
+
[ MESSAGE_SEVERITY_ERROR ]: error,
|
|
44
|
+
[ MESSAGE_SEVERITY_SUCCESS ]: success,
|
|
45
|
+
[ MESSAGE_SEVERITY_INFO ]: info,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* React component to render a block message.
|
|
50
|
+
*
|
|
51
|
+
* @param {MessageProps} props - Component props.
|
|
52
|
+
* @returns {React.ReactElement } Banner component.
|
|
53
|
+
*/
|
|
54
|
+
export default function Message( {
|
|
55
|
+
severity = null,
|
|
56
|
+
icon = null,
|
|
57
|
+
children,
|
|
58
|
+
}: MessageProps ): React.ReactElement {
|
|
59
|
+
return (
|
|
60
|
+
<div className="jetpack-ai-assistant__message">
|
|
61
|
+
{ ( severity || icon ) && <Icon icon={ messageIconsMap[ severity ] || icon } /> }
|
|
62
|
+
<div className="jetpack-ai-assistant__message-content">{ children }</div>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* React component to render a guideline message.
|
|
69
|
+
*
|
|
70
|
+
* @returns {React.ReactElement } - Message component.
|
|
71
|
+
*/
|
|
72
|
+
export function GuidelineMessage(): React.ReactElement {
|
|
73
|
+
return (
|
|
74
|
+
<Message severity={ MESSAGE_SEVERITY_INFO }>
|
|
75
|
+
{ createInterpolateElement(
|
|
76
|
+
__(
|
|
77
|
+
'AI-generated content could be inaccurate or biased. <link>Learn more</link>',
|
|
78
|
+
'jetpack-ai-client'
|
|
79
|
+
),
|
|
80
|
+
{
|
|
81
|
+
link: <ExternalLink href="https://automattic.com/ai-guidelines" />,
|
|
82
|
+
}
|
|
83
|
+
) }
|
|
84
|
+
</Message>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
+
import { action } from '@storybook/addon-actions';
|
|
4
5
|
import { useState } from '@wordpress/element';
|
|
5
6
|
import React from 'react';
|
|
6
7
|
/**
|
|
7
8
|
* Internal dependencies
|
|
8
9
|
*/
|
|
10
|
+
import { REQUESTING_STATES } from '../../../types';
|
|
9
11
|
import AIControl from '../index';
|
|
10
12
|
/**
|
|
11
13
|
* Types
|
|
@@ -15,7 +17,26 @@ import type { Meta } from '@storybook/react';
|
|
|
15
17
|
export default {
|
|
16
18
|
title: 'JS Packages/AI Client/AI Control',
|
|
17
19
|
component: AIControl,
|
|
18
|
-
|
|
20
|
+
decorators: [
|
|
21
|
+
Story => (
|
|
22
|
+
<div style={ { backgroundColor: 'white' } }>
|
|
23
|
+
<Story />
|
|
24
|
+
</div>
|
|
25
|
+
),
|
|
26
|
+
],
|
|
27
|
+
argTypes: {
|
|
28
|
+
requestingState: {
|
|
29
|
+
control: {
|
|
30
|
+
type: 'select',
|
|
31
|
+
},
|
|
32
|
+
options: REQUESTING_STATES,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
parameters: {
|
|
36
|
+
controls: {
|
|
37
|
+
exclude: /on[A-Z].*/,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
19
40
|
} as Meta< typeof AIControl >;
|
|
20
41
|
|
|
21
42
|
const Template = args => {
|
|
@@ -33,9 +54,14 @@ const DefaultArgs = {
|
|
|
33
54
|
loading: false,
|
|
34
55
|
isOpaque: false,
|
|
35
56
|
placeholder: '',
|
|
57
|
+
requestingState: 'init',
|
|
36
58
|
showButtonsLabel: true,
|
|
37
59
|
showAccept: false,
|
|
38
60
|
acceptLabel: 'Accept',
|
|
61
|
+
onChange: action( 'onChange' ),
|
|
62
|
+
onSend: action( 'onSend' ),
|
|
63
|
+
onStop: action( 'onStop' ),
|
|
64
|
+
onAccept: action( 'onAccept' ),
|
|
39
65
|
};
|
|
40
66
|
|
|
41
67
|
export const Default = Template.bind( {} );
|