@automattic/jetpack-ai-client 0.2.1 → 0.3.1
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 +11 -0
- package/build/components/ai-control/index.d.ts +14 -26
- package/build/components/ai-control/index.js +48 -13
- package/build/components/ai-status-indicator/index.d.ts +1 -1
- package/build/components/ai-status-indicator/index.js +3 -7
- package/build/components/index.d.ts +1 -1
- package/build/components/index.js +1 -1
- package/package.json +15 -15
- package/src/components/ai-control/Readme.md +7 -1
- package/src/components/ai-control/index.tsx +184 -91
- package/src/components/ai-control/style.scss +43 -72
- package/src/components/ai-status-indicator/index.tsx +3 -10
- package/src/components/ai-status-indicator/style.scss +12 -45
- package/src/components/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ 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.3.1] - 2024-01-04
|
|
9
|
+
### Changed
|
|
10
|
+
- Updated package dependencies. [#34815]
|
|
11
|
+
|
|
12
|
+
## [0.3.0] - 2023-12-20
|
|
13
|
+
### Changed
|
|
14
|
+
- AI Client: improved usability with new block positioning, prompt and suggestion action buttons. [#34383]
|
|
15
|
+
- Updated package dependencies. [#34696]
|
|
16
|
+
|
|
8
17
|
## [0.2.1] - 2023-12-03
|
|
9
18
|
### Changed
|
|
10
19
|
- Updated the prompt shadow for a better sense of depth. [#34362]
|
|
@@ -171,6 +180,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
171
180
|
- Updated package dependencies. [#31659]
|
|
172
181
|
- Updated package dependencies. [#31785]
|
|
173
182
|
|
|
183
|
+
[0.3.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.3.0...v0.3.1
|
|
184
|
+
[0.3.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.2.1...v0.3.0
|
|
174
185
|
[0.2.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.2.0...v0.2.1
|
|
175
186
|
[0.2.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.16...v0.2.0
|
|
176
187
|
[0.1.16]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.15...v0.1.16
|
|
@@ -7,14 +7,7 @@ import './style.scss';
|
|
|
7
7
|
* Types
|
|
8
8
|
*/
|
|
9
9
|
import type { RequestingStateProp } from '../../types';
|
|
10
|
-
|
|
11
|
-
* AI Control component.
|
|
12
|
-
*
|
|
13
|
-
* @param {AIControlProps} props - Component props.
|
|
14
|
-
* @param {React.MutableRefObject} ref - Ref to the component.
|
|
15
|
-
* @returns {React.ReactElement} Rendered component.
|
|
16
|
-
*/
|
|
17
|
-
export declare function AIControl({ disabled, value, placeholder, showAccept, acceptLabel, showButtonLabels, isTransparent, state, showClearButton, showGuideLine, onChange, onSend, onStop, onAccept, }: {
|
|
10
|
+
type AiControlProps = {
|
|
18
11
|
disabled?: boolean;
|
|
19
12
|
value: string;
|
|
20
13
|
placeholder?: string;
|
|
@@ -23,27 +16,22 @@ export declare function AIControl({ disabled, value, placeholder, showAccept, ac
|
|
|
23
16
|
showButtonLabels?: boolean;
|
|
24
17
|
isTransparent?: boolean;
|
|
25
18
|
state?: RequestingStateProp;
|
|
26
|
-
showClearButton?: boolean;
|
|
27
|
-
showGuideLine?: boolean;
|
|
28
|
-
onChange?: (newValue: string) => void;
|
|
29
|
-
onSend?: (currentValue: string) => void;
|
|
30
|
-
onStop?: () => void;
|
|
31
|
-
onAccept?: () => void;
|
|
32
|
-
}, ref: React.MutableRefObject<null>): React.ReactElement;
|
|
33
|
-
declare const _default: React.ForwardRefExoticComponent<{
|
|
34
|
-
disabled?: boolean;
|
|
35
|
-
value: string;
|
|
36
|
-
placeholder?: string;
|
|
37
|
-
showAccept?: boolean;
|
|
38
|
-
acceptLabel?: string;
|
|
39
|
-
showButtonLabels?: boolean;
|
|
40
|
-
isTransparent?: boolean;
|
|
41
|
-
state?: "init" | "requesting" | "suggesting" | "done" | "error";
|
|
42
|
-
showClearButton?: boolean;
|
|
43
19
|
showGuideLine?: boolean;
|
|
20
|
+
customFooter?: React.ReactElement;
|
|
44
21
|
onChange?: (newValue: string) => void;
|
|
45
22
|
onSend?: (currentValue: string) => void;
|
|
46
23
|
onStop?: () => void;
|
|
47
24
|
onAccept?: () => void;
|
|
48
|
-
|
|
25
|
+
onDiscard?: () => void;
|
|
26
|
+
showRemove?: boolean;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* AI Control component.
|
|
30
|
+
*
|
|
31
|
+
* @param {AiControlProps} props - Component props.
|
|
32
|
+
* @param {React.MutableRefObject} ref - Ref to the component.
|
|
33
|
+
* @returns {React.ReactElement} Rendered component.
|
|
34
|
+
*/
|
|
35
|
+
export declare function AIControl({ disabled, value, placeholder, showAccept, acceptLabel, showButtonLabels, isTransparent, state, showGuideLine, customFooter, onChange, onSend, onStop, onAccept, onDiscard, showRemove, }: AiControlProps, ref: React.MutableRefObject<null>): React.ReactElement;
|
|
36
|
+
declare const _default: React.ForwardRefExoticComponent<AiControlProps & React.RefAttributes<null>>;
|
|
49
37
|
export default _default;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* External dependencies
|
|
4
4
|
*/
|
|
5
5
|
import { PlainText } from '@wordpress/block-editor';
|
|
6
|
-
import { Button } from '@wordpress/components';
|
|
6
|
+
import { Button, ButtonGroup } from '@wordpress/components';
|
|
7
7
|
import { useKeyboardShortcut } from '@wordpress/compose';
|
|
8
|
-
import { forwardRef, useImperativeHandle, useRef } from '@wordpress/element';
|
|
8
|
+
import { forwardRef, useImperativeHandle, useRef, useEffect, useCallback, } from '@wordpress/element';
|
|
9
9
|
import { __ } from '@wordpress/i18n';
|
|
10
|
-
import { Icon, closeSmall, check, arrowUp } from '@wordpress/icons';
|
|
10
|
+
import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons';
|
|
11
11
|
import classNames from 'classnames';
|
|
12
|
+
import React from 'react';
|
|
12
13
|
/**
|
|
13
14
|
* Internal dependencies
|
|
14
15
|
*/
|
|
@@ -20,14 +21,51 @@ const noop = () => { };
|
|
|
20
21
|
/**
|
|
21
22
|
* AI Control component.
|
|
22
23
|
*
|
|
23
|
-
* @param {
|
|
24
|
+
* @param {AiControlProps} props - Component props.
|
|
24
25
|
* @param {React.MutableRefObject} ref - Ref to the component.
|
|
25
26
|
* @returns {React.ReactElement} Rendered component.
|
|
26
27
|
*/
|
|
27
|
-
export function AIControl({ disabled = false, value = '', placeholder = '', showAccept = false, acceptLabel = __('Accept', 'jetpack-ai-client'), showButtonLabels = true, isTransparent = false, state = 'init',
|
|
28
|
+
export function AIControl({ disabled = false, value = '', placeholder = '', showAccept = false, acceptLabel = __('Accept', 'jetpack-ai-client'), showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, customFooter = null, onChange = noop, onSend = noop, onStop = noop, onAccept = noop, onDiscard = null, showRemove = false, }, ref // eslint-disable-line @typescript-eslint/ban-types
|
|
28
29
|
) {
|
|
29
30
|
const promptUserInputRef = useRef(null);
|
|
30
31
|
const loading = state === 'requesting' || state === 'suggesting';
|
|
32
|
+
const [editRequest, setEditRequest] = React.useState(false);
|
|
33
|
+
const [lastValue, setLastValue] = React.useState(value || null);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (editRequest) {
|
|
36
|
+
promptUserInputRef?.current?.focus();
|
|
37
|
+
}
|
|
38
|
+
if (!editRequest && lastValue !== null && value !== lastValue) {
|
|
39
|
+
onChange?.(lastValue);
|
|
40
|
+
}
|
|
41
|
+
}, [editRequest, lastValue, value]);
|
|
42
|
+
const sendRequest = useCallback(() => {
|
|
43
|
+
setLastValue(value);
|
|
44
|
+
setEditRequest(false);
|
|
45
|
+
onSend?.(value);
|
|
46
|
+
}, [value]);
|
|
47
|
+
const changeHandler = useCallback((newValue) => {
|
|
48
|
+
onChange?.(newValue);
|
|
49
|
+
if (state === 'init') {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!lastValue) {
|
|
53
|
+
// here we're coming from a one-click action
|
|
54
|
+
setEditRequest(newValue.length > 0);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// here we're coming from an edit action
|
|
58
|
+
setEditRequest(newValue !== lastValue);
|
|
59
|
+
}
|
|
60
|
+
}, [lastValue, state]);
|
|
61
|
+
const discardHandler = useCallback(() => {
|
|
62
|
+
onDiscard?.();
|
|
63
|
+
onAccept?.();
|
|
64
|
+
}, []);
|
|
65
|
+
const cancelEdit = useCallback(() => {
|
|
66
|
+
onChange(lastValue || '');
|
|
67
|
+
setEditRequest(false);
|
|
68
|
+
}, [lastValue]);
|
|
31
69
|
// Pass the ref to forwardRef.
|
|
32
70
|
useImperativeHandle(ref, () => promptUserInputRef.current);
|
|
33
71
|
useKeyboardShortcut('mod+enter', () => {
|
|
@@ -39,15 +77,12 @@ export function AIControl({ disabled = false, value = '', placeholder = '', show
|
|
|
39
77
|
});
|
|
40
78
|
useKeyboardShortcut('enter', e => {
|
|
41
79
|
e.preventDefault();
|
|
42
|
-
|
|
80
|
+
sendRequest();
|
|
43
81
|
}, {
|
|
44
82
|
target: promptUserInputRef,
|
|
45
83
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return (_jsxs("div", { className: "jetpack-components-ai-control__container", children: [_jsxs("div", { className: classNames('jetpack-components-ai-control__wrapper', {
|
|
50
|
-
'is-transparent': isTransparent,
|
|
51
|
-
}), children: [_jsx(AiStatusIndicator, { state: state }), _jsx("div", { className: "jetpack-components-ai-control__input-wrapper", children: _jsx(PlainText, { value: value, onChange: onChange, placeholder: placeholder, className: "jetpack-components-ai-control__input", disabled: loading || disabled, ref: promptUserInputRef }) }), value?.length > 0 && showClearButton && (_jsx(Button, { icon: closeSmall, className: "jetpack-components-ai-control__clear", onClick: () => onChange('') })), _jsx("div", { className: "jetpack-components-ai-control__controls-prompt_button_wrapper", children: !loading ? (_jsxs(Button, { className: actionButtonClasses, onClick: () => onSend?.(value), isSmall: true, disabled: !value?.length || disabled, label: __('Send request', 'jetpack-ai-client'), children: [_jsx(Icon, { icon: arrowUp }), showButtonLabels && __('Send', 'jetpack-ai-client')] })) : (_jsxs(Button, { className: actionButtonClasses, onClick: onStop, isSmall: true, label: __('Stop request', 'jetpack-ai-client'), children: [_jsx(Icon, { icon: closeSmall }), showButtonLabels && __('Stop (ESC)', 'jetpack-ai-client')] })) }), showAccept && (_jsx("div", { className: "jetpack-components-ai-control__controls-prompt_button_wrapper", children: _jsxs(Button, { className: actionButtonClasses, onClick: onAccept, isSmall: true, label: acceptLabel, children: [_jsx(Icon, { icon: check }), showButtonLabels && acceptLabel] }) }))] }), showGuideLine && _jsx(GuidelineMessage, {})] }));
|
|
84
|
+
return (_jsx("div", { className: "jetpack-components-ai-control__container-wrapper", children: _jsxs("div", { className: "jetpack-components-ai-control__container", children: [_jsxs("div", { className: classNames('jetpack-components-ai-control__wrapper', {
|
|
85
|
+
'is-transparent': isTransparent,
|
|
86
|
+
}), children: [_jsx(AiStatusIndicator, { state: state }), _jsx("div", { className: "jetpack-components-ai-control__input-wrapper", children: _jsx(PlainText, { value: value, onChange: changeHandler, placeholder: placeholder, className: "jetpack-components-ai-control__input", disabled: loading || disabled, ref: promptUserInputRef }) }), (!showAccept || editRequest) && (_jsx("div", { className: "jetpack-components-ai-control__controls-prompt_button_wrapper", children: !loading ? (_jsxs(_Fragment, { children: [editRequest && (_jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", onClick: cancelEdit, variant: "secondary", label: __('Cancel', 'jetpack-ai-client'), children: showButtonLabels ? (__('Cancel', 'jetpack-ai-client')) : (_jsx(Icon, { icon: closeSmall })) })), showRemove && !editRequest && !value?.length && onDiscard && (_jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", onClick: discardHandler, variant: "secondary", label: __('Cancel', 'jetpack-ai-client'), children: showButtonLabels ? (__('Cancel', 'jetpack-ai-client')) : (_jsx(Icon, { icon: closeSmall })) })), value?.length > 0 && (_jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", onClick: sendRequest, variant: "primary", disabled: !value?.length || disabled, label: __('Send request', 'jetpack-ai-client'), children: showButtonLabels ? (__('Generate', 'jetpack-ai-client')) : (_jsx(Icon, { icon: arrowUp })) }))] })) : (_jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", onClick: onStop, variant: "secondary", label: __('Stop request', 'jetpack-ai-client'), children: showButtonLabels ? (__('Stop', 'jetpack-ai-client')) : (_jsx(Icon, { icon: closeSmall })) })) })), showAccept && !editRequest && (_jsxs("div", { className: "jetpack-components-ai-control__controls-prompt_button_wrapper", children: [(value?.length > 0 || lastValue === null) && (_jsxs(ButtonGroup, { children: [_jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", label: __('Discard', 'jetpack-ai-client'), onClick: discardHandler, tooltipPosition: "top", children: _jsx(Icon, { icon: trash }) }), _jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", label: __('Regenerate', 'jetpack-ai-client'), onClick: () => onSend?.(value), tooltipPosition: "top", disabled: !value?.length || value === null || disabled, children: _jsx(Icon, { icon: reusableBlock }) })] })), _jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", onClick: onAccept, variant: "primary", label: acceptLabel, children: showButtonLabels ? acceptLabel : _jsx(Icon, { icon: check }) })] }))] }), showGuideLine && !loading && !editRequest && (customFooter || _jsx(GuidelineMessage, {}))] }) }));
|
|
52
87
|
}
|
|
53
88
|
export default forwardRef(AIControl);
|
|
@@ -12,4 +12,4 @@ export type AiStatusIndicatorProps = {
|
|
|
12
12
|
* @param {AiStatusIndicatorProps} props - component props.
|
|
13
13
|
* @returns {React.ReactElement} - rendered component.
|
|
14
14
|
*/
|
|
15
|
-
export default function AiStatusIndicator({ state
|
|
15
|
+
export default function AiStatusIndicator({ state }: AiStatusIndicatorProps): React.ReactElement;
|
|
@@ -2,12 +2,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
/**
|
|
3
3
|
* External dependencies
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { Spinner } from '@wordpress/components';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
|
-
/*
|
|
8
|
-
* Internal dependencies
|
|
9
|
-
*/
|
|
10
|
-
import { aiAssistantIcon } from '../../icons';
|
|
11
7
|
import './style.scss';
|
|
12
8
|
/**
|
|
13
9
|
* AiStatusIndicator component.
|
|
@@ -15,8 +11,8 @@ import './style.scss';
|
|
|
15
11
|
* @param {AiStatusIndicatorProps} props - component props.
|
|
16
12
|
* @returns {React.ReactElement} - rendered component.
|
|
17
13
|
*/
|
|
18
|
-
export default function AiStatusIndicator({ state
|
|
14
|
+
export default function AiStatusIndicator({ state }) {
|
|
19
15
|
return (_jsx("div", { className: classNames('jetpack-ai-status-indicator__icon-wrapper', {
|
|
20
16
|
[`is-${state}`]: true,
|
|
21
|
-
}), children: _jsx(
|
|
17
|
+
}), children: _jsx(Spinner, {}) }));
|
|
22
18
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default as AIControl } from './ai-control';
|
|
2
2
|
export { default as AiStatusIndicator } from './ai-status-indicator';
|
|
3
3
|
export { default as AudioDurationDisplay } from './audio-duration-display';
|
|
4
|
-
export { GuidelineMessage } from './ai-control/message';
|
|
4
|
+
export { GuidelineMessage, default as FooterMessage } from './ai-control/message';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default as AIControl } from './ai-control';
|
|
2
2
|
export { default as AiStatusIndicator } from './ai-status-indicator';
|
|
3
3
|
export { default as AudioDurationDisplay } from './audio-duration-display';
|
|
4
|
-
export { GuidelineMessage } from './ai-control/message';
|
|
4
|
+
export { GuidelineMessage, default as FooterMessage } from './ai-control/message';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-ai-client",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
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": {
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
},
|
|
23
23
|
"type": "module",
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@storybook/addon-actions": "7.
|
|
26
|
-
"@storybook/blocks": "7.
|
|
27
|
-
"@storybook/react": "7.
|
|
25
|
+
"@storybook/addon-actions": "7.6.5",
|
|
26
|
+
"@storybook/blocks": "7.6.5",
|
|
27
|
+
"@storybook/react": "7.6.5",
|
|
28
28
|
"jest": "^29.6.2",
|
|
29
29
|
"jest-environment-jsdom": "29.7.0",
|
|
30
30
|
"typescript": "5.0.4"
|
|
@@ -38,19 +38,19 @@
|
|
|
38
38
|
"main": "./build/index.js",
|
|
39
39
|
"types": "./build/index.d.ts",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@automattic/jetpack-base-styles": "^0.6.
|
|
42
|
-
"@automattic/jetpack-connection": "^0.30.
|
|
43
|
-
"@automattic/jetpack-shared-extension-utils": "^0.13.
|
|
41
|
+
"@automattic/jetpack-base-styles": "^0.6.15",
|
|
42
|
+
"@automattic/jetpack-connection": "^0.30.12",
|
|
43
|
+
"@automattic/jetpack-shared-extension-utils": "^0.13.7",
|
|
44
44
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
45
45
|
"@types/react": "18.2.33",
|
|
46
|
-
"@wordpress/api-fetch": "6.
|
|
47
|
-
"@wordpress/block-editor": "12.
|
|
48
|
-
"@wordpress/components": "25.
|
|
49
|
-
"@wordpress/compose": "6.
|
|
50
|
-
"@wordpress/data": "9.
|
|
51
|
-
"@wordpress/element": "5.
|
|
52
|
-
"@wordpress/i18n": "4.
|
|
53
|
-
"@wordpress/icons": "9.
|
|
46
|
+
"@wordpress/api-fetch": "6.45.0",
|
|
47
|
+
"@wordpress/block-editor": "12.16.0",
|
|
48
|
+
"@wordpress/components": "25.14.0",
|
|
49
|
+
"@wordpress/compose": "6.25.0",
|
|
50
|
+
"@wordpress/data": "9.18.0",
|
|
51
|
+
"@wordpress/element": "5.25.0",
|
|
52
|
+
"@wordpress/i18n": "4.48.0",
|
|
53
|
+
"@wordpress/icons": "9.39.0",
|
|
54
54
|
"classnames": "2.3.2",
|
|
55
55
|
"debug": "4.3.4",
|
|
56
56
|
"react": "18.2.0",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
- `isTransparent` (**boolean**) (Optional): Controls the opacity of the component. Default value is `false`.
|
|
12
12
|
- `state` (**RequestingStateProp**) (Optional): Determines the state of the component. Default value is `'init'`.
|
|
13
13
|
- `showClearButton` (**boolean**) (Optional): Determines if the clear button is shown when the input has a value. Default value is `true`.
|
|
14
|
+
- `showGuideLine`: (**boolean**) (Optional): Whether to show the usual AI guidelines at the bottom of the input.
|
|
15
|
+
- `customFooter`: (**ReactElement**) (Optional): if provided together with `showGuideLine` it will be rendered at the bottom of the input.
|
|
14
16
|
- `onChange` (**Function**) (Optional): Handler for input change. Default action is no operation.
|
|
15
17
|
- `onSend` (**Function**) (Optional): Handler to send a request. Default action is no operation.
|
|
16
18
|
- `onStop` (**Function**) (Optional): Handler to stop a request. Default action is no operation.
|
|
@@ -19,6 +21,8 @@
|
|
|
19
21
|
#### Example Usage
|
|
20
22
|
|
|
21
23
|
```jsx
|
|
24
|
+
import { AIControl, FooterMessage } from '@automattic/jetpack-ai-client';
|
|
25
|
+
|
|
22
26
|
<AIControl
|
|
23
27
|
value="Type here"
|
|
24
28
|
placeholder="Placeholder text"
|
|
@@ -26,5 +30,7 @@
|
|
|
26
30
|
onSend={ handleSend }
|
|
27
31
|
onStop={ handleStop }
|
|
28
32
|
onAccept={ handleAccept }
|
|
33
|
+
showGuideLine={ true }
|
|
34
|
+
customFooter={ <FooterMessage severity="info">Remember AI suggestions can be inaccurate</FooterMessage> }
|
|
29
35
|
/>
|
|
30
|
-
```
|
|
36
|
+
```
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { PlainText } from '@wordpress/block-editor';
|
|
5
|
-
import { Button } from '@wordpress/components';
|
|
5
|
+
import { Button, ButtonGroup } from '@wordpress/components';
|
|
6
6
|
import { useKeyboardShortcut } from '@wordpress/compose';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
forwardRef,
|
|
9
|
+
useImperativeHandle,
|
|
10
|
+
useRef,
|
|
11
|
+
useEffect,
|
|
12
|
+
useCallback,
|
|
13
|
+
} from '@wordpress/element';
|
|
8
14
|
import { __ } from '@wordpress/i18n';
|
|
9
|
-
import { Icon, closeSmall, check, arrowUp } from '@wordpress/icons';
|
|
15
|
+
import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons';
|
|
10
16
|
import classNames from 'classnames';
|
|
11
17
|
import React from 'react';
|
|
12
18
|
/**
|
|
@@ -19,7 +25,7 @@ import { GuidelineMessage } from './message';
|
|
|
19
25
|
* Types
|
|
20
26
|
*/
|
|
21
27
|
import type { RequestingStateProp } from '../../types';
|
|
22
|
-
type
|
|
28
|
+
type AiControlProps = {
|
|
23
29
|
disabled?: boolean;
|
|
24
30
|
value: string;
|
|
25
31
|
placeholder?: string;
|
|
@@ -28,12 +34,14 @@ type AIControlProps = {
|
|
|
28
34
|
showButtonLabels?: boolean;
|
|
29
35
|
isTransparent?: boolean;
|
|
30
36
|
state?: RequestingStateProp;
|
|
31
|
-
showClearButton?: boolean;
|
|
32
37
|
showGuideLine?: boolean;
|
|
38
|
+
customFooter?: React.ReactElement;
|
|
33
39
|
onChange?: ( newValue: string ) => void;
|
|
34
40
|
onSend?: ( currentValue: string ) => void;
|
|
35
41
|
onStop?: () => void;
|
|
36
42
|
onAccept?: () => void;
|
|
43
|
+
onDiscard?: () => void;
|
|
44
|
+
showRemove?: boolean;
|
|
37
45
|
};
|
|
38
46
|
|
|
39
47
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
@@ -42,7 +50,7 @@ const noop = () => {};
|
|
|
42
50
|
/**
|
|
43
51
|
* AI Control component.
|
|
44
52
|
*
|
|
45
|
-
* @param {
|
|
53
|
+
* @param {AiControlProps} props - Component props.
|
|
46
54
|
* @param {React.MutableRefObject} ref - Ref to the component.
|
|
47
55
|
* @returns {React.ReactElement} Rendered component.
|
|
48
56
|
*/
|
|
@@ -56,32 +64,65 @@ export function AIControl(
|
|
|
56
64
|
showButtonLabels = true,
|
|
57
65
|
isTransparent = false,
|
|
58
66
|
state = 'init',
|
|
59
|
-
showClearButton = true,
|
|
60
67
|
showGuideLine = false,
|
|
68
|
+
customFooter = null,
|
|
61
69
|
onChange = noop,
|
|
62
70
|
onSend = noop,
|
|
63
71
|
onStop = noop,
|
|
64
72
|
onAccept = noop,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
placeholder?: string;
|
|
69
|
-
showAccept?: boolean;
|
|
70
|
-
acceptLabel?: string;
|
|
71
|
-
showButtonLabels?: boolean;
|
|
72
|
-
isTransparent?: boolean;
|
|
73
|
-
state?: RequestingStateProp;
|
|
74
|
-
showClearButton?: boolean;
|
|
75
|
-
showGuideLine?: boolean;
|
|
76
|
-
onChange?: ( newValue: string ) => void;
|
|
77
|
-
onSend?: ( currentValue: string ) => void;
|
|
78
|
-
onStop?: () => void;
|
|
79
|
-
onAccept?: () => void;
|
|
80
|
-
},
|
|
73
|
+
onDiscard = null,
|
|
74
|
+
showRemove = false,
|
|
75
|
+
}: AiControlProps,
|
|
81
76
|
ref: React.MutableRefObject< null > // eslint-disable-line @typescript-eslint/ban-types
|
|
82
77
|
): React.ReactElement {
|
|
83
78
|
const promptUserInputRef = useRef( null );
|
|
84
79
|
const loading = state === 'requesting' || state === 'suggesting';
|
|
80
|
+
const [ editRequest, setEditRequest ] = React.useState( false );
|
|
81
|
+
const [ lastValue, setLastValue ] = React.useState( value || null );
|
|
82
|
+
|
|
83
|
+
useEffect( () => {
|
|
84
|
+
if ( editRequest ) {
|
|
85
|
+
promptUserInputRef?.current?.focus();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if ( ! editRequest && lastValue !== null && value !== lastValue ) {
|
|
89
|
+
onChange?.( lastValue );
|
|
90
|
+
}
|
|
91
|
+
}, [ editRequest, lastValue, value ] );
|
|
92
|
+
|
|
93
|
+
const sendRequest = useCallback( () => {
|
|
94
|
+
setLastValue( value );
|
|
95
|
+
setEditRequest( false );
|
|
96
|
+
onSend?.( value );
|
|
97
|
+
}, [ value ] );
|
|
98
|
+
|
|
99
|
+
const changeHandler = useCallback(
|
|
100
|
+
( newValue: string ) => {
|
|
101
|
+
onChange?.( newValue );
|
|
102
|
+
if ( state === 'init' ) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if ( ! lastValue ) {
|
|
107
|
+
// here we're coming from a one-click action
|
|
108
|
+
setEditRequest( newValue.length > 0 );
|
|
109
|
+
} else {
|
|
110
|
+
// here we're coming from an edit action
|
|
111
|
+
setEditRequest( newValue !== lastValue );
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
[ lastValue, state ]
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const discardHandler = useCallback( () => {
|
|
118
|
+
onDiscard?.();
|
|
119
|
+
onAccept?.();
|
|
120
|
+
}, [] );
|
|
121
|
+
|
|
122
|
+
const cancelEdit = useCallback( () => {
|
|
123
|
+
onChange( lastValue || '' );
|
|
124
|
+
setEditRequest( false );
|
|
125
|
+
}, [ lastValue ] );
|
|
85
126
|
|
|
86
127
|
// Pass the ref to forwardRef.
|
|
87
128
|
useImperativeHandle( ref, () => promptUserInputRef.current );
|
|
@@ -102,85 +143,137 @@ export function AIControl(
|
|
|
102
143
|
'enter',
|
|
103
144
|
e => {
|
|
104
145
|
e.preventDefault();
|
|
105
|
-
|
|
146
|
+
sendRequest();
|
|
106
147
|
},
|
|
107
148
|
{
|
|
108
149
|
target: promptUserInputRef,
|
|
109
150
|
}
|
|
110
151
|
);
|
|
111
152
|
|
|
112
|
-
const actionButtonClasses = classNames( 'jetpack-components-ai-control__controls-prompt_button', {
|
|
113
|
-
'has-label': showButtonLabels,
|
|
114
|
-
} );
|
|
115
|
-
|
|
116
153
|
return (
|
|
117
|
-
<div className="jetpack-components-ai-control__container">
|
|
118
|
-
<div
|
|
119
|
-
|
|
120
|
-
'
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
154
|
+
<div className="jetpack-components-ai-control__container-wrapper">
|
|
155
|
+
<div className="jetpack-components-ai-control__container">
|
|
156
|
+
<div
|
|
157
|
+
className={ classNames( 'jetpack-components-ai-control__wrapper', {
|
|
158
|
+
'is-transparent': isTransparent,
|
|
159
|
+
} ) }
|
|
160
|
+
>
|
|
161
|
+
<AiStatusIndicator state={ state } />
|
|
162
|
+
|
|
163
|
+
<div className="jetpack-components-ai-control__input-wrapper">
|
|
164
|
+
<PlainText
|
|
165
|
+
value={ value }
|
|
166
|
+
onChange={ changeHandler }
|
|
167
|
+
placeholder={ placeholder }
|
|
168
|
+
className="jetpack-components-ai-control__input"
|
|
169
|
+
disabled={ loading || disabled }
|
|
170
|
+
ref={ promptUserInputRef }
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
{ ( ! showAccept || editRequest ) && (
|
|
175
|
+
<div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
|
|
176
|
+
{ ! loading ? (
|
|
177
|
+
<>
|
|
178
|
+
{ editRequest && (
|
|
179
|
+
<Button
|
|
180
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
181
|
+
onClick={ cancelEdit }
|
|
182
|
+
variant="secondary"
|
|
183
|
+
label={ __( 'Cancel', 'jetpack-ai-client' ) }
|
|
184
|
+
>
|
|
185
|
+
{ showButtonLabels ? (
|
|
186
|
+
__( 'Cancel', 'jetpack-ai-client' )
|
|
187
|
+
) : (
|
|
188
|
+
<Icon icon={ closeSmall } />
|
|
189
|
+
) }
|
|
190
|
+
</Button>
|
|
191
|
+
) }
|
|
135
192
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
193
|
+
{ showRemove && ! editRequest && ! value?.length && onDiscard && (
|
|
194
|
+
<Button
|
|
195
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
196
|
+
onClick={ discardHandler }
|
|
197
|
+
variant="secondary"
|
|
198
|
+
label={ __( 'Cancel', 'jetpack-ai-client' ) }
|
|
199
|
+
>
|
|
200
|
+
{ showButtonLabels ? (
|
|
201
|
+
__( 'Cancel', 'jetpack-ai-client' )
|
|
202
|
+
) : (
|
|
203
|
+
<Icon icon={ closeSmall } />
|
|
204
|
+
) }
|
|
205
|
+
</Button>
|
|
206
|
+
) }
|
|
207
|
+
|
|
208
|
+
{ value?.length > 0 && (
|
|
209
|
+
<Button
|
|
210
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
211
|
+
onClick={ sendRequest }
|
|
212
|
+
variant="primary"
|
|
213
|
+
disabled={ ! value?.length || disabled }
|
|
214
|
+
label={ __( 'Send request', 'jetpack-ai-client' ) }
|
|
215
|
+
>
|
|
216
|
+
{ showButtonLabels ? (
|
|
217
|
+
__( 'Generate', 'jetpack-ai-client' )
|
|
218
|
+
) : (
|
|
219
|
+
<Icon icon={ arrowUp } />
|
|
220
|
+
) }
|
|
221
|
+
</Button>
|
|
222
|
+
) }
|
|
223
|
+
</>
|
|
224
|
+
) : (
|
|
225
|
+
<Button
|
|
226
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
227
|
+
onClick={ onStop }
|
|
228
|
+
variant="secondary"
|
|
229
|
+
label={ __( 'Stop request', 'jetpack-ai-client' ) }
|
|
230
|
+
>
|
|
231
|
+
{ showButtonLabels ? (
|
|
232
|
+
__( 'Stop', 'jetpack-ai-client' )
|
|
233
|
+
) : (
|
|
234
|
+
<Icon icon={ closeSmall } />
|
|
235
|
+
) }
|
|
236
|
+
</Button>
|
|
237
|
+
) }
|
|
238
|
+
</div>
|
|
166
239
|
) }
|
|
167
|
-
</div>
|
|
168
240
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
241
|
+
{ showAccept && ! editRequest && (
|
|
242
|
+
<div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
|
|
243
|
+
{ ( value?.length > 0 || lastValue === null ) && (
|
|
244
|
+
<ButtonGroup>
|
|
245
|
+
<Button
|
|
246
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
247
|
+
label={ __( 'Discard', 'jetpack-ai-client' ) }
|
|
248
|
+
onClick={ discardHandler }
|
|
249
|
+
tooltipPosition="top"
|
|
250
|
+
>
|
|
251
|
+
<Icon icon={ trash } />
|
|
252
|
+
</Button>
|
|
253
|
+
<Button
|
|
254
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
255
|
+
label={ __( 'Regenerate', 'jetpack-ai-client' ) }
|
|
256
|
+
onClick={ () => onSend?.( value ) }
|
|
257
|
+
tooltipPosition="top"
|
|
258
|
+
disabled={ ! value?.length || value === null || disabled }
|
|
259
|
+
>
|
|
260
|
+
<Icon icon={ reusableBlock } />
|
|
261
|
+
</Button>
|
|
262
|
+
</ButtonGroup>
|
|
263
|
+
) }
|
|
264
|
+
<Button
|
|
265
|
+
className="jetpack-components-ai-control__controls-prompt_button"
|
|
266
|
+
onClick={ onAccept }
|
|
267
|
+
variant="primary"
|
|
268
|
+
label={ acceptLabel }
|
|
269
|
+
>
|
|
270
|
+
{ showButtonLabels ? acceptLabel : <Icon icon={ check } /> }
|
|
271
|
+
</Button>
|
|
272
|
+
</div>
|
|
273
|
+
) }
|
|
274
|
+
</div>
|
|
275
|
+
{ showGuideLine && ! loading && ! editRequest && ( customFooter || <GuidelineMessage /> ) }
|
|
182
276
|
</div>
|
|
183
|
-
{ showGuideLine && <GuidelineMessage /> }
|
|
184
277
|
</div>
|
|
185
278
|
);
|
|
186
279
|
}
|
|
@@ -2,20 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
// AI CONTROL
|
|
4
4
|
|
|
5
|
+
.jetpack-components-ai-control__container-wrapper {
|
|
6
|
+
position: sticky;
|
|
7
|
+
bottom: 16px;
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
.jetpack-components-ai-control__container {
|
|
6
11
|
color: var( --jp-gray-80 );
|
|
7
12
|
background-color: var( --jp-white );
|
|
8
13
|
box-shadow: 0px 12px 15px 0px rgba(0, 0, 0, 0.12), 0px 3px 9px 0px rgba(0, 0, 0, 0.12), 0px 1px 3px 0px rgba(0, 0, 0, 0.15);
|
|
9
14
|
font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
10
|
-
|
|
15
|
+
border-radius: 6px;
|
|
16
|
+
border: 1px solid var(--gutenberg-gray-400, #CCC);
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
.jetpack-components-ai-control__wrapper {
|
|
14
20
|
display: flex;
|
|
15
|
-
padding: 12px
|
|
16
|
-
|
|
17
|
-
align-items: center;
|
|
21
|
+
padding: 8px 8px 8px var(--grid-unit-15, 12px);
|
|
22
|
+
align-items: flex-end;
|
|
18
23
|
box-sizing: border-box;
|
|
24
|
+
border-radius: 6px 6px 0 0;
|
|
25
|
+
gap: 6px;
|
|
19
26
|
|
|
20
27
|
&.is-transparent {
|
|
21
28
|
opacity: 0.4;
|
|
@@ -23,10 +30,11 @@
|
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
.jetpack-components-ai-control__input-wrapper {
|
|
26
|
-
position: relative;
|
|
27
33
|
display: flex;
|
|
28
34
|
flex-grow: 1;
|
|
29
|
-
|
|
35
|
+
align-self: center;
|
|
36
|
+
min-height: 36px; // need compat height with buttons wrapper
|
|
37
|
+
align-items: center;
|
|
30
38
|
|
|
31
39
|
textarea.jetpack-components-ai-control__input {
|
|
32
40
|
background-color: var( --jp-white );
|
|
@@ -63,67 +71,28 @@
|
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
.jetpack-components-ai-control__clear.components-button.has-icon {
|
|
67
|
-
width: 24px;
|
|
68
|
-
min-width: 24px;
|
|
69
|
-
height: 24px;
|
|
70
|
-
padding: 0;
|
|
71
|
-
border-radius: 50%;
|
|
72
|
-
background-color: black;
|
|
73
|
-
opacity: 0.2;
|
|
74
|
-
color: white;
|
|
75
|
-
|
|
76
|
-
&:hover {
|
|
77
|
-
opacity: 0.4;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.jetpack-components-ai-controlton__icon {
|
|
82
|
-
flex-shrink: 0;
|
|
83
|
-
display: flex;
|
|
84
|
-
align-items: center;
|
|
85
|
-
justify-content: center;
|
|
86
|
-
|
|
87
|
-
.input-icon {
|
|
88
|
-
// Brand green regardless of theme
|
|
89
|
-
color: var( --jp-green-40 );
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.input-spinner {
|
|
93
|
-
margin: 0;
|
|
94
|
-
width: 24px;
|
|
95
|
-
height: 24px;
|
|
96
|
-
|
|
97
|
-
path {
|
|
98
|
-
color: var( --jp-green-40 );
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
74
|
.jetpack-components-ai-control__controls-prompt_button_wrapper {
|
|
104
75
|
text-transform: uppercase;
|
|
105
76
|
font-size: 11px;
|
|
106
77
|
font-weight: 600;
|
|
107
|
-
line-height:
|
|
78
|
+
line-height: 1em;
|
|
108
79
|
user-select: none;
|
|
109
80
|
white-space: nowrap;
|
|
110
81
|
display: flex;
|
|
111
82
|
align-items: center;
|
|
83
|
+
gap: 8px;
|
|
112
84
|
|
|
113
85
|
.components-button.is-small:not(.has-label) {
|
|
114
86
|
padding: 0;
|
|
115
87
|
}
|
|
116
|
-
}
|
|
117
88
|
|
|
118
|
-
.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
&:hover,
|
|
123
|
-
&:active {
|
|
124
|
-
color: var( --wp-components-color-accent, var( --wp-admin-theme-color ) );
|
|
89
|
+
.components-button-group .components-button {
|
|
90
|
+
box-shadow: none;
|
|
91
|
+
padding: 6px 8px;
|
|
125
92
|
}
|
|
93
|
+
}
|
|
126
94
|
|
|
95
|
+
.jetpack-components-ai-control__controls-prompt_button {
|
|
127
96
|
&:disabled {
|
|
128
97
|
opacity: 0.6;
|
|
129
98
|
cursor: not-allowed;
|
|
@@ -134,24 +103,26 @@
|
|
|
134
103
|
|
|
135
104
|
.jetpack-ai-assistant__message {
|
|
136
105
|
display: flex;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
106
|
+
line-height: 28px;
|
|
107
|
+
font-size: 12px;
|
|
108
|
+
align-self: center;
|
|
109
|
+
align-items: center;
|
|
110
|
+
background-color: var( --jp-white-off );
|
|
111
|
+
padding: 0 12px;
|
|
112
|
+
border-radius: 0 0 6px 6px;
|
|
113
|
+
|
|
114
|
+
> svg {
|
|
115
|
+
fill: var( --jp-gray-40 );
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.jetpack-ai-assistant__message-content {
|
|
119
|
+
flex-grow: 2;
|
|
120
|
+
margin: 0 8px;
|
|
121
|
+
color: var( --jp-gray-50 );
|
|
122
|
+
line-height: 1.4em;
|
|
123
|
+
|
|
124
|
+
.components-external-link {
|
|
125
|
+
color: var( --jp-gray-50 );
|
|
126
|
+
}
|
|
127
|
+
}
|
|
157
128
|
}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { Spinner } from '@wordpress/components';
|
|
5
5
|
import classNames from 'classnames';
|
|
6
|
-
/*
|
|
7
|
-
* Internal dependencies
|
|
8
|
-
*/
|
|
9
|
-
import { aiAssistantIcon } from '../../icons';
|
|
10
6
|
/*
|
|
11
7
|
* Types
|
|
12
8
|
*/
|
|
@@ -27,17 +23,14 @@ export type AiStatusIndicatorProps = {
|
|
|
27
23
|
* @param {AiStatusIndicatorProps} props - component props.
|
|
28
24
|
* @returns {React.ReactElement} - rendered component.
|
|
29
25
|
*/
|
|
30
|
-
export default function AiStatusIndicator( {
|
|
31
|
-
state,
|
|
32
|
-
size = 24,
|
|
33
|
-
}: AiStatusIndicatorProps ): React.ReactElement {
|
|
26
|
+
export default function AiStatusIndicator( { state }: AiStatusIndicatorProps ): React.ReactElement {
|
|
34
27
|
return (
|
|
35
28
|
<div
|
|
36
29
|
className={ classNames( 'jetpack-ai-status-indicator__icon-wrapper', {
|
|
37
30
|
[ `is-${ state }` ]: true,
|
|
38
31
|
} ) }
|
|
39
32
|
>
|
|
40
|
-
<
|
|
33
|
+
<Spinner />
|
|
41
34
|
</div>
|
|
42
35
|
);
|
|
43
36
|
}
|
|
@@ -1,52 +1,19 @@
|
|
|
1
1
|
@import '@automattic/jetpack-base-styles/style';
|
|
2
2
|
|
|
3
3
|
.jetpack-ai-status-indicator__icon-wrapper {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
animation: fade 800ms linear infinite;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
&.is-suggesting {
|
|
17
|
-
color: var( --jp-green-30 );
|
|
18
|
-
.ai-assistant-icon > path {
|
|
19
|
-
animation: fadingSpark 300ms ease-in-out alternate infinite;
|
|
20
|
-
|
|
21
|
-
&.spark-first {
|
|
22
|
-
animation-delay: 0.2s;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
&.spark-second {
|
|
26
|
-
animation-delay: 0.6s;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
&.is-done {
|
|
32
|
-
color: var( --jp-green-40 );
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
&.is-error {
|
|
36
|
-
color: var( --jp-yellow-30 );
|
|
4
|
+
transition: opacity 0.25s ease-in-out, width 0.25s;
|
|
5
|
+
width: 0;
|
|
6
|
+
opacity: 0;
|
|
7
|
+
align-self: baseline;
|
|
8
|
+
|
|
9
|
+
> svg {
|
|
10
|
+
height: 24px;
|
|
11
|
+
width: 24px;
|
|
12
|
+
margin: 6px 0 0;
|
|
37
13
|
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
14
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
15
|
+
&.is-requesting, &.is-suggesting {
|
|
16
|
+
opacity: 1;
|
|
17
|
+
width: 24px;
|
|
44
18
|
}
|
|
45
19
|
}
|
|
46
|
-
|
|
47
|
-
@keyframes fade {
|
|
48
|
-
0% { opacity: 1; }
|
|
49
|
-
50% { opacity: 0.4; }
|
|
50
|
-
100% { opacity: 1; }
|
|
51
|
-
}
|
|
52
|
-
|
package/src/components/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default as AIControl } from './ai-control';
|
|
2
2
|
export { default as AiStatusIndicator } from './ai-status-indicator';
|
|
3
3
|
export { default as AudioDurationDisplay } from './audio-duration-display';
|
|
4
|
-
export { GuidelineMessage } from './ai-control/message';
|
|
4
|
+
export { GuidelineMessage, default as FooterMessage } from './ai-control/message';
|