@automattic/jetpack-ai-client 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +18 -96
  3. package/index.ts +28 -1
  4. package/package.json +17 -4
  5. package/src/ask-question/Readme.md +1 -1
  6. package/src/ask-question/index.ts +21 -7
  7. package/src/components/ai-control/Readme.md +29 -0
  8. package/src/components/ai-control/index.tsx +165 -0
  9. package/src/components/ai-control/stories/index.stories.tsx +68 -0
  10. package/src/components/ai-control/style.scss +130 -0
  11. package/src/components/ai-status-indicator/Readme.md +11 -0
  12. package/src/components/ai-status-indicator/index.tsx +43 -0
  13. package/src/components/ai-status-indicator/stories/index.mdx +25 -0
  14. package/src/components/ai-status-indicator/stories/index.stories.tsx +84 -0
  15. package/src/components/ai-status-indicator/style.scss +50 -0
  16. package/src/data-flow/Readme.md +115 -0
  17. package/src/data-flow/context.tsx +76 -0
  18. package/src/data-flow/index.ts +3 -0
  19. package/src/data-flow/use-ai-context.ts +88 -0
  20. package/src/data-flow/with-ai-assistant-data.tsx +52 -0
  21. package/src/hooks/use-ai-suggestions/Readme.md +127 -0
  22. package/src/hooks/use-ai-suggestions/index.ts +358 -0
  23. package/src/icons/Readme.md +22 -0
  24. package/src/icons/ai-assistant.tsx +31 -0
  25. package/src/icons/index.ts +3 -0
  26. package/src/icons/origami-plane.tsx +20 -0
  27. package/src/icons/speak-tone.tsx +24 -0
  28. package/src/icons/stories/index.stories.tsx +46 -0
  29. package/src/icons/stories/style.module.scss +21 -0
  30. package/src/suggestions-event-source/index.ts +113 -15
  31. package/src/types.ts +48 -0
  32. package/src/index.js +0 -26
@@ -0,0 +1,127 @@
1
+ # useAiSuggestions
2
+
3
+ This is a custom React hook that obtains suggestions from an AI by hitting a specific query endpoint.
4
+
5
+ ## Usage
6
+
7
+ ```jsx
8
+ import { useAiSuggestions } from '@automattic/jetpack-ai-client';
9
+
10
+ // Inside your component...
11
+
12
+ const { suggestion, ...other } = useAiSuggestions( { options } );
13
+ ```
14
+
15
+ ## API
16
+
17
+ ### `useAiSuggestions( { options } )`
18
+
19
+ Invokes the custom hook with the provided options.
20
+
21
+ #### Options
22
+
23
+ - `prompt: PromptItemProps[]` (optional): An array of request prompts.
24
+ - `autoRequest: boolean` (optional, defaults to `false`): Determines whether to request suggestions automatically.
25
+ - `askQuestionOptions: AskQuestionOptionsArgProps` (optional): [Options for the askQuestion](../../ask-question/Readme.md#ask-question-parameters) function.
26
+ - `onSuggestion: ( suggestion: string ) => void` (optional): A callback function that gets triggered when a suggestion is received.
27
+ - `onDone: ( content: string ) => void` (optional): A callback function that gets triggered when the process is complete.
28
+ - `onError: ( error: RequestingErrorProps ) => void` (optional): A callback function that gets triggered when an error occurs.
29
+
30
+ #### Returns
31
+
32
+ An object with the following properties:
33
+
34
+ - `suggestion: string`: The obtained suggestion.
35
+ - `error: RequestingErrorProps | undefined`: An error object if an error occurs.
36
+ - `requestingState: RequestingStateProp`: The state of the request.
37
+ - `eventSource: SuggestionsEventSource | undefined`: The event source of the request.
38
+ - `request: ( prompt: Array< PromptItemProps >, options: AskQuestionOptionsArgProps ) => Promise< void >`: The request handler.
39
+
40
+ ### `PromptItemProps`
41
+
42
+ An object containing the following properties:
43
+
44
+ - `role: 'system' | 'user' | 'assistant'`: The role of the item in the prompt.
45
+ - `content: string`: The content of the prompt.
46
+
47
+ ### `RequestingErrorProps`
48
+
49
+ An object containing the following properties:
50
+
51
+ - `code: SuggestionErrorCode`: A string code to refer to the error.
52
+ - `message: string`: The user-friendly error message.
53
+ - `severity: 'info' | 'error'`: The severity of the error.
54
+
55
+ ## Examples
56
+
57
+ ### Rendering `suggestion` or `error`, based on `requestingState`.
58
+
59
+ In this example, the state of the request and any returned `suggestion` or `error` are displayed in the rendered output based on the `requestingState`. It sets `autoRequest` to true to trigger the request automatically.
60
+
61
+ ```jsx
62
+ import { useAiSuggestions } from '@automattic/jetpack-ai-client';
63
+
64
+ function ExampleComponent() {
65
+ const { suggestion, error, requestingState } = useAiSuggestions( {
66
+ prompt: [ { role: 'user', content: 'Hello AI!' } ],
67
+ autoRequest: true,
68
+ } );
69
+
70
+ return (
71
+ <div>
72
+ { requestingState === 'requesting' && <p>Requesting...</p> }
73
+ { requestingState === 'error' && <p>Error: { error?.message }</p> }
74
+ { requestingState === 'done' && <p>Suggestion: { suggestion }</p> }
75
+ </div>
76
+ );
77
+ }
78
+ ```
79
+ ### Listening events by using callback functions
80
+
81
+ The following example invokes `useAiSuggestions` with a single prompt and two callback functions for when a suggestion is received or when an error occurs.
82
+
83
+ ```jsx
84
+ import { useAiSuggestions } from '@automattic/jetpack-ai-client';
85
+
86
+ function ExampleComponent() {
87
+ useAiSuggestions( {
88
+ autoRequest: true,
89
+ prompt: [ { role: 'user', content: 'Hello AI!' } ],
90
+ onSuggestion: suggestion => console.log( 'Suggestion: ', suggestion ),
91
+ onError: error => console.log( 'Error: ', error ),
92
+ } );
93
+
94
+ // Your component logic...
95
+ }
96
+ ```
97
+
98
+ ### Handle requests programmatically
99
+
100
+ In example below the request is manually triggered by a button click (`autoRequest` is false by default).
101
+ The handleClick function calls the `request` function, which in turn invokes the AI with a new prompt.
102
+
103
+ ```jsx
104
+ import { useAiSuggestions } from '@automattic/jetpack-ai-client';
105
+
106
+ function ExampleComponent() {
107
+ const { request } = useAiSuggestions( {
108
+ prompt: [ { role: 'user', content: 'Hello AI!' } ],
109
+ onSuggestion: suggestion => console.log( 'Suggestion: ', suggestion ),
110
+ onError: error => console.log( 'Error: ', error ),
111
+ } );
112
+
113
+ // Trigger the request manually.
114
+ const handleClick = async () => {
115
+ await request( [ { role: 'user', content: 'What is the weather like?' } ] );
116
+ }
117
+
118
+ // Your component logic...
119
+
120
+ return (
121
+ <div>
122
+ { /* Your component UI... */ }
123
+ <button onClick={ handleClick }>Get AI Suggestions</button>
124
+ </div>
125
+ );
126
+ }
127
+ ```
@@ -0,0 +1,358 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
5
+ import { __ } from '@wordpress/i18n';
6
+ import debugFactory from 'debug';
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import askQuestion from '../../ask-question';
11
+ import {
12
+ ERROR_MODERATION,
13
+ ERROR_NETWORK,
14
+ ERROR_QUOTA_EXCEEDED,
15
+ ERROR_SERVICE_UNAVAILABLE,
16
+ ERROR_UNCLEAR_PROMPT,
17
+ } from '../../types';
18
+ /**
19
+ * Types & constants
20
+ */
21
+ import type { AskQuestionOptionsArgProps } from '../../ask-question';
22
+ import type SuggestionsEventSource from '../../suggestions-event-source';
23
+ import type { PromptProp, SuggestionErrorCode } from '../../types';
24
+ import type { RequestingStateProp } from '../../types';
25
+
26
+ export type RequestingErrorProps = {
27
+ /*
28
+ * A string code to refer to the error.
29
+ */
30
+ code: SuggestionErrorCode;
31
+
32
+ /*
33
+ * The user-friendly error message.
34
+ */
35
+ message: string;
36
+
37
+ /*
38
+ * The severity of the error.
39
+ */
40
+ severity: 'info' | 'error';
41
+ };
42
+
43
+ type useAiSuggestionsOptions = {
44
+ /*
45
+ * Request prompt.
46
+ */
47
+ prompt?: PromptProp;
48
+
49
+ /*
50
+ * Whether to request suggestions automatically.
51
+ */
52
+ autoRequest?: boolean;
53
+
54
+ /**
55
+ * AskQuestion options.
56
+ */
57
+ askQuestionOptions?: AskQuestionOptionsArgProps;
58
+
59
+ /*
60
+ * onSuggestion callback.
61
+ */
62
+ onSuggestion?: ( suggestion: string ) => void;
63
+
64
+ /*
65
+ * onDone callback.
66
+ */
67
+ onDone?: ( content: string ) => void;
68
+
69
+ /*
70
+ * onError callback.
71
+ */
72
+ onError?: ( error: RequestingErrorProps ) => void;
73
+ };
74
+
75
+ type useAiSuggestionsProps = {
76
+ /*
77
+ * The suggestion.
78
+ */
79
+ suggestion: string;
80
+
81
+ /*
82
+ * The error.
83
+ */
84
+ error: RequestingErrorProps | undefined;
85
+
86
+ /*
87
+ * Whether the request is in progress.
88
+ */
89
+ requestingState: RequestingStateProp;
90
+
91
+ /*
92
+ * The event source.
93
+ */
94
+ eventSource: SuggestionsEventSource | undefined;
95
+
96
+ /*
97
+ * The request handler.
98
+ */
99
+ request: ( prompt: PromptProp, options?: AskQuestionOptionsArgProps ) => Promise< void >;
100
+ };
101
+
102
+ const debug = debugFactory( 'jetpack-ai-client:use-suggestion' );
103
+
104
+ /**
105
+ * Get the error data for a given error code.
106
+ *
107
+ * @param {SuggestionErrorCode} errorCode - The error code.
108
+ * @returns {RequestingErrorProps} The error data.
109
+ */
110
+ export function getErrorData( errorCode: SuggestionErrorCode ): RequestingErrorProps {
111
+ switch ( errorCode ) {
112
+ case ERROR_QUOTA_EXCEEDED:
113
+ return {
114
+ code: ERROR_QUOTA_EXCEEDED,
115
+ message: __( 'You have reached the limit of requests for this site.', 'jetpack-ai-client' ),
116
+ severity: 'info',
117
+ };
118
+ case ERROR_UNCLEAR_PROMPT:
119
+ return {
120
+ code: ERROR_UNCLEAR_PROMPT,
121
+ message: __( 'Your request was unclear. Mind trying again?', 'jetpack-ai-client' ),
122
+ severity: 'info',
123
+ };
124
+ case ERROR_SERVICE_UNAVAILABLE:
125
+ return {
126
+ code: ERROR_SERVICE_UNAVAILABLE,
127
+ message: __(
128
+ 'Jetpack AI services are currently unavailable. Sorry for the inconvenience.',
129
+ 'jetpack-ai-client'
130
+ ),
131
+ severity: 'info',
132
+ };
133
+ case ERROR_MODERATION:
134
+ return {
135
+ code: ERROR_MODERATION,
136
+ message: __(
137
+ 'This request has been flagged by our moderation system. Please try to rephrase it and try again.',
138
+ 'jetpack-ai-client'
139
+ ),
140
+ severity: 'info',
141
+ };
142
+ case ERROR_NETWORK:
143
+ return {
144
+ code: ERROR_NETWORK,
145
+ message: __(
146
+ 'It was not possible to process your request. Mind trying again?',
147
+ 'jetpack-ai-client'
148
+ ),
149
+ severity: 'info',
150
+ };
151
+ default:
152
+ return {
153
+ code: ERROR_NETWORK,
154
+ message: __(
155
+ 'It was not possible to process your request. Mind trying again?',
156
+ 'jetpack-ai-client'
157
+ ),
158
+ severity: 'info',
159
+ };
160
+ }
161
+ }
162
+
163
+ /**
164
+ * React custom hook to get suggestions from AI,
165
+ * by hitting the query endpoint.
166
+ *
167
+ * @param {useAiSuggestionsOptions} options - The options for the hook.
168
+ * @returns {useAiSuggestionsProps} The props for the hook.
169
+ */
170
+ export default function useAiSuggestions( {
171
+ prompt,
172
+ autoRequest = false,
173
+ askQuestionOptions = {},
174
+ onSuggestion,
175
+ onDone,
176
+ onError,
177
+ }: useAiSuggestionsOptions = {} ): useAiSuggestionsProps {
178
+ const [ requestingState, setRequestingState ] = useState< RequestingStateProp >( 'init' );
179
+ const [ suggestion, setSuggestion ] = useState< string >( '' );
180
+ const [ error, setError ] = useState< RequestingErrorProps >();
181
+
182
+ // Store the event source in a ref, so we can handle it if needed.
183
+ const eventSourceRef = useRef< SuggestionsEventSource | undefined >( undefined );
184
+
185
+ /**
186
+ * onSuggestion function handler.
187
+ *
188
+ * @param {string} suggestion - The suggestion.
189
+ * @returns {void}
190
+ */
191
+ const handleSuggestion = useCallback(
192
+ ( event: CustomEvent ) => {
193
+ setSuggestion( event?.detail );
194
+ onSuggestion?.( event?.detail );
195
+ },
196
+ [ onSuggestion ]
197
+ );
198
+
199
+ /**
200
+ * onDone function handler.
201
+ *
202
+ * @param {string} content - The content.
203
+ * @returns {void}
204
+ */
205
+ const handleDone = useCallback(
206
+ ( event: CustomEvent ) => {
207
+ onDone?.( event?.detail );
208
+ setRequestingState( 'done' );
209
+ },
210
+ [ onDone ]
211
+ );
212
+
213
+ const handleError = useCallback(
214
+ ( errorCode: SuggestionErrorCode ) => {
215
+ eventSourceRef?.current?.close();
216
+ setRequestingState( 'error' );
217
+ setError( getErrorData( errorCode ) );
218
+ onError?.( getErrorData( errorCode ) );
219
+ },
220
+ [ onError ]
221
+ );
222
+
223
+ const handleErrorQuotaExceededError = useCallback(
224
+ () => handleError( ERROR_QUOTA_EXCEEDED ),
225
+ []
226
+ );
227
+
228
+ const handleUnclearPromptError = useCallback( () => handleError( ERROR_UNCLEAR_PROMPT ), [] );
229
+
230
+ const handleServiceUnavailableError = useCallback(
231
+ () => handleError( ERROR_SERVICE_UNAVAILABLE ),
232
+ []
233
+ );
234
+
235
+ const handleModerationError = useCallback( () => handleError( ERROR_MODERATION ), [] );
236
+
237
+ const handleNetwotkError = useCallback( () => handleError( ERROR_NETWORK ), [] );
238
+
239
+ /**
240
+ * Request handler.
241
+ *
242
+ * @param {PromptProp} promptArg - The messages array of the prompt.
243
+ * @param {AskQuestionOptionsArgProps} options - The options for the askQuestion request. Uses the hook's askQuestionOptions by default.
244
+ * @returns {Promise<void>} The promise.
245
+ */
246
+ const request = useCallback(
247
+ async (
248
+ promptArg: PromptProp,
249
+ options: AskQuestionOptionsArgProps = { ...askQuestionOptions }
250
+ ) => {
251
+ if ( Array.isArray( promptArg ) && promptArg?.length ) {
252
+ promptArg.forEach( ( { role, content: promptContent }, i ) =>
253
+ debug( '(%s/%s) %o\n%s', i + 1, promptArg.length, `[${ role }]`, promptContent )
254
+ );
255
+ } else {
256
+ debug( '%o', promptArg );
257
+ }
258
+
259
+ // Set the request status.
260
+ setRequestingState( 'requesting' );
261
+
262
+ try {
263
+ eventSourceRef.current = await askQuestion( promptArg, options );
264
+
265
+ if ( ! eventSourceRef?.current ) {
266
+ return;
267
+ }
268
+
269
+ // Alias
270
+ const eventSource = eventSourceRef.current;
271
+
272
+ // Set the request status.
273
+ setRequestingState( 'suggesting' );
274
+
275
+ eventSource.addEventListener( 'suggestion', handleSuggestion );
276
+
277
+ eventSource.addEventListener( ERROR_QUOTA_EXCEEDED, handleErrorQuotaExceededError );
278
+ eventSource.addEventListener( ERROR_UNCLEAR_PROMPT, handleUnclearPromptError );
279
+ eventSource.addEventListener( ERROR_SERVICE_UNAVAILABLE, handleServiceUnavailableError );
280
+ eventSource.addEventListener( ERROR_MODERATION, handleModerationError );
281
+ eventSource.addEventListener( ERROR_NETWORK, handleNetwotkError );
282
+
283
+ eventSource.addEventListener( 'done', handleDone );
284
+ } catch ( e ) {
285
+ // eslint-disable-next-line no-console
286
+ console.error( e );
287
+ }
288
+ },
289
+ [
290
+ handleDone,
291
+ handleErrorQuotaExceededError,
292
+ handleUnclearPromptError,
293
+ handleServiceUnavailableError,
294
+ handleModerationError,
295
+ handleNetwotkError,
296
+ handleSuggestion,
297
+ ]
298
+ );
299
+
300
+ // Request suggestions automatically when ready.
301
+ useEffect( () => {
302
+ // Check if there is a prompt to request.
303
+ if ( ! prompt?.length ) {
304
+ return;
305
+ }
306
+
307
+ // Trigger the request.
308
+ if ( autoRequest ) {
309
+ request( prompt, askQuestionOptions );
310
+ }
311
+
312
+ return () => {
313
+ if ( ! eventSourceRef?.current ) {
314
+ return;
315
+ }
316
+
317
+ // Alias
318
+ const eventSource = eventSourceRef.current;
319
+
320
+ // Close the connection.
321
+ eventSource.close();
322
+
323
+ // Clean up the event listeners.
324
+ eventSource.removeEventListener( 'suggestion', handleSuggestion );
325
+
326
+ eventSource.removeEventListener( ERROR_QUOTA_EXCEEDED, handleErrorQuotaExceededError );
327
+ eventSource.removeEventListener( ERROR_UNCLEAR_PROMPT, handleUnclearPromptError );
328
+ eventSource.removeEventListener( ERROR_SERVICE_UNAVAILABLE, handleServiceUnavailableError );
329
+ eventSource.removeEventListener( ERROR_MODERATION, handleModerationError );
330
+ eventSource.removeEventListener( ERROR_NETWORK, handleNetwotkError );
331
+
332
+ eventSource.removeEventListener( 'done', handleDone );
333
+ };
334
+ }, [
335
+ autoRequest,
336
+ handleDone,
337
+ handleErrorQuotaExceededError,
338
+ handleModerationError,
339
+ handleServiceUnavailableError,
340
+ handleSuggestion,
341
+ handleUnclearPromptError,
342
+ prompt,
343
+ request,
344
+ ] );
345
+
346
+ return {
347
+ // Data
348
+ suggestion,
349
+ error,
350
+ requestingState,
351
+
352
+ // Request handler
353
+ request,
354
+
355
+ // SuggestionsEventSource
356
+ eventSource: eventSourceRef.current,
357
+ };
358
+ }
@@ -0,0 +1,22 @@
1
+ # Icons
2
+
3
+ This library provides you with a selection of icons to use in your Gutenberg applications.
4
+
5
+ - aiAssistantIcon
6
+ - origamiPlane
7
+ - speakTone
8
+
9
+ ```jsx
10
+ import { Icon } from '@wordpress/components';
11
+ import { aiAssistantIcon } from '@automattic/jetpack-ai-client';
12
+
13
+ function YourComponent() {
14
+ // ...
15
+ return (
16
+ <div>
17
+ // Your code here...
18
+ <Icon icon={ aiAssistantIcon } />
19
+ </div>
20
+ );
21
+ }
22
+ ```
@@ -0,0 +1,31 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { SVG, Path } from '@wordpress/components';
5
+ import React from 'react';
6
+
7
+ const aiAssistant = (
8
+ <SVG
9
+ viewBox="0 0 32 32"
10
+ width="32"
11
+ height="32"
12
+ fill="currentColor"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ className="ai-assistant-icon"
15
+ >
16
+ <Path
17
+ className="spark-first"
18
+ d="M9.33301 5.33325L10.4644 8.20188L13.333 9.33325L10.4644 10.4646L9.33301 13.3333L8.20164 10.4646L5.33301 9.33325L8.20164 8.20188L9.33301 5.33325Z"
19
+ />
20
+ <Path
21
+ className="spark-second"
22
+ d="M21.3333 5.33333L22.8418 9.15817L26.6667 10.6667L22.8418 12.1752L21.3333 16L19.8248 12.1752L16 10.6667L19.8248 9.15817L21.3333 5.33333Z"
23
+ />
24
+ <Path
25
+ className="spark-third"
26
+ d="M14.6667 13.3333L16.5523 18.1144L21.3333 20L16.5523 21.8856L14.6667 26.6667L12.781 21.8856L8 20L12.781 18.1144L14.6667 13.3333Z"
27
+ />
28
+ </SVG>
29
+ );
30
+
31
+ export default aiAssistant;
@@ -0,0 +1,3 @@
1
+ export { default as aiAssistantIcon } from './ai-assistant';
2
+ export { default as origamiPlaneIcon } from './origami-plane';
3
+ export { default as speakToneIcon } from './speak-tone';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { SVG, Path } from '@wordpress/components';
5
+ import React from 'react';
6
+
7
+ const origamiPlane = (
8
+ <SVG
9
+ width="24"
10
+ height="24"
11
+ viewBox="0 0 24 24"
12
+ fill="currentColor"
13
+ xmlns="http://www.w3.org/2000/SVG"
14
+ >
15
+ <Path d="M13.5142 18.5098C14.1227 19.6569 14.5367 20.5899 14.5367 20.5899L18.7588 3.84402L4.05433 12.901C4.05433 12.901 5.10588 12.9894 6.41062 13.2114C7.97628 13.4777 9.90652 13.9362 10.8219 14.6646C11.7434 15.3979 12.7731 17.1127 13.5142 18.5098ZM14.0468 16.3975C13.8238 16.0255 13.5879 15.6537 13.3445 15.3013C12.8931 14.6478 12.3509 13.9643 11.7559 13.4909C11.1195 12.9844 10.2767 12.6409 9.51316 12.3968C9.11301 12.2689 8.69633 12.1567 8.28231 12.0585L16.4016 7.05758L14.0468 16.3975Z" />
16
+ <Path d="M18 5L11 14" stroke="currentColor" />
17
+ </SVG>
18
+ );
19
+
20
+ export default origamiPlane;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { SVG, Path } from '@wordpress/components';
5
+ import React from 'react';
6
+
7
+ const speakTone = (
8
+ <SVG width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
9
+ <Path
10
+ fillRule="evenodd"
11
+ clipRule="evenodd"
12
+ d="M12.5 10C12.5 11.3807 11.3807 12.5 10 12.5C8.61929 12.5 7.5 11.3807 7.5 10C7.5 8.61929 8.61929 7.5 10 7.5C11.3807 7.5 12.5 8.61929 12.5 10ZM14 10C14 12.2091 12.2091 14 10 14C7.79086 14 6 12.2091 6 10C6 7.79086 7.79086 6 10 6C12.2091 6 14 7.79086 14 10ZM16.75 21V19C16.75 17.4812 15.5188 16.25 14 16.25L6 16.25C4.48122 16.25 3.25 17.4812 3.25 19V21H4.75L4.75 19C4.75 18.3096 5.30964 17.75 6 17.75L14 17.75C14.6904 17.75 15.25 18.3096 15.25 19V21H16.75Z"
13
+ fill="currentColor"
14
+ />
15
+ <Path
16
+ fillRule="evenodd"
17
+ clipRule="evenodd"
18
+ d="M19.976 16.3599C21.2507 14.5642 22.0001 12.3695 22.0001 9.99969C22.0001 7.63128 21.2515 5.43769 19.9782 3.64258L18.754 4.50967C19.8537 6.05996 20.5001 7.95434 20.5001 9.99969C20.5001 12.0464 19.8528 13.9419 18.7519 15.4928L19.976 16.3599ZM17.3357 14.4897C18.2357 13.222 18.7648 11.6727 18.7648 9.99969C18.7648 8.32808 18.2365 6.77984 17.3379 5.51279L16.1137 6.37988C16.8387 7.4021 17.2648 8.65114 17.2648 9.99969C17.2648 11.3496 16.8378 12.5998 16.1116 13.6226L17.3357 14.4897Z"
19
+ fill="currentColor"
20
+ />
21
+ </SVG>
22
+ );
23
+
24
+ export default speakTone;
@@ -0,0 +1,46 @@
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;
@@ -0,0 +1,21 @@
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
+ }