@automattic/jetpack-ai-client 0.1.4 → 0.1.6
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 +30 -0
- package/index.ts +1 -0
- package/package.json +12 -12
- package/src/components/ai-control/Readme.md +3 -2
- package/src/components/ai-control/index.tsx +28 -25
- package/src/components/ai-control/style.scss +2 -1
- package/src/hooks/use-media-recording/Readme.md +48 -0
- package/src/hooks/use-media-recording/index.ts +204 -0
- package/src/icons/index.ts +4 -0
- package/src/icons/mic.tsx +29 -0
- package/src/icons/player-pause.tsx +18 -0
- package/src/icons/player-play.tsx +20 -0
- package/src/icons/player-stop.tsx +17 -0
- package/src/types.ts +2 -1
- package/.gitattributes +0 -7
- package/jest.config.cjs +0 -5
- package/src/components/ai-control/stories/index.stories.tsx +0 -68
- package/src/components/ai-status-indicator/stories/index.mdx +0 -25
- package/src/components/ai-status-indicator/stories/index.stories.tsx +0 -84
- package/src/icons/stories/index.stories.tsx +0 -46
- package/src/icons/stories/style.module.scss +0 -21
- package/tests/index.test.js +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ 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.6] - 2023-09-04
|
|
9
|
+
### Added
|
|
10
|
+
- AI Client: add play and pause icons [#32788]
|
|
11
|
+
- AI Client: add player stop button icon [#32728]
|
|
12
|
+
- AI Client: create blob audio data. Introduce onDone() callback [#32791]
|
|
13
|
+
- AI Client: improve useMediaRecorder() timeslice recording option [#32805]
|
|
14
|
+
- AI Client: introduce useMediaRecording() hook [#32767]
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- AI Client: minor change in useMediaRecording() hook example [#32769]
|
|
18
|
+
- Updated package dependencies. [#32803]
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
- Remove unnecessary files from mirror repo and published package. [#32674]
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- AI Client: fix mic icon visual issue in Safari [#32787]
|
|
25
|
+
|
|
26
|
+
## [0.1.5] - 2023-08-28
|
|
27
|
+
### Added
|
|
28
|
+
- AI Client: add mic icon [#32665]
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- AI Assistant: Change messages to turn content optional and start supporting a context property. [#32495]
|
|
32
|
+
- AI Extension: Add showClearButton prop to AIControl component and fix names [#32682]
|
|
33
|
+
- AI Extension: Specify input background color [#32628]
|
|
34
|
+
- Updated package dependencies. [#32605]
|
|
35
|
+
|
|
8
36
|
## [0.1.4] - 2023-08-14
|
|
9
37
|
### Added
|
|
10
38
|
- AI Client: Add border-box in AIControl. [#32419]
|
|
@@ -82,6 +110,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
82
110
|
- Updated package dependencies. [#31659]
|
|
83
111
|
- Updated package dependencies. [#31785]
|
|
84
112
|
|
|
113
|
+
[0.1.6]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.5...v0.1.6
|
|
114
|
+
[0.1.5]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.4...v0.1.5
|
|
85
115
|
[0.1.4]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.3...v0.1.4
|
|
86
116
|
[0.1.3]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.2...v0.1.3
|
|
87
117
|
[0.1.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.1...v0.1.2
|
package/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { default as askQuestion } from './src/ask-question';
|
|
|
9
9
|
* Hooks
|
|
10
10
|
*/
|
|
11
11
|
export { default as useAiSuggestions } from './src/hooks/use-ai-suggestions';
|
|
12
|
+
export { default as useMediaRecording } from './src/hooks/use-media-recording';
|
|
12
13
|
|
|
13
14
|
/*
|
|
14
15
|
* Components: Icons
|
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.6",
|
|
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": {
|
|
@@ -32,18 +32,18 @@
|
|
|
32
32
|
".": "./index.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@automattic/jetpack-base-styles": "^0.6.
|
|
36
|
-
"@automattic/jetpack-connection": "
|
|
37
|
-
"@automattic/jetpack-shared-extension-utils": "^0.11.
|
|
35
|
+
"@automattic/jetpack-base-styles": "^0.6.8",
|
|
36
|
+
"@automattic/jetpack-connection": "^0.29.9",
|
|
37
|
+
"@automattic/jetpack-shared-extension-utils": "^0.11.4",
|
|
38
38
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
39
|
-
"@wordpress/api-fetch": "6.
|
|
40
|
-
"@wordpress/block-editor": "12.
|
|
41
|
-
"@wordpress/components": "25.
|
|
42
|
-
"@wordpress/compose": "6.
|
|
43
|
-
"@wordpress/data": "9.
|
|
44
|
-
"@wordpress/element": "5.
|
|
45
|
-
"@wordpress/i18n": "4.
|
|
46
|
-
"@wordpress/icons": "9.
|
|
39
|
+
"@wordpress/api-fetch": "6.38.0",
|
|
40
|
+
"@wordpress/block-editor": "12.9.0",
|
|
41
|
+
"@wordpress/components": "25.7.0",
|
|
42
|
+
"@wordpress/compose": "6.18.0",
|
|
43
|
+
"@wordpress/data": "9.11.0",
|
|
44
|
+
"@wordpress/element": "5.18.0",
|
|
45
|
+
"@wordpress/i18n": "4.41.0",
|
|
46
|
+
"@wordpress/icons": "9.32.0",
|
|
47
47
|
"classnames": "2.3.2",
|
|
48
48
|
"debug": "4.3.4",
|
|
49
49
|
"react": "18.2.0",
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
- `placeholder` (**string**) (Optional): Placeholder text for the input field. Default value is `''`.
|
|
8
8
|
- `showAccept` (**boolean**) (Optional): Determines if the accept button is shown. Default value is `false`.
|
|
9
9
|
- `acceptLabel` (**string**) (Optional): Label text for the accept button. Default value is `'Accept'`.
|
|
10
|
-
- `
|
|
11
|
-
- `
|
|
10
|
+
- `showButtonLabels` (**boolean**) (Optional): Determines if button labels are shown. Default value is `true`.
|
|
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
|
+
- `showClearButton` (**boolean**) (Optional): Determines if the clear button is shown when the input has a value. Default value is `true`.
|
|
13
14
|
- `onChange` (**Function**) (Optional): Handler for input change. Default action is no operation.
|
|
14
15
|
- `onSend` (**Function**) (Optional): Handler to send a request. Default action is no operation.
|
|
15
16
|
- `onStop` (**Function**) (Optional): Handler to stop a request. Default action is no operation.
|
|
@@ -26,20 +26,21 @@ const noop = () => {};
|
|
|
26
26
|
/**
|
|
27
27
|
* AI Control component.
|
|
28
28
|
*
|
|
29
|
-
* @param {object} props
|
|
30
|
-
* @param {boolean} props.disabled
|
|
31
|
-
* @param {string} props.value
|
|
32
|
-
* @param {string} props.placeholder
|
|
33
|
-
* @param {boolean} props.showAccept
|
|
34
|
-
* @param {string} props.acceptLabel
|
|
35
|
-
* @param {boolean} props.
|
|
36
|
-
* @param {boolean} props.
|
|
37
|
-
* @param {string} props.state
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {Function} props.
|
|
40
|
-
* @param {Function} props.
|
|
41
|
-
* @param {Function} props.
|
|
42
|
-
* @param {
|
|
29
|
+
* @param {object} props - Component props
|
|
30
|
+
* @param {boolean} props.disabled - Input disabled state
|
|
31
|
+
* @param {string} props.value - The input value
|
|
32
|
+
* @param {string} props.placeholder - The input placeholder
|
|
33
|
+
* @param {boolean} props.showAccept - Whether to show the accept button
|
|
34
|
+
* @param {string} props.acceptLabel - The accept button label
|
|
35
|
+
* @param {boolean} props.showButtonLabels - Whether to show the button labels
|
|
36
|
+
* @param {boolean} props.isTransparent - Whether the component has low opacity
|
|
37
|
+
* @param {string} props.state - The request state
|
|
38
|
+
* @param {boolean} props.showClearButton - Whether to show the clear button when the input has a value
|
|
39
|
+
* @param {Function} props.onChange - Input change handler
|
|
40
|
+
* @param {Function} props.onSend - Request send handler
|
|
41
|
+
* @param {Function} props.onStop - Request stop handler
|
|
42
|
+
* @param {Function} props.onAccept - Response accept handler
|
|
43
|
+
* @param {object} ref - Auto injected ref from react
|
|
43
44
|
* @returns {object} - AI Control component
|
|
44
45
|
*/
|
|
45
46
|
export function AIControl(
|
|
@@ -49,9 +50,10 @@ export function AIControl(
|
|
|
49
50
|
placeholder = '',
|
|
50
51
|
showAccept = false,
|
|
51
52
|
acceptLabel = __( 'Accept', 'jetpack-ai-client' ),
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
showButtonLabels = true,
|
|
54
|
+
isTransparent = false,
|
|
54
55
|
state = 'init',
|
|
56
|
+
showClearButton = true,
|
|
55
57
|
onChange = noop,
|
|
56
58
|
onSend = noop,
|
|
57
59
|
onStop = noop,
|
|
@@ -62,9 +64,10 @@ export function AIControl(
|
|
|
62
64
|
placeholder?: string;
|
|
63
65
|
showAccept?: boolean;
|
|
64
66
|
acceptLabel?: string;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
showButtonLabels?: boolean;
|
|
68
|
+
isTransparent?: boolean;
|
|
67
69
|
state?: RequestingStateProp;
|
|
70
|
+
showClearButton?: boolean;
|
|
68
71
|
onChange?: ( newValue: string ) => void;
|
|
69
72
|
onSend?: ( currentValue: string ) => void;
|
|
70
73
|
onStop?: () => void;
|
|
@@ -74,7 +77,7 @@ export function AIControl(
|
|
|
74
77
|
) {
|
|
75
78
|
const promptUserInputRef = useRef( null );
|
|
76
79
|
const loading = state === 'requesting' || state === 'suggesting';
|
|
77
|
-
const showGuideLine = ! ( loading || disabled || value?.length ||
|
|
80
|
+
const showGuideLine = ! ( loading || disabled || value?.length || isTransparent );
|
|
78
81
|
|
|
79
82
|
// Pass the ref to forwardRef.
|
|
80
83
|
useImperativeHandle( ref, () => promptUserInputRef.current );
|
|
@@ -103,14 +106,14 @@ export function AIControl(
|
|
|
103
106
|
);
|
|
104
107
|
|
|
105
108
|
const actionButtonClasses = classNames( 'jetpack-components-ai-control__controls-prompt_button', {
|
|
106
|
-
'has-label':
|
|
109
|
+
'has-label': showButtonLabels,
|
|
107
110
|
} );
|
|
108
111
|
|
|
109
112
|
return (
|
|
110
113
|
<div className="jetpack-components-ai-control__container">
|
|
111
114
|
<div
|
|
112
115
|
className={ classNames( 'jetpack-components-ai-control__wrapper', {
|
|
113
|
-
'is-
|
|
116
|
+
'is-transparent': isTransparent,
|
|
114
117
|
} ) }
|
|
115
118
|
>
|
|
116
119
|
<AiStatusIndicator state={ state } />
|
|
@@ -126,7 +129,7 @@ export function AIControl(
|
|
|
126
129
|
/>
|
|
127
130
|
</div>
|
|
128
131
|
|
|
129
|
-
{ value?.length > 0 && (
|
|
132
|
+
{ value?.length > 0 && showClearButton && (
|
|
130
133
|
<Button
|
|
131
134
|
icon={ closeSmall }
|
|
132
135
|
className="jetpack-components-ai-control__clear"
|
|
@@ -144,7 +147,7 @@ export function AIControl(
|
|
|
144
147
|
label={ __( 'Send request', 'jetpack-ai-client' ) }
|
|
145
148
|
>
|
|
146
149
|
<Icon icon={ arrowUp } />
|
|
147
|
-
{
|
|
150
|
+
{ showButtonLabels && __( 'Send', 'jetpack-ai-client' ) }
|
|
148
151
|
</Button>
|
|
149
152
|
) : (
|
|
150
153
|
<Button
|
|
@@ -154,7 +157,7 @@ export function AIControl(
|
|
|
154
157
|
label={ __( 'Stop request', 'jetpack-ai-client' ) }
|
|
155
158
|
>
|
|
156
159
|
<Icon icon={ closeSmall } />
|
|
157
|
-
{
|
|
160
|
+
{ showButtonLabels && __( 'Stop', 'jetpack-ai-client' ) }
|
|
158
161
|
</Button>
|
|
159
162
|
) }
|
|
160
163
|
</div>
|
|
@@ -168,7 +171,7 @@ export function AIControl(
|
|
|
168
171
|
label={ acceptLabel }
|
|
169
172
|
>
|
|
170
173
|
<Icon icon={ check } />
|
|
171
|
-
{
|
|
174
|
+
{ showButtonLabels && acceptLabel }
|
|
172
175
|
</Button>
|
|
173
176
|
</div>
|
|
174
177
|
) }
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
align-items: center;
|
|
18
18
|
box-sizing: border-box;
|
|
19
19
|
|
|
20
|
-
&.is-
|
|
20
|
+
&.is-transparent {
|
|
21
21
|
opacity: 0.4;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
width: 100%;
|
|
30
30
|
|
|
31
31
|
textarea.jetpack-components-ai-control__input {
|
|
32
|
+
background-color: var( --jp-white );
|
|
32
33
|
width: 100%;
|
|
33
34
|
min-height: 20px;
|
|
34
35
|
border-radius: 2px;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# `useMediaRecording` Custom React Hook
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
`useMediaRecording` is a custom React hook for handling media recording functionalities in a React application. It provides an easy way to start, pause, resume, and stop media recording, as well as to track the current recording state.
|
|
6
|
+
|
|
7
|
+
Based on [MediaRecorder](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder) API
|
|
8
|
+
|
|
9
|
+
## API
|
|
10
|
+
|
|
11
|
+
The hook returns an object with the following properties and methods:
|
|
12
|
+
|
|
13
|
+
- `start: ( timeslice ) => void`: Start the media recording
|
|
14
|
+
- `pause: () => void`: Pause the current media recording
|
|
15
|
+
- `resume: () => void`: Resume a paused recording
|
|
16
|
+
- `stop: () => void`: Stop the current recording
|
|
17
|
+
- `state: 'inactive' | 'recording' | 'paused'`: Current recording state
|
|
18
|
+
|
|
19
|
+
## Example
|
|
20
|
+
|
|
21
|
+
Here's an example React component that utilizes the `useMediaRecording` hook.
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import useMediaRecording from './useMediaRecording';
|
|
25
|
+
|
|
26
|
+
const MediaRecorderComponent = () => {
|
|
27
|
+
const { start, pause, resume, stop, state } = useMediaRecording();
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div>
|
|
31
|
+
<h1>Media Recorder</h1>
|
|
32
|
+
<p>Current State: { state }</p>
|
|
33
|
+
<button onClick={ start } disabled={ state !== 'inactive' }>
|
|
34
|
+
Start
|
|
35
|
+
</button>
|
|
36
|
+
<button onClick={ pause } disabled={ state !== 'recording' }>
|
|
37
|
+
Pause
|
|
38
|
+
</button>
|
|
39
|
+
<button onClick={ resume } disabled={ state !== 'paused' }>
|
|
40
|
+
Resume
|
|
41
|
+
</button>
|
|
42
|
+
<button onClick={ stop } disabled={ state === 'inactive' }>
|
|
43
|
+
Stop
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
```
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useRef, useState, useEffect, useCallback } from '@wordpress/element';
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Types
|
|
8
|
+
*/
|
|
9
|
+
type RecordingStateProp = 'inactive' | 'recording' | 'paused';
|
|
10
|
+
type UseMediaRecordingProps = {
|
|
11
|
+
onDone?: ( blob: Blob ) => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type UseMediaRecordingReturn = {
|
|
15
|
+
/**
|
|
16
|
+
* The current recording state
|
|
17
|
+
*/
|
|
18
|
+
state: RecordingStateProp;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The recorded blob
|
|
22
|
+
*/
|
|
23
|
+
blob: Blob | null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `start` recording handler
|
|
27
|
+
*/
|
|
28
|
+
start: ( timeslice?: number ) => void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `pause` recording handler
|
|
32
|
+
*/
|
|
33
|
+
pause: () => void;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `resume` recording handler
|
|
37
|
+
*/
|
|
38
|
+
resume: () => void;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `stop` recording handler
|
|
42
|
+
*/
|
|
43
|
+
stop: () => void;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type MediaRecorderEvent = {
|
|
47
|
+
data: Blob;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* react custom hook to handle media recording.
|
|
52
|
+
*
|
|
53
|
+
* @param {UseMediaRecordingProps} props - The props
|
|
54
|
+
* @returns {UseMediaRecordingReturn} The media recorder instance
|
|
55
|
+
*/
|
|
56
|
+
export default function useMediaRecording( {
|
|
57
|
+
onDone,
|
|
58
|
+
}: UseMediaRecordingProps = {} ): UseMediaRecordingReturn {
|
|
59
|
+
// Reference to the media recorder instance
|
|
60
|
+
const mediaRecordRef = useRef( null );
|
|
61
|
+
|
|
62
|
+
// Recording state: `inactive`, `recording`, `paused`
|
|
63
|
+
const [ state, setState ] = useState< RecordingStateProp >( 'inactive' );
|
|
64
|
+
|
|
65
|
+
// The recorded blob
|
|
66
|
+
const [ blob, setBlob ] = useState< Blob | null >( null );
|
|
67
|
+
|
|
68
|
+
// Store the recorded chunks
|
|
69
|
+
const recordedChunks = useRef< Array< Blob > >( [] ).current;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get the recorded blob.
|
|
73
|
+
*
|
|
74
|
+
* @returns {Blob} The recorded blob
|
|
75
|
+
*/
|
|
76
|
+
function getBlob() {
|
|
77
|
+
return new Blob( recordedChunks, {
|
|
78
|
+
type: 'audio/webm',
|
|
79
|
+
} );
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// `start` recording handler
|
|
83
|
+
const start = useCallback( ( timeslice: number ) => {
|
|
84
|
+
if ( ! timeslice ) {
|
|
85
|
+
return mediaRecordRef?.current?.start();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if ( timeslice < 100 ) {
|
|
89
|
+
timeslice = 100; // set minimum timeslice to 100ms
|
|
90
|
+
}
|
|
91
|
+
mediaRecordRef?.current?.start( timeslice );
|
|
92
|
+
}, [] );
|
|
93
|
+
|
|
94
|
+
// `pause` recording handler
|
|
95
|
+
const pause = useCallback( () => {
|
|
96
|
+
mediaRecordRef?.current?.pause();
|
|
97
|
+
}, [] );
|
|
98
|
+
|
|
99
|
+
// `resume` recording handler
|
|
100
|
+
const resume = useCallback( () => {
|
|
101
|
+
mediaRecordRef?.current?.resume();
|
|
102
|
+
}, [] );
|
|
103
|
+
|
|
104
|
+
// `stop` recording handler
|
|
105
|
+
const stop = useCallback( () => {
|
|
106
|
+
mediaRecordRef?.current?.stop();
|
|
107
|
+
}, [] );
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* `start` event listener for the media recorder instance.
|
|
111
|
+
*/
|
|
112
|
+
function onStartListener() {
|
|
113
|
+
setState( 'recording' );
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* `stop` event listener for the media recorder instance.
|
|
118
|
+
*
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
function onStopListener(): void {
|
|
122
|
+
setState( 'inactive' );
|
|
123
|
+
onDone?.( getBlob() );
|
|
124
|
+
|
|
125
|
+
// Clear the recorded chunks
|
|
126
|
+
recordedChunks.length = 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* `pause` event listener for the media recorder instance.
|
|
131
|
+
*/
|
|
132
|
+
function onPauseListener() {
|
|
133
|
+
setState( 'paused' );
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* `resume` event listener for the media recorder instance.
|
|
138
|
+
*/
|
|
139
|
+
function onResumeListener() {
|
|
140
|
+
setState( 'recording' );
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* `dataavailable` event listener for the media recorder instance.
|
|
145
|
+
*
|
|
146
|
+
* @param {MediaRecorderEvent} event - The event object
|
|
147
|
+
* @returns {void}
|
|
148
|
+
*/
|
|
149
|
+
function onDataAvailableListener( event: MediaRecorderEvent ): void {
|
|
150
|
+
const { data } = event;
|
|
151
|
+
if ( ! data?.size ) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Store the recorded chunks
|
|
156
|
+
recordedChunks.push( data );
|
|
157
|
+
|
|
158
|
+
// Create and store the Blob for the recorded chunks
|
|
159
|
+
setBlob( getBlob() );
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Create media recorder instance
|
|
163
|
+
useEffect( () => {
|
|
164
|
+
// Check if the getUserMedia API is supported
|
|
165
|
+
if ( ! navigator.mediaDevices?.getUserMedia ) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const constraints = { audio: true };
|
|
170
|
+
|
|
171
|
+
navigator.mediaDevices
|
|
172
|
+
.getUserMedia( constraints )
|
|
173
|
+
.then( stream => {
|
|
174
|
+
mediaRecordRef.current = new MediaRecorder( stream );
|
|
175
|
+
|
|
176
|
+
mediaRecordRef.current.addEventListener( 'start', onStartListener );
|
|
177
|
+
mediaRecordRef.current.addEventListener( 'stop', onStopListener );
|
|
178
|
+
mediaRecordRef.current.addEventListener( 'pause', onPauseListener );
|
|
179
|
+
mediaRecordRef.current.addEventListener( 'resume', onResumeListener );
|
|
180
|
+
mediaRecordRef.current.addEventListener( 'dataavailable', onDataAvailableListener );
|
|
181
|
+
} )
|
|
182
|
+
.catch( err => {
|
|
183
|
+
// @todo: handle error
|
|
184
|
+
throw err;
|
|
185
|
+
} );
|
|
186
|
+
return () => {
|
|
187
|
+
mediaRecordRef.current.removeEventListener( 'start', onStartListener );
|
|
188
|
+
mediaRecordRef.current.removeEventListener( 'stop', onStopListener );
|
|
189
|
+
mediaRecordRef.current.removeEventListener( 'pause', onPauseListener );
|
|
190
|
+
mediaRecordRef.current.removeEventListener( 'resume', onResumeListener );
|
|
191
|
+
mediaRecordRef.current.removeEventListener( 'dataavailable', onDataAvailableListener );
|
|
192
|
+
};
|
|
193
|
+
}, [] );
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
state,
|
|
197
|
+
blob,
|
|
198
|
+
|
|
199
|
+
start,
|
|
200
|
+
pause,
|
|
201
|
+
resume,
|
|
202
|
+
stop,
|
|
203
|
+
};
|
|
204
|
+
}
|
package/src/icons/index.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { default as aiAssistantIcon } from './ai-assistant';
|
|
2
|
+
export { default as micIcon } from './mic';
|
|
2
3
|
export { default as origamiPlaneIcon } from './origami-plane';
|
|
4
|
+
export { default as playerPlayIcon } from './player-play';
|
|
5
|
+
export { default as playerStopIcon } from './player-stop';
|
|
6
|
+
export { default as playerPauseIcon } from './player-pause';
|
|
3
7
|
export { default as speakToneIcon } from './speak-tone';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { SVG, Rect, Line, Path } from '@wordpress/components';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
const mic = (
|
|
8
|
+
<SVG width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/SVG">
|
|
9
|
+
<Rect
|
|
10
|
+
x="8.75"
|
|
11
|
+
y="2.75"
|
|
12
|
+
width="6.5"
|
|
13
|
+
height="11.5"
|
|
14
|
+
rx="3.25"
|
|
15
|
+
stroke="currentColor"
|
|
16
|
+
strokeWidth="1.5"
|
|
17
|
+
fill="none"
|
|
18
|
+
/>
|
|
19
|
+
<Line x1="12" y1="17" x2="12" y2="21" stroke="currentColor" strokeWidth="1.5" fill="none" />
|
|
20
|
+
<Path
|
|
21
|
+
d="M18 11C18 11.7879 17.8448 12.5681 17.5433 13.2961C17.2417 14.0241 16.7998 14.6855 16.2426 15.2426C15.6855 15.7998 15.0241 16.2418 14.2961 16.5433C13.5681 16.8448 12.7879 17 12 17C11.2121 17 10.4319 16.8448 9.7039 16.5433C8.97595 16.2417 8.31451 15.7998 7.75736 15.2426C7.20021 14.6855 6.75825 14.0241 6.45672 13.2961C6.15519 12.5681 6 11.7879 6 11"
|
|
22
|
+
stroke="currentColor"
|
|
23
|
+
strokeWidth="1.5"
|
|
24
|
+
fill="none"
|
|
25
|
+
/>
|
|
26
|
+
</SVG>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export default mic;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { SVG, Path, Rect } from '@wordpress/components';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
const playerPause = (
|
|
8
|
+
<SVG width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9
|
+
<Path
|
|
10
|
+
d="M16 6.33325C14.7306 6.33325 13.4736 6.58329 12.3007 7.06908C11.1279 7.55488 10.0623 8.26692 9.16464 9.16455C8.26701 10.0622 7.55497 11.1278 7.06917 12.3006C6.58338 13.4735 6.33334 14.7305 6.33334 15.9999C6.33334 17.2694 6.58338 18.5264 7.06917 19.6992C7.55497 20.872 8.26701 21.9377 9.16464 22.8353C10.0623 23.7329 11.1279 24.445 12.3007 24.9308C13.4736 25.4165 14.7306 25.6666 16 25.6666C18.5638 25.6666 21.0225 24.6481 22.8354 22.8353C24.6482 21.0224 25.6667 18.5637 25.6667 15.9999C25.6667 13.4362 24.6482 10.9774 22.8354 9.16455C21.0225 7.3517 18.5638 6.33325 16 6.33325ZM4.33334 15.9999C4.33334 12.9057 5.56251 9.93826 7.75043 7.75034C9.93836 5.56242 12.9058 4.33325 16 4.33325C19.0942 4.33325 22.0617 5.56242 24.2496 7.75034C26.4375 9.93826 27.6667 12.9057 27.6667 15.9999C27.6667 19.0941 26.4375 22.0616 24.2496 24.2495C22.0617 26.4374 19.0942 27.6666 16 27.6666C12.9058 27.6666 9.93836 26.4374 7.75043 24.2495C5.56251 22.0616 4.33334 19.0941 4.33334 15.9999Z"
|
|
11
|
+
fill="currentColor"
|
|
12
|
+
/>
|
|
13
|
+
<Rect x="17" y="12" width="3" height="8" fill="currentColor" />
|
|
14
|
+
<Rect x="12" y="12" width="3" height="8" fill="currentColor" />
|
|
15
|
+
</SVG>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export default playerPause;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { SVG, Path } from '@wordpress/components';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
const playerPlay = (
|
|
8
|
+
<SVG width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
9
|
+
<Path
|
|
10
|
+
d="M16 6.33325C14.7306 6.33325 13.4736 6.58329 12.3007 7.06908C11.1279 7.55488 10.0623 8.26692 9.16464 9.16455C8.26701 10.0622 7.55497 11.1278 7.06917 12.3006C6.58338 13.4735 6.33334 14.7305 6.33334 15.9999C6.33334 17.2694 6.58338 18.5264 7.06917 19.6992C7.55497 20.872 8.26701 21.9377 9.16464 22.8353C10.0623 23.7329 11.1279 24.445 12.3007 24.9308C13.4736 25.4165 14.7306 25.6666 16 25.6666C18.5638 25.6666 21.0225 24.6481 22.8354 22.8353C24.6482 21.0224 25.6667 18.5637 25.6667 15.9999C25.6667 13.4362 24.6482 10.9774 22.8354 9.16455C21.0225 7.3517 18.5638 6.33325 16 6.33325ZM4.33334 15.9999C4.33334 12.9057 5.56251 9.93826 7.75043 7.75034C9.93836 5.56242 12.9058 4.33325 16 4.33325C19.0942 4.33325 22.0617 5.56242 24.2496 7.75034C26.4375 9.93826 27.6667 12.9057 27.6667 15.9999C27.6667 19.0941 26.4375 22.0616 24.2496 24.2495C22.0617 26.4374 19.0942 27.6666 16 27.6666C12.9058 27.6666 9.93836 26.4374 7.75043 24.2495C5.56251 22.0616 4.33334 19.0941 4.33334 15.9999Z"
|
|
11
|
+
fill="currentColor"
|
|
12
|
+
/>
|
|
13
|
+
<Path
|
|
14
|
+
d="M20.1838 15.098C20.8674 15.5051 20.8675 16.4949 20.1839 16.902L14.8877 20.0553C14.188 20.4719 13.301 19.9677 13.301 19.1533L13.301 12.8467C13.301 12.0323 14.188 11.5281 14.8877 11.9447L20.1838 15.098Z"
|
|
15
|
+
fill="currentColor"
|
|
16
|
+
/>
|
|
17
|
+
</SVG>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default playerPlay;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { SVG, Rect, Path } from '@wordpress/components';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
const playerStop = (
|
|
8
|
+
<SVG width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/SVG">
|
|
9
|
+
<Path
|
|
10
|
+
d="M16.0002 6.33301C14.7307 6.33301 13.4737 6.58304 12.3009 7.06884C11.1281 7.55463 10.0624 8.26668 9.1648 9.16431C8.26716 10.0619 7.55512 11.1276 7.06933 12.3004C6.58353 13.4732 6.3335 14.7302 6.3335 15.9997C6.3335 17.2691 6.58353 18.5261 7.06933 19.6989C7.55512 20.8718 8.26716 21.9374 9.1648 22.835C10.0624 23.7327 11.1281 24.4447 12.3009 24.9305C13.4737 25.4163 14.7307 25.6663 16.0002 25.6663C18.5639 25.6663 21.0227 24.6479 22.8355 22.835C24.6484 21.0222 25.6668 18.5634 25.6668 15.9997C25.6668 13.4359 24.6484 10.9772 22.8355 9.16431C21.0227 7.35146 18.5639 6.33301 16.0002 6.33301ZM4.3335 15.9997C4.3335 12.9055 5.56266 9.93802 7.75058 7.7501C9.93851 5.56217 12.906 4.33301 16.0002 4.33301C19.0944 4.33301 22.0618 5.56217 24.2497 7.7501C26.4377 9.93802 27.6668 12.9055 27.6668 15.9997C27.6668 19.0939 26.4377 22.0613 24.2497 24.2493C22.0618 26.4372 19.0944 27.6663 16.0002 27.6663C12.906 27.6663 9.93851 26.4372 7.75058 24.2493C5.56266 22.0613 4.3335 19.0939 4.3335 15.9997Z"
|
|
11
|
+
fill="currentColor"
|
|
12
|
+
/>
|
|
13
|
+
<Rect x="12" y="12" width="8" height="8" fill="currentColor" />
|
|
14
|
+
</SVG>
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export default playerStop;
|
package/src/types.ts
CHANGED
package/.gitattributes
DELETED
package/jest.config.cjs
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { action } from '@storybook/addon-actions';
|
|
5
|
-
import { useState } from '@wordpress/element';
|
|
6
|
-
import React from 'react';
|
|
7
|
-
/**
|
|
8
|
-
* Internal dependencies
|
|
9
|
-
*/
|
|
10
|
-
import { REQUESTING_STATES } from '../../../types';
|
|
11
|
-
import AIControl from '../index';
|
|
12
|
-
/**
|
|
13
|
-
* Types
|
|
14
|
-
*/
|
|
15
|
-
import type { Meta } from '@storybook/react';
|
|
16
|
-
|
|
17
|
-
export default {
|
|
18
|
-
title: 'JS Packages/AI Client/AI Control',
|
|
19
|
-
component: AIControl,
|
|
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
|
-
},
|
|
40
|
-
} as Meta< typeof AIControl >;
|
|
41
|
-
|
|
42
|
-
const Template = args => {
|
|
43
|
-
const [ value, setValue ] = useState( '' );
|
|
44
|
-
|
|
45
|
-
const handleChange = ( newValue: string ) => {
|
|
46
|
-
setValue( newValue );
|
|
47
|
-
args?.onChange?.( newValue );
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return <AIControl { ...args } onChange={ handleChange } value={ args?.value ?? value } />;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const DefaultArgs = {
|
|
54
|
-
loading: false,
|
|
55
|
-
isOpaque: false,
|
|
56
|
-
placeholder: '',
|
|
57
|
-
requestingState: 'init',
|
|
58
|
-
showButtonsLabel: true,
|
|
59
|
-
showAccept: false,
|
|
60
|
-
acceptLabel: 'Accept',
|
|
61
|
-
onChange: action( 'onChange' ),
|
|
62
|
-
onSend: action( 'onSend' ),
|
|
63
|
-
onStop: action( 'onStop' ),
|
|
64
|
-
onAccept: action( 'onAccept' ),
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const Default = Template.bind( {} );
|
|
68
|
-
Default.args = DefaultArgs;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Meta, Story, Canvas } from '@storybook/blocks';
|
|
2
|
-
import AiStatusIndicator from '../index';
|
|
3
|
-
import * as AiStatusIndicatorStory from './index.stories';
|
|
4
|
-
|
|
5
|
-
<Meta of={AiStatusIndicatorStory} />
|
|
6
|
-
|
|
7
|
-
# AiStatusIndicator
|
|
8
|
-
|
|
9
|
-
## Requesting states
|
|
10
|
-
|
|
11
|
-
### Init
|
|
12
|
-
<Story id="js-packages-ai-client-aistatusindicator--init" />
|
|
13
|
-
|
|
14
|
-
### Requesting
|
|
15
|
-
<Story id="js-packages-ai-client-aistatusindicator--requesting" />
|
|
16
|
-
|
|
17
|
-
### Suggesting
|
|
18
|
-
<Story id="js-packages-ai-client-aistatusindicator--suggesting" />
|
|
19
|
-
|
|
20
|
-
### Done
|
|
21
|
-
<Story id="js-packages-ai-client-aistatusindicator--done" />
|
|
22
|
-
|
|
23
|
-
### Error
|
|
24
|
-
<Story id="js-packages-ai-client-aistatusindicator--error" />
|
|
25
|
-
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* External Dependencies
|
|
3
|
-
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
/*
|
|
6
|
-
* Internal Dependencies
|
|
7
|
-
*/
|
|
8
|
-
import AiStatusIndicator, { AiStatusIndicatorProps } from '..';
|
|
9
|
-
import { REQUESTING_STATES } from '../../../types';
|
|
10
|
-
|
|
11
|
-
type AiStatusIndicatoryStoryProps = AiStatusIndicatorProps & {
|
|
12
|
-
icon: string;
|
|
13
|
-
children?: React.ReactNode;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default {
|
|
17
|
-
title: 'JS Packages/AI Client/AiStatusIndicator',
|
|
18
|
-
component: AiStatusIndicator,
|
|
19
|
-
argTypes: {
|
|
20
|
-
state: {
|
|
21
|
-
control: {
|
|
22
|
-
type: 'select',
|
|
23
|
-
},
|
|
24
|
-
options: REQUESTING_STATES,
|
|
25
|
-
},
|
|
26
|
-
size: {
|
|
27
|
-
control: {
|
|
28
|
-
type: 'select',
|
|
29
|
-
},
|
|
30
|
-
options: [ 24, 32, 48, 64 ],
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
action: {
|
|
34
|
-
table: {
|
|
35
|
-
disable: true,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const DefaultTemplate = ( args: AiStatusIndicatoryStoryProps ) => {
|
|
42
|
-
const props: AiStatusIndicatorProps = {
|
|
43
|
-
state: args.state,
|
|
44
|
-
size: args.size,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
return <AiStatusIndicator { ...props } />;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export const _default = DefaultTemplate.bind( {} );
|
|
51
|
-
_default.args = {
|
|
52
|
-
state: 'init',
|
|
53
|
-
size: 24,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const Init = DefaultTemplate.bind( {} );
|
|
57
|
-
Init.args = {
|
|
58
|
-
state: 'init',
|
|
59
|
-
size: 48,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const Requesting = DefaultTemplate.bind( {} );
|
|
63
|
-
Requesting.args = {
|
|
64
|
-
state: 'requesting',
|
|
65
|
-
size: 48,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const Suggesting = DefaultTemplate.bind( {} );
|
|
69
|
-
Suggesting.args = {
|
|
70
|
-
state: 'suggesting',
|
|
71
|
-
size: 48,
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export const Error = DefaultTemplate.bind( {} );
|
|
75
|
-
Error.args = {
|
|
76
|
-
state: 'error',
|
|
77
|
-
size: 48,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const Done = DefaultTemplate.bind( {} );
|
|
81
|
-
Done.args = {
|
|
82
|
-
state: 'done',
|
|
83
|
-
size: 48,
|
|
84
|
-
};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { Icon } from '@wordpress/components';
|
|
5
|
-
import React from 'react';
|
|
6
|
-
/**
|
|
7
|
-
* Internal dependencies
|
|
8
|
-
*/
|
|
9
|
-
import * as allIcons from '../index';
|
|
10
|
-
import styles from './style.module.scss';
|
|
11
|
-
/**
|
|
12
|
-
* Types
|
|
13
|
-
*/
|
|
14
|
-
import type { Meta } from '@storybook/react';
|
|
15
|
-
|
|
16
|
-
export default {
|
|
17
|
-
title: 'JS Packages/AI Client/Icons',
|
|
18
|
-
component: allIcons,
|
|
19
|
-
parameters: {},
|
|
20
|
-
} as Meta< typeof allIcons >;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Icons story components.
|
|
24
|
-
*
|
|
25
|
-
* @returns {object} - story component
|
|
26
|
-
*/
|
|
27
|
-
function IconsStory() {
|
|
28
|
-
return (
|
|
29
|
-
<div className={ styles[ 'icons-container' ] }>
|
|
30
|
-
{ Object.entries( allIcons ).map( ( [ name, icon ] ) => {
|
|
31
|
-
return (
|
|
32
|
-
<div key={ name } className={ styles[ 'icon-container' ] }>
|
|
33
|
-
<Icon icon={ icon } size={ 32 } />
|
|
34
|
-
<div className={ styles[ 'icon-name' ] }>{ name }</div>
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
} ) }
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const Template = args => <IconsStory { ...args } />;
|
|
43
|
-
|
|
44
|
-
const DefaultArgs = {};
|
|
45
|
-
export const Default = Template.bind( {} );
|
|
46
|
-
Default.args = DefaultArgs;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
.icons-container {
|
|
2
|
-
display: flex;
|
|
3
|
-
flex-wrap: wrap;
|
|
4
|
-
margin-bottom: 20px;
|
|
5
|
-
flex-direction: column;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.icon-container {
|
|
9
|
-
padding: 10px;
|
|
10
|
-
box-shadow: 0 0 1px inset rgba(0, 0, 0 , 0.5 );
|
|
11
|
-
background-color: white;
|
|
12
|
-
border-radius: 5px;
|
|
13
|
-
margin: 2px;
|
|
14
|
-
display: flex;
|
|
15
|
-
gap: 8px;
|
|
16
|
-
align-items: center;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.icon-name {
|
|
20
|
-
font-size: 14px;
|
|
21
|
-
}
|
package/tests/index.test.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// We recommend using `jest` for testing. If you're testing React code, we recommend `@testing-library/react` and related packages.
|
|
2
|
-
// Please match the versions used elsewhere in the monorepo.
|
|
3
|
-
//
|
|
4
|
-
// Please don't add new uses of `mocha`, `chai`, `sinon`, `enzyme`, and so on. We're trying to standardize on one testing framework.
|
|
5
|
-
//
|
|
6
|
-
// The default setup is to have files named like "name.test.js" (or .jsx, .ts, or .tsx) in this `tests/` directory.
|
|
7
|
-
// But you could instead put them in `src/`, or put files like "name.js" (or .jsx, .ts, or .tsx) in `test` or `__tests__` directories somewhere.
|
|
8
|
-
|
|
9
|
-
describe( 'ai-client', () => {
|
|
10
|
-
it( 'should export something', () => {
|
|
11
|
-
expect( true ).toBe( true );
|
|
12
|
-
} );
|
|
13
|
-
} );
|