@automattic/jetpack-ai-client 0.1.3 → 0.1.4

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 CHANGED
@@ -5,6 +5,20 @@ 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.4] - 2023-08-14
9
+ ### Added
10
+ - AI Client: Add border-box in AIControl. [#32419]
11
+ - AI Client: Export AiStatusIndicator. [#32397]
12
+ - AI Client: Import base styles in the AI status indicator component. [#32396]
13
+ - AI Control: Forward ref to consumer. [#32400]
14
+ - AI Control: Import jetpack-base-styles. [#32376]
15
+
16
+ ### Changed
17
+ - AI Client: Expose stopSuggestion function on useAiSuggestions hook so the consumer can stop a suggestion in the middle. [#32382]
18
+
19
+ ### Removed
20
+ - AI Client: Remove redundant switch case [#32405]
21
+
8
22
  ## [0.1.3] - 2023-08-09
9
23
  ### Added
10
24
  - AI Client: Introduce disabled prop in AI Control. [#32326]
@@ -68,6 +82,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
68
82
  - Updated package dependencies. [#31659]
69
83
  - Updated package dependencies. [#31785]
70
84
 
85
+ [0.1.4]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.3...v0.1.4
71
86
  [0.1.3]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.2...v0.1.3
72
87
  [0.1.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.1...v0.1.2
73
88
  [0.1.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.0...v0.1.1
package/index.ts CHANGED
@@ -18,7 +18,7 @@ export * from './src/icons';
18
18
  /*
19
19
  * Components
20
20
  */
21
- export { default as AIControl } from './src/components/ai-control';
21
+ export * from './src/components';
22
22
 
23
23
  /*
24
24
  * Contexts
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.3",
4
+ "version": "0.1.4",
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,6 +32,7 @@
32
32
  ".": "./index.ts"
33
33
  },
34
34
  "dependencies": {
35
+ "@automattic/jetpack-base-styles": "^0.6.5",
35
36
  "@automattic/jetpack-connection": "workspace:*",
36
37
  "@automattic/jetpack-shared-extension-utils": "^0.11.1",
37
38
  "@microsoft/fetch-event-source": "2.0.1",
@@ -4,10 +4,11 @@
4
4
  import { PlainText } from '@wordpress/block-editor';
5
5
  import { Button } from '@wordpress/components';
6
6
  import { useKeyboardShortcut } from '@wordpress/compose';
7
- import { useRef } from '@wordpress/element';
7
+ import { forwardRef, useImperativeHandle, useRef } from '@wordpress/element';
8
8
  import { __ } from '@wordpress/i18n';
9
9
  import { Icon, closeSmall, check, arrowUp } from '@wordpress/icons';
10
10
  import classNames from 'classnames';
11
+ import React from 'react';
11
12
  /**
12
13
  * Internal dependencies
13
14
  */
@@ -38,39 +39,46 @@ const noop = () => {};
38
39
  * @param {Function} props.onSend - send request handler
39
40
  * @param {Function} props.onStop - stop request handler
40
41
  * @param {Function} props.onAccept - accept handler
42
+ * @param {object} ref - Auto injected ref from react
41
43
  * @returns {object} - AI Control component
42
44
  */
43
- export default function AIControl( {
44
- disabled = false,
45
- value = '',
46
- placeholder = '',
47
- showAccept = false,
48
- acceptLabel = __( 'Accept', 'jetpack-ai-client' ),
49
- showButtonsLabel = true,
50
- isOpaque = false,
51
- state = 'init',
52
- onChange = noop,
53
- onSend = noop,
54
- onStop = noop,
55
- onAccept = noop,
56
- }: {
57
- disabled?: boolean;
58
- value: string;
59
- placeholder?: string;
60
- showAccept?: boolean;
61
- acceptLabel?: string;
62
- showButtonsLabel?: boolean;
63
- isOpaque?: boolean;
64
- state?: RequestingStateProp;
65
- onChange?: ( newValue: string ) => void;
66
- onSend?: ( currentValue: string ) => void;
67
- onStop?: () => void;
68
- onAccept?: () => void;
69
- } ) {
45
+ export function AIControl(
46
+ {
47
+ disabled = false,
48
+ value = '',
49
+ placeholder = '',
50
+ showAccept = false,
51
+ acceptLabel = __( 'Accept', 'jetpack-ai-client' ),
52
+ showButtonsLabel = true,
53
+ isOpaque = false,
54
+ state = 'init',
55
+ onChange = noop,
56
+ onSend = noop,
57
+ onStop = noop,
58
+ onAccept = noop,
59
+ }: {
60
+ disabled?: boolean;
61
+ value: string;
62
+ placeholder?: string;
63
+ showAccept?: boolean;
64
+ acceptLabel?: string;
65
+ showButtonsLabel?: boolean;
66
+ isOpaque?: boolean;
67
+ state?: RequestingStateProp;
68
+ onChange?: ( newValue: string ) => void;
69
+ onSend?: ( currentValue: string ) => void;
70
+ onStop?: () => void;
71
+ onAccept?: () => void;
72
+ },
73
+ ref
74
+ ) {
70
75
  const promptUserInputRef = useRef( null );
71
76
  const loading = state === 'requesting' || state === 'suggesting';
72
77
  const showGuideLine = ! ( loading || disabled || value?.length || isOpaque );
73
78
 
79
+ // Pass the ref to forwardRef.
80
+ useImperativeHandle( ref, () => promptUserInputRef.current );
81
+
74
82
  useKeyboardShortcut(
75
83
  'mod+enter',
76
84
  () => {
@@ -169,3 +177,5 @@ export default function AIControl( {
169
177
  </div>
170
178
  );
171
179
  }
180
+
181
+ export default forwardRef( AIControl );
@@ -1,3 +1,5 @@
1
+ @import '@automattic/jetpack-base-styles/root-variables';
2
+
1
3
  // AI CONTROL
2
4
 
3
5
  .jetpack-components-ai-control__container {
@@ -13,6 +15,7 @@
13
15
  padding: 12px 14px;
14
16
  gap: 8px;
15
17
  align-items: center;
18
+ box-sizing: border-box;
16
19
 
17
20
  &.is-opaque {
18
21
  opacity: 0.4;
@@ -1,3 +1,5 @@
1
+ @import '@automattic/jetpack-base-styles/style';
2
+
1
3
  .jetpack-ai-status-indicator__icon-wrapper {
2
4
  color: var( --jp-green-60 );
3
5
  height: 24px;
@@ -0,0 +1,2 @@
1
+ export { default as AIControl } from './ai-control';
2
+ export { default as AiStatusIndicator } from './ai-status-indicator';
@@ -33,6 +33,11 @@ export type AiDataContextProps = {
33
33
  */
34
34
  requestSuggestion: ( prompt: PromptProp, options?: AskQuestionOptionsArgProps ) => void;
35
35
 
36
+ /*
37
+ * Stop suggestion function
38
+ */
39
+ stopSuggestion: () => void;
40
+
36
41
  /*
37
42
  * The Suggestions Event Source instance
38
43
  */
@@ -25,6 +25,7 @@ const withAiDataProvider = createHigherOrderComponent( ( WrappedComponent: React
25
25
  error: requestingError,
26
26
  requestingState,
27
27
  request: requestSuggestion,
28
+ stopSuggestion,
28
29
  eventSource,
29
30
  } = useAiSuggestions();
30
31
 
@@ -37,8 +38,16 @@ const withAiDataProvider = createHigherOrderComponent( ( WrappedComponent: React
37
38
  eventSource,
38
39
 
39
40
  requestSuggestion,
41
+ stopSuggestion,
40
42
  } ),
41
- [ suggestion, requestingError, requestingState, eventSource, requestSuggestion ]
43
+ [
44
+ suggestion,
45
+ requestingError,
46
+ requestingState,
47
+ eventSource,
48
+ requestSuggestion,
49
+ stopSuggestion,
50
+ ]
42
51
  );
43
52
 
44
53
  return (
@@ -97,6 +97,11 @@ type useAiSuggestionsProps = {
97
97
  * The request handler.
98
98
  */
99
99
  request: ( prompt: PromptProp, options?: AskQuestionOptionsArgProps ) => Promise< void >;
100
+
101
+ /*
102
+ * The handler to stop a suggestion.
103
+ */
104
+ stopSuggestion: () => void;
100
105
  };
101
106
 
102
107
  const debug = debugFactory( 'jetpack-ai-client:use-suggestion' );
@@ -140,14 +145,6 @@ export function getErrorData( errorCode: SuggestionErrorCode ): RequestingErrorP
140
145
  severity: 'info',
141
146
  };
142
147
  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
148
  default:
152
149
  return {
153
150
  code: ERROR_NETWORK,
@@ -297,6 +294,46 @@ export default function useAiSuggestions( {
297
294
  ]
298
295
  );
299
296
 
297
+ /**
298
+ * Stop suggestion handler.
299
+ *
300
+ * @returns {void}
301
+ */
302
+ const stopSuggestion = useCallback( () => {
303
+ if ( ! eventSourceRef?.current ) {
304
+ return;
305
+ }
306
+
307
+ // Alias
308
+ const eventSource = eventSourceRef?.current;
309
+
310
+ // Close the connection.
311
+ eventSource.close();
312
+
313
+ // Clean up the event listeners.
314
+ eventSource.removeEventListener( 'suggestion', handleSuggestion );
315
+
316
+ eventSource.removeEventListener( ERROR_QUOTA_EXCEEDED, handleErrorQuotaExceededError );
317
+ eventSource.removeEventListener( ERROR_UNCLEAR_PROMPT, handleUnclearPromptError );
318
+ eventSource.removeEventListener( ERROR_SERVICE_UNAVAILABLE, handleServiceUnavailableError );
319
+ eventSource.removeEventListener( ERROR_MODERATION, handleModerationError );
320
+ eventSource.removeEventListener( ERROR_NETWORK, handleNetworkError );
321
+
322
+ eventSource.removeEventListener( 'done', handleDone );
323
+
324
+ // Set requesting state to done since the suggestion stopped.
325
+ setRequestingState( 'done' );
326
+ }, [
327
+ eventSourceRef,
328
+ handleSuggestion,
329
+ handleErrorQuotaExceededError,
330
+ handleUnclearPromptError,
331
+ handleServiceUnavailableError,
332
+ handleModerationError,
333
+ handleNetworkError,
334
+ handleDone,
335
+ ] );
336
+
300
337
  // Request suggestions automatically when ready.
301
338
  useEffect( () => {
302
339
  // Check if there is a prompt to request.
@@ -310,38 +347,10 @@ export default function useAiSuggestions( {
310
347
  }
311
348
 
312
349
  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, handleNetworkError );
331
-
332
- eventSource.removeEventListener( 'done', handleDone );
350
+ // Stop the suggestion if the component unmounts.
351
+ stopSuggestion();
333
352
  };
334
- }, [
335
- autoRequest,
336
- handleDone,
337
- handleErrorQuotaExceededError,
338
- handleModerationError,
339
- handleServiceUnavailableError,
340
- handleSuggestion,
341
- handleUnclearPromptError,
342
- prompt,
343
- request,
344
- ] );
353
+ }, [ autoRequest, prompt, request, stopSuggestion ] );
345
354
 
346
355
  return {
347
356
  // Data
@@ -349,8 +358,9 @@ export default function useAiSuggestions( {
349
358
  error,
350
359
  requestingState,
351
360
 
352
- // Request handler
361
+ // Request/stop handlers
353
362
  request,
363
+ stopSuggestion,
354
364
 
355
365
  // SuggestionsEventSource
356
366
  eventSource: eventSourceRef.current,