@automattic/jetpack-ai-client 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/build/components/ai-control/ai-control.d.ts +28 -0
  3. package/build/components/ai-control/ai-control.js +22 -0
  4. package/build/components/ai-control/block-ai-control.d.ts +37 -0
  5. package/build/components/ai-control/block-ai-control.js +82 -0
  6. package/build/components/ai-control/extension-ai-control.d.ts +35 -0
  7. package/build/components/ai-control/extension-ai-control.js +86 -0
  8. package/build/components/ai-control/index.d.ts +3 -40
  9. package/build/components/ai-control/index.js +3 -86
  10. package/build/components/index.d.ts +2 -2
  11. package/build/components/index.js +2 -2
  12. package/build/components/{ai-control/message.d.ts → message/index.d.ts} +25 -8
  13. package/build/components/message/index.js +69 -0
  14. package/build/icons/error-exclamation.d.ts +2 -0
  15. package/build/icons/error-exclamation.js +7 -0
  16. package/build/index.d.ts +1 -0
  17. package/build/index.js +4 -0
  18. package/build/libs/index.d.ts +1 -0
  19. package/build/libs/index.js +1 -0
  20. package/build/libs/markdown/html-to-markdown.d.ts +23 -0
  21. package/build/libs/markdown/html-to-markdown.js +31 -0
  22. package/build/libs/markdown/index.d.ts +17 -0
  23. package/build/libs/markdown/index.js +14 -0
  24. package/build/libs/markdown/markdown-to-html.d.ts +24 -0
  25. package/build/libs/markdown/markdown-to-html.js +33 -0
  26. package/build/types.d.ts +10 -0
  27. package/package.json +8 -3
  28. package/src/components/ai-control/ai-control.tsx +79 -0
  29. package/src/components/ai-control/block-ai-control.tsx +278 -0
  30. package/src/components/ai-control/extension-ai-control.tsx +217 -0
  31. package/src/components/ai-control/index.tsx +3 -281
  32. package/src/components/ai-control/style.scss +4 -42
  33. package/src/components/index.ts +3 -2
  34. package/src/components/message/index.tsx +157 -0
  35. package/src/components/message/style.scss +83 -0
  36. package/src/icons/error-exclamation.tsx +18 -0
  37. package/src/index.ts +5 -0
  38. package/src/libs/index.ts +6 -0
  39. package/src/libs/markdown/README.md +74 -0
  40. package/src/libs/markdown/html-to-markdown.ts +42 -0
  41. package/src/libs/markdown/index.ts +28 -0
  42. package/src/libs/markdown/markdown-to-html.ts +48 -0
  43. package/src/types.ts +11 -0
  44. package/build/components/ai-control/message.js +0 -57
  45. package/src/components/ai-control/message.tsx +0 -118
@@ -0,0 +1,217 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { Button, ButtonGroup } from '@wordpress/components';
5
+ import { useKeyboardShortcut } from '@wordpress/compose';
6
+ import { useImperativeHandle, useRef, useEffect, useCallback, useState } from '@wordpress/element';
7
+ import { __ } from '@wordpress/i18n';
8
+ import { Icon, closeSmall, arrowUp, undo } from '@wordpress/icons';
9
+ import React, { forwardRef } from 'react';
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import { GuidelineMessage, ErrorMessage, UpgradeMessage } from '../message/index.js';
14
+ import AIControl from './ai-control.js';
15
+ import './style.scss';
16
+ /**
17
+ * Types
18
+ */
19
+ import type { RequestingStateProp } from '../../types.js';
20
+ import type { ReactElement } from 'react';
21
+
22
+ type ExtensionAIControlProps = {
23
+ disabled?: boolean;
24
+ value: string;
25
+ placeholder?: string;
26
+ showButtonLabels?: boolean;
27
+ isTransparent?: boolean;
28
+ state?: RequestingStateProp;
29
+ showGuideLine?: boolean;
30
+ error?: string;
31
+ requestsRemaining?: number;
32
+ showUpgradeMessage?: boolean;
33
+ onChange?: ( newValue: string ) => void;
34
+ onSend?: ( currentValue: string ) => void;
35
+ onStop?: () => void;
36
+ onClose?: () => void;
37
+ onUndo?: () => void;
38
+ onUpgrade?: () => void;
39
+ };
40
+
41
+ /**
42
+ * ExtensionAIControl component. Used by the AI Assistant inline extensions, adding logic and components to the base AIControl component.
43
+ *
44
+ * @param {ExtensionAIControlProps} props - Component props
45
+ * @param {React.MutableRefObject} ref - Ref to the component
46
+ * @returns {ReactElement} Rendered component
47
+ */
48
+ export function ExtensionAIControl(
49
+ {
50
+ disabled = false,
51
+ value = '',
52
+ placeholder = '',
53
+ showButtonLabels = true,
54
+ isTransparent = false,
55
+ state = 'init',
56
+ showGuideLine = false,
57
+ error,
58
+ requestsRemaining,
59
+ showUpgradeMessage = false,
60
+ onChange,
61
+ onSend,
62
+ onStop,
63
+ onClose,
64
+ onUndo,
65
+ onUpgrade,
66
+ }: ExtensionAIControlProps,
67
+ ref: React.MutableRefObject< HTMLInputElement >
68
+ ): ReactElement {
69
+ const loading = state === 'requesting' || state === 'suggesting';
70
+ const [ editRequest, setEditRequest ] = useState( false );
71
+ const [ lastValue, setLastValue ] = useState( value || null );
72
+ const promptUserInputRef = useRef( null );
73
+
74
+ // Pass the ref to forwardRef.
75
+ useImperativeHandle( ref, () => promptUserInputRef.current );
76
+
77
+ useEffect( () => {
78
+ if ( editRequest ) {
79
+ promptUserInputRef?.current?.focus();
80
+ }
81
+ }, [ editRequest ] );
82
+
83
+ const sendHandler = useCallback( () => {
84
+ setLastValue( value );
85
+ setEditRequest( false );
86
+ onSend?.( value );
87
+ }, [ onSend, value ] );
88
+
89
+ const changeHandler = useCallback(
90
+ ( newValue: string ) => {
91
+ onChange?.( newValue );
92
+ if ( state === 'init' ) {
93
+ return;
94
+ }
95
+
96
+ if ( ! lastValue ) {
97
+ // here we're coming from a one-click action
98
+ setEditRequest( newValue.length > 0 );
99
+ } else {
100
+ // here we're coming from an edit action
101
+ setEditRequest( newValue !== lastValue );
102
+ }
103
+ },
104
+ [ onChange, lastValue, state ]
105
+ );
106
+
107
+ const stopHandler = useCallback( () => {
108
+ onStop?.();
109
+ }, [ onStop ] );
110
+
111
+ const closeHandler = useCallback( () => {
112
+ onClose?.();
113
+ }, [ onClose ] );
114
+
115
+ const undoHandler = useCallback( () => {
116
+ onUndo?.();
117
+ }, [ onUndo ] );
118
+
119
+ const upgradeHandler = useCallback( () => {
120
+ onUpgrade?.();
121
+ }, [ onUpgrade ] );
122
+
123
+ useKeyboardShortcut(
124
+ 'enter',
125
+ e => {
126
+ e.preventDefault();
127
+ sendHandler();
128
+ },
129
+ {
130
+ target: promptUserInputRef,
131
+ }
132
+ );
133
+
134
+ const actions = (
135
+ <>
136
+ { loading ? (
137
+ <Button
138
+ className="jetpack-components-ai-control__controls-prompt_button"
139
+ onClick={ stopHandler }
140
+ variant="secondary"
141
+ label={ __( 'Stop request', 'jetpack-ai-client' ) }
142
+ >
143
+ { showButtonLabels ? __( 'Stop', 'jetpack-ai-client' ) : <Icon icon={ closeSmall } /> }
144
+ </Button>
145
+ ) : (
146
+ <>
147
+ { value?.length > 0 && (
148
+ <div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
149
+ <Button
150
+ className="jetpack-components-ai-control__controls-prompt_button"
151
+ onClick={ sendHandler }
152
+ variant="primary"
153
+ disabled={ ! value?.length || disabled }
154
+ label={ __( 'Send request', 'jetpack-ai-client' ) }
155
+ >
156
+ { showButtonLabels ? (
157
+ __( 'Generate', 'jetpack-ai-client' )
158
+ ) : (
159
+ <Icon icon={ arrowUp } />
160
+ ) }
161
+ </Button>
162
+ </div>
163
+ ) }
164
+ { value?.length <= 0 && state === 'done' && (
165
+ <div className="jetpack-components-ai-control__controls-prompt_button_wrapper">
166
+ <ButtonGroup>
167
+ <Button
168
+ className="jetpack-components-ai-control__controls-prompt_button"
169
+ label={ __( 'Undo', 'jetpack-ai-client' ) }
170
+ onClick={ undoHandler }
171
+ tooltipPosition="top"
172
+ >
173
+ <Icon icon={ undo } />
174
+ </Button>
175
+ <Button
176
+ className="jetpack-components-ai-control__controls-prompt_button"
177
+ label={ __( 'Close', 'jetpack-ai-client' ) }
178
+ onClick={ closeHandler }
179
+ variant="tertiary"
180
+ >
181
+ { __( 'Close', 'jetpack-ai-client' ) }
182
+ </Button>
183
+ </ButtonGroup>
184
+ </div>
185
+ ) }
186
+ </>
187
+ ) }
188
+ </>
189
+ );
190
+
191
+ let message = null;
192
+ if ( error ) {
193
+ message = <ErrorMessage error={ error } onTryAgainClick={ sendHandler } />;
194
+ } else if ( showUpgradeMessage ) {
195
+ message = (
196
+ <UpgradeMessage requestsRemaining={ requestsRemaining } onUpgradeClick={ upgradeHandler } />
197
+ );
198
+ } else if ( showGuideLine ) {
199
+ message = <GuidelineMessage />;
200
+ }
201
+
202
+ return (
203
+ <AIControl
204
+ disabled={ disabled || loading }
205
+ value={ value }
206
+ placeholder={ placeholder }
207
+ isTransparent={ isTransparent }
208
+ state={ state }
209
+ onChange={ changeHandler }
210
+ actions={ actions }
211
+ message={ message }
212
+ promptUserInputRef={ promptUserInputRef }
213
+ />
214
+ );
215
+ }
216
+
217
+ export default forwardRef( ExtensionAIControl );
@@ -1,281 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { PlainText } from '@wordpress/block-editor';
5
- import { Button, ButtonGroup } from '@wordpress/components';
6
- import { useKeyboardShortcut } from '@wordpress/compose';
7
- import { useImperativeHandle, useRef, useEffect, useCallback } from '@wordpress/element';
8
- import { __ } from '@wordpress/i18n';
9
- import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons';
10
- import classNames from 'classnames';
11
- import debugFactory from 'debug';
12
- import React, { forwardRef } from 'react';
13
- /**
14
- * Internal dependencies
15
- */
16
- import './style.scss';
17
- import AiStatusIndicator from '../ai-status-indicator/index.js';
18
- import { GuidelineMessage } from './message.js';
19
- /**
20
- * Types
21
- */
22
- import type { RequestingStateProp } from '../../types.js';
23
- import type { ReactElement } from 'react';
24
- type AiControlProps = {
25
- disabled?: boolean;
26
- value: string;
27
- placeholder?: string;
28
- showAccept?: boolean;
29
- acceptLabel?: string;
30
- showButtonLabels?: boolean;
31
- isTransparent?: boolean;
32
- state?: RequestingStateProp;
33
- showGuideLine?: boolean;
34
- customFooter?: ReactElement;
35
- onChange?: ( newValue: string ) => void;
36
- onSend?: ( currentValue: string ) => void;
37
- onStop?: () => void;
38
- onAccept?: () => void;
39
- onDiscard?: () => void;
40
- showRemove?: boolean;
41
- bannerComponent?: ReactElement;
42
- errorComponent?: ReactElement;
43
- };
44
-
45
- // eslint-disable-next-line @typescript-eslint/no-empty-function
46
- const noop = () => {};
47
-
48
- const debug = debugFactory( 'jetpack-ai-client:ai-control' );
49
-
50
- /**
51
- * AI Control component.
52
- *
53
- * @param {AiControlProps} props - Component props.
54
- * @param {React.MutableRefObject} ref - Ref to the component.
55
- * @returns {ReactElement} Rendered component.
56
- */
57
- export function AIControl(
58
- {
59
- disabled = false,
60
- value = '',
61
- placeholder = '',
62
- showAccept = false,
63
- acceptLabel = __( 'Accept', 'jetpack-ai-client' ),
64
- showButtonLabels = true,
65
- isTransparent = false,
66
- state = 'init',
67
- showGuideLine = false,
68
- customFooter = null,
69
- onChange = noop,
70
- onSend = noop,
71
- onStop = noop,
72
- onAccept = noop,
73
- onDiscard = null,
74
- showRemove = false,
75
- bannerComponent = null,
76
- errorComponent = null,
77
- }: AiControlProps,
78
- ref: React.MutableRefObject< HTMLInputElement >
79
- ): ReactElement {
80
- const promptUserInputRef = useRef( null );
81
- const loading = state === 'requesting' || state === 'suggesting';
82
- const [ editRequest, setEditRequest ] = React.useState( false );
83
- const [ lastValue, setLastValue ] = React.useState( value || null );
84
-
85
- useEffect( () => {
86
- if ( editRequest ) {
87
- promptUserInputRef?.current?.focus();
88
- }
89
- }, [ editRequest ] );
90
-
91
- const sendRequest = useCallback( () => {
92
- setLastValue( value );
93
- setEditRequest( false );
94
- onSend?.( value );
95
- }, [ value ] );
96
-
97
- const changeHandler = useCallback(
98
- ( newValue: string ) => {
99
- onChange?.( newValue );
100
- if ( state === 'init' ) {
101
- return;
102
- }
103
-
104
- if ( ! lastValue ) {
105
- // here we're coming from a one-click action
106
- setEditRequest( newValue.length > 0 );
107
- } else {
108
- // here we're coming from an edit action
109
- setEditRequest( newValue !== lastValue );
110
- }
111
- },
112
- [ lastValue, state ]
113
- );
114
-
115
- const discardHandler = useCallback( () => {
116
- onDiscard?.();
117
- }, [] );
118
-
119
- const cancelEdit = useCallback( () => {
120
- debug( 'cancelEdit, revert to last value', lastValue );
121
- onChange( lastValue || '' );
122
- setEditRequest( false );
123
- }, [ lastValue ] );
124
-
125
- // Pass the ref to forwardRef.
126
- useImperativeHandle( ref, () => promptUserInputRef.current );
127
-
128
- useKeyboardShortcut(
129
- 'mod+enter',
130
- () => {
131
- if ( showAccept ) {
132
- onAccept?.();
133
- }
134
- },
135
- {
136
- target: promptUserInputRef,
137
- }
138
- );
139
-
140
- useKeyboardShortcut(
141
- 'enter',
142
- e => {
143
- e.preventDefault();
144
- sendRequest();
145
- },
146
- {
147
- target: promptUserInputRef,
148
- }
149
- );
150
-
151
- return (
152
- <div className="jetpack-components-ai-control__container-wrapper">
153
- { errorComponent }
154
- <div className="jetpack-components-ai-control__container">
155
- { bannerComponent }
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
- ) }
192
-
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>
239
- ) }
240
-
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 /> ) }
276
- </div>
277
- </div>
278
- );
279
- }
280
-
281
- export default forwardRef( AIControl );
1
+ export { default as AIControl } from './ai-control.js';
2
+ export { default as BlockAIControl } from './block-ai-control.js';
3
+ export { default as ExtensionAIControl } from './extension-ai-control.js';
@@ -1,7 +1,5 @@
1
1
  @import '@automattic/jetpack-base-styles/root-variables';
2
2
 
3
- // AI CONTROL
4
-
5
3
  .jetpack-components-ai-control__container-wrapper {
6
4
  position: sticky;
7
5
  bottom: 16px;
@@ -72,12 +70,6 @@
72
70
  }
73
71
 
74
72
  .jetpack-components-ai-control__controls-prompt_button_wrapper {
75
- text-transform: uppercase;
76
- font-size: 11px;
77
- font-weight: 600;
78
- line-height: 1em;
79
- user-select: none;
80
- white-space: nowrap;
81
73
  display: flex;
82
74
  align-items: center;
83
75
  gap: 8px;
@@ -90,6 +82,10 @@
90
82
  box-shadow: none;
91
83
  padding: 6px 8px;
92
84
  }
85
+
86
+ .components-button-group {
87
+ display: flex;
88
+ }
93
89
  }
94
90
 
95
91
  .jetpack-components-ai-control__controls-prompt_button {
@@ -98,37 +94,3 @@
98
94
  cursor: not-allowed;
99
95
  }
100
96
  }
101
-
102
- // MESSAGE
103
-
104
- .jetpack-ai-assistant__message {
105
- display: flex;
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
-
128
- // Force padding 0 in link buttons, since default Gutenberg version in WordPress doesn't use iframe and
129
- // Buttons receive styles from edit-post-visual-editor.
130
- .components-button.is-link {
131
- padding: 0;
132
- }
133
- }
134
- }
@@ -1,8 +1,9 @@
1
- export { default as AIControl } from './ai-control/index.js';
1
+ export { AIControl, BlockAIControl } from './ai-control/index.js';
2
2
  export { default as AiStatusIndicator } from './ai-status-indicator/index.js';
3
3
  export { default as AudioDurationDisplay } from './audio-duration-display/index.js';
4
4
  export {
5
5
  GuidelineMessage,
6
6
  UpgradeMessage,
7
+ ErrorMessage,
7
8
  default as FooterMessage,
8
- } from './ai-control/message.js';
9
+ } from './message/index.js';