@automattic/jetpack-ai-client 0.14.1 → 0.14.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.
- package/CHANGELOG.md +13 -0
- package/build/components/ai-control/ai-control.js +2 -2
- package/build/components/ai-control/extension-ai-control.d.ts +2 -1
- package/build/components/ai-control/extension-ai-control.js +3 -3
- package/build/components/ai-status-indicator/index.js +2 -2
- package/build/components/message/index.d.ts +4 -2
- package/build/components/message/index.js +6 -6
- package/build/hooks/use-image-generator/index.d.ts +1 -0
- package/build/hooks/use-image-generator/index.js +1 -0
- package/build/libs/markdown/index.d.ts +3 -2
- package/build/libs/markdown/index.js +2 -2
- package/build/libs/markdown/markdown-to-html.d.ts +9 -7
- package/build/libs/markdown/markdown-to-html.js +45 -11
- package/package.json +13 -13
- package/src/components/ai-control/ai-control.tsx +3 -3
- package/src/components/ai-control/extension-ai-control.tsx +8 -1
- package/src/components/ai-status-indicator/index.tsx +2 -2
- package/src/components/message/index.tsx +19 -5
- package/src/hooks/use-image-generator/index.ts +4 -3
- package/src/libs/markdown/README.md +3 -2
- package/src/libs/markdown/index.ts +5 -3
- package/src/libs/markdown/markdown-to-html.ts +65 -14
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ 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.14.3] - 2024-06-10
|
|
9
|
+
### Changed
|
|
10
|
+
- AI Featured Image: export generic image generation request function. [#37668]
|
|
11
|
+
- Change codebase to use clsx instead of classnames. [#37708]
|
|
12
|
+
- Updated package dependencies. [#37669]
|
|
13
|
+
|
|
14
|
+
## [0.14.2] - 2024-06-03
|
|
15
|
+
### Added
|
|
16
|
+
- AI Client: Add list-related fixes on MarkdownToHTML conversion. [#37564]
|
|
17
|
+
- Jetpack AI: Support upgrade links on the AI Control that will open on a new tab. [#37629]
|
|
18
|
+
|
|
8
19
|
## [0.14.1] - 2024-05-27
|
|
9
20
|
### Changed
|
|
10
21
|
- AI Client: Add paragraph tweaks to Markdown conversion libs. [#37461]
|
|
@@ -328,6 +339,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
328
339
|
- Updated package dependencies. [#31659]
|
|
329
340
|
- Updated package dependencies. [#31785]
|
|
330
341
|
|
|
342
|
+
[0.14.3]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.2...v0.14.3
|
|
343
|
+
[0.14.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.1...v0.14.2
|
|
331
344
|
[0.14.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.14.0...v0.14.1
|
|
332
345
|
[0.14.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.13.1...v0.14.0
|
|
333
346
|
[0.13.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.13.0...v0.13.1
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
* External dependencies
|
|
4
4
|
*/
|
|
5
5
|
import { PlainText } from '@wordpress/block-editor';
|
|
6
|
-
import
|
|
6
|
+
import clsx from 'clsx';
|
|
7
7
|
/**
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
@@ -16,7 +16,7 @@ import './style.scss';
|
|
|
16
16
|
* @returns {ReactElement} Rendered component
|
|
17
17
|
*/
|
|
18
18
|
export default function AIControl({ className, disabled = false, value = '', placeholder = '', isTransparent = false, state = 'init', onChange, banner = null, error = null, actions = null, message = null, promptUserInputRef = null, wrapperRef = null, }) {
|
|
19
|
-
return (_jsxs("div", { className:
|
|
19
|
+
return (_jsxs("div", { className: clsx('jetpack-components-ai-control__container-wrapper', className), ref: wrapperRef, children: [error, _jsxs("div", { className: "jetpack-components-ai-control__container", children: [banner, _jsxs("div", { className: clsx('jetpack-components-ai-control__wrapper', {
|
|
20
20
|
'is-transparent': isTransparent,
|
|
21
21
|
}), 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: disabled, ref: promptUserInputRef }) }), actions] }), message] })] }));
|
|
22
22
|
}
|
|
@@ -17,6 +17,7 @@ type ExtensionAIControlProps = {
|
|
|
17
17
|
error?: RequestingErrorProps;
|
|
18
18
|
requestsRemaining?: number;
|
|
19
19
|
showUpgradeMessage?: boolean;
|
|
20
|
+
upgradeUrl?: string;
|
|
20
21
|
wrapperRef?: React.MutableRefObject<HTMLDivElement | null>;
|
|
21
22
|
onChange?: (newValue: string) => void;
|
|
22
23
|
onSend?: (currentValue: string) => void;
|
|
@@ -33,6 +34,6 @@ type ExtensionAIControlProps = {
|
|
|
33
34
|
* @param {React.MutableRefObject} ref - Ref to the component
|
|
34
35
|
* @returns {ReactElement} Rendered component
|
|
35
36
|
*/
|
|
36
|
-
export declare function ExtensionAIControl({ className, disabled, value, placeholder, showButtonLabels, isTransparent, state, showGuideLine, error, requestsRemaining, showUpgradeMessage, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }: ExtensionAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
|
|
37
|
+
export declare function ExtensionAIControl({ className, disabled, value, placeholder, showButtonLabels, isTransparent, state, showGuideLine, error, requestsRemaining, showUpgradeMessage, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }: ExtensionAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
|
|
37
38
|
declare const _default: React.ForwardRefExoticComponent<ExtensionAIControlProps & React.RefAttributes<HTMLInputElement>>;
|
|
38
39
|
export default _default;
|
|
@@ -21,7 +21,7 @@ import './style.scss';
|
|
|
21
21
|
* @param {React.MutableRefObject} ref - Ref to the component
|
|
22
22
|
* @returns {ReactElement} Rendered component
|
|
23
23
|
*/
|
|
24
|
-
export function ExtensionAIControl({ className, disabled = false, value = '', placeholder = '', showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, error, requestsRemaining, showUpgradeMessage = false, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }, ref) {
|
|
24
|
+
export function ExtensionAIControl({ className, disabled = false, value = '', placeholder = '', showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, error, requestsRemaining, showUpgradeMessage = false, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }, ref) {
|
|
25
25
|
const loading = state === 'requesting' || state === 'suggesting';
|
|
26
26
|
const [editRequest, setEditRequest] = useState(false);
|
|
27
27
|
const [lastValue, setLastValue] = useState(value || null);
|
|
@@ -76,10 +76,10 @@ export function ExtensionAIControl({ className, disabled = false, value = '', pl
|
|
|
76
76
|
const actions = (_jsx(_Fragment, { children: loading ? (_jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", onClick: stopHandler, variant: "secondary", label: __('Stop request', 'jetpack-ai-client'), children: showButtonLabels ? __('Stop', 'jetpack-ai-client') : _jsx(Icon, { icon: closeSmall }) })) : (_jsxs(_Fragment, { children: [value?.length > 0 && (_jsx("div", { className: "jetpack-components-ai-control__controls-prompt_button_wrapper", children: _jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", onClick: sendHandler, variant: "primary", disabled: !value?.length || disabled, label: __('Send request', 'jetpack-ai-client'), children: showButtonLabels ? (__('Generate', 'jetpack-ai-client')) : (_jsx(Icon, { icon: arrowUp })) }) })), value?.length <= 0 && state === 'done' && (_jsx("div", { className: "jetpack-components-ai-control__controls-prompt_button_wrapper", children: _jsxs(ButtonGroup, { children: [_jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", label: __('Undo', 'jetpack-ai-client'), onClick: undoHandler, tooltipPosition: "top", children: _jsx(Icon, { icon: undo }) }), _jsx(Button, { className: "jetpack-components-ai-control__controls-prompt_button", label: __('Close', 'jetpack-ai-client'), onClick: closeHandler, variant: "tertiary", children: __('Close', 'jetpack-ai-client') })] }) }))] })) }));
|
|
77
77
|
let message = null;
|
|
78
78
|
if (error?.message) {
|
|
79
|
-
message = (_jsx(ErrorMessage, { error: error.message, code: error.code, onTryAgainClick: tryAgainHandler, onUpgradeClick: upgradeHandler }));
|
|
79
|
+
message = (_jsx(ErrorMessage, { error: error.message, code: error.code, onTryAgainClick: tryAgainHandler, onUpgradeClick: upgradeHandler, upgradeUrl: upgradeUrl }));
|
|
80
80
|
}
|
|
81
81
|
else if (showUpgradeMessage) {
|
|
82
|
-
message = (_jsx(UpgradeMessage, { requestsRemaining: requestsRemaining, onUpgradeClick: upgradeHandler }));
|
|
82
|
+
message = (_jsx(UpgradeMessage, { requestsRemaining: requestsRemaining, onUpgradeClick: upgradeHandler, upgradeUrl: upgradeUrl }));
|
|
83
83
|
}
|
|
84
84
|
else if (showGuideLine) {
|
|
85
85
|
message = _jsx(GuidelineMessage, {});
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
* External dependencies
|
|
4
4
|
*/
|
|
5
5
|
import { Spinner } from '@wordpress/components';
|
|
6
|
-
import
|
|
6
|
+
import clsx from 'clsx';
|
|
7
7
|
import './style.scss';
|
|
8
8
|
/**
|
|
9
9
|
* AiStatusIndicator component.
|
|
@@ -12,7 +12,7 @@ import './style.scss';
|
|
|
12
12
|
* @returns {React.ReactElement} - rendered component.
|
|
13
13
|
*/
|
|
14
14
|
export default function AiStatusIndicator({ state }) {
|
|
15
|
-
return (_jsx("div", { className:
|
|
15
|
+
return (_jsx("div", { className: clsx('jetpack-ai-status-indicator__icon-wrapper', {
|
|
16
16
|
[`is-${state}`]: true,
|
|
17
17
|
}), children: _jsx(Spinner, {}) }));
|
|
18
18
|
}
|
|
@@ -25,12 +25,14 @@ export type UpgradeMessageProps = {
|
|
|
25
25
|
requestsRemaining: number;
|
|
26
26
|
severity?: MessageSeverityProp;
|
|
27
27
|
onUpgradeClick: OnUpgradeClick;
|
|
28
|
+
upgradeUrl?: string;
|
|
28
29
|
};
|
|
29
30
|
export type ErrorMessageProps = {
|
|
30
31
|
error?: string;
|
|
31
32
|
code?: SuggestionErrorCode;
|
|
32
33
|
onTryAgainClick: () => void;
|
|
33
34
|
onUpgradeClick: OnUpgradeClick;
|
|
35
|
+
upgradeUrl?: string;
|
|
34
36
|
};
|
|
35
37
|
/**
|
|
36
38
|
* React component to render a block message.
|
|
@@ -51,12 +53,12 @@ export declare function GuidelineMessage(): React.ReactElement;
|
|
|
51
53
|
* @param {number} requestsRemaining - Number of requests remaining.
|
|
52
54
|
* @returns {React.ReactElement } - Message component.
|
|
53
55
|
*/
|
|
54
|
-
export declare function UpgradeMessage({ requestsRemaining, severity, onUpgradeClick, }: UpgradeMessageProps): React.ReactElement;
|
|
56
|
+
export declare function UpgradeMessage({ requestsRemaining, severity, onUpgradeClick, upgradeUrl, }: UpgradeMessageProps): React.ReactElement;
|
|
55
57
|
/**
|
|
56
58
|
* React component to render an error message
|
|
57
59
|
*
|
|
58
60
|
* @param {number} requestsRemaining - Number of requests remaining.
|
|
59
61
|
* @returns {React.ReactElement } - Message component.
|
|
60
62
|
*/
|
|
61
|
-
export declare function ErrorMessage({ error, code, onTryAgainClick, onUpgradeClick, }: ErrorMessageProps): React.ReactElement;
|
|
63
|
+
export declare function ErrorMessage({ error, code, onTryAgainClick, onUpgradeClick, upgradeUrl, }: ErrorMessageProps): React.ReactElement;
|
|
62
64
|
export {};
|
|
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
import { ExternalLink, Button } from '@wordpress/components';
|
|
6
6
|
import { __, sprintf } from '@wordpress/i18n';
|
|
7
7
|
import { Icon, check, arrowRight } from '@wordpress/icons';
|
|
8
|
-
import
|
|
8
|
+
import clsx from 'clsx';
|
|
9
9
|
/**
|
|
10
10
|
* Internal dependencies
|
|
11
11
|
*/
|
|
@@ -35,7 +35,7 @@ const messageIconsMap = {
|
|
|
35
35
|
* @returns {React.ReactElement } Banner component.
|
|
36
36
|
*/
|
|
37
37
|
export default function Message({ severity = MESSAGE_SEVERITY_INFO, icon = null, showSidebarIcon = false, onSidebarIconClick = () => { }, children, }) {
|
|
38
|
-
return (_jsxs("div", { className:
|
|
38
|
+
return (_jsxs("div", { className: clsx('jetpack-ai-assistant__message', `jetpack-ai-assistant__message-severity-${severity}`), children: [(messageIconsMap[severity] || icon) && (_jsx(Icon, { icon: messageIconsMap[severity] || icon })), _jsx("div", { className: "jetpack-ai-assistant__message-content", children: children }), showSidebarIcon && (_jsx(Button, { className: "jetpack-ai-assistant__message-sidebar", onClick: onSidebarIconClick, children: _jsx(Icon, { size: 20, icon: arrowRight }) }))] }));
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* React component to render a guideline message.
|
|
@@ -51,14 +51,14 @@ export function GuidelineMessage() {
|
|
|
51
51
|
* @param {number} requestsRemaining - Number of requests remaining.
|
|
52
52
|
* @returns {React.ReactElement } - Message component.
|
|
53
53
|
*/
|
|
54
|
-
export function UpgradeMessage({ requestsRemaining, severity, onUpgradeClick, }) {
|
|
54
|
+
export function UpgradeMessage({ requestsRemaining, severity, onUpgradeClick, upgradeUrl, }) {
|
|
55
55
|
let messageSeverity = severity;
|
|
56
56
|
if (messageSeverity == null) {
|
|
57
57
|
messageSeverity = requestsRemaining > 0 ? MESSAGE_SEVERITY_INFO : MESSAGE_SEVERITY_WARNING;
|
|
58
58
|
}
|
|
59
59
|
return (_jsxs(Message, { severity: messageSeverity, children: [_jsx("span", { children: sprintf(
|
|
60
60
|
// translators: %1$d: number of requests remaining
|
|
61
|
-
__('You have %1$d
|
|
61
|
+
__('You have %1$d requests remaining.', 'jetpack-ai-client'), requestsRemaining) }), _jsx(Button, { variant: "link", onClick: onUpgradeClick, href: upgradeUrl, target: upgradeUrl ? '_blank' : null, children: __('Upgrade now', 'jetpack-ai-client') })] }));
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
64
|
* React component to render an error message
|
|
@@ -66,9 +66,9 @@ export function UpgradeMessage({ requestsRemaining, severity, onUpgradeClick, })
|
|
|
66
66
|
* @param {number} requestsRemaining - Number of requests remaining.
|
|
67
67
|
* @returns {React.ReactElement } - Message component.
|
|
68
68
|
*/
|
|
69
|
-
export function ErrorMessage({ error, code, onTryAgainClick, onUpgradeClick, }) {
|
|
69
|
+
export function ErrorMessage({ error, code, onTryAgainClick, onUpgradeClick, upgradeUrl, }) {
|
|
70
70
|
const errorMessage = error || __('Something went wrong', 'jetpack-ai-client');
|
|
71
71
|
return (_jsxs(Message, { severity: MESSAGE_SEVERITY_ERROR, children: [_jsx("span", { children: sprintf(
|
|
72
72
|
// translators: %1$d: A dynamic error message
|
|
73
|
-
__('Error: %1$s', 'jetpack-ai-client'), errorMessage) }), code === ERROR_QUOTA_EXCEEDED ? (_jsx(Button, { variant: "link", onClick: onUpgradeClick, children: __('Upgrade now', 'jetpack-ai-client') })) : (_jsx(Button, { variant: "link", onClick: onTryAgainClick, children: __('Try again', 'jetpack-ai-client') }))] }));
|
|
73
|
+
__('Error: %1$s', 'jetpack-ai-client'), errorMessage) }), code === ERROR_QUOTA_EXCEEDED ? (_jsx(Button, { variant: "link", onClick: onUpgradeClick, href: upgradeUrl, target: upgradeUrl ? '_blank' : null, children: __('Upgrade now', 'jetpack-ai-client') })) : (_jsx(Button, { variant: "link", onClick: onTryAgainClick, children: __('Try again', 'jetpack-ai-client') }))] }));
|
|
74
74
|
}
|
|
@@ -7,10 +7,11 @@ import MarkdownToHTML from './markdown-to-html.js';
|
|
|
7
7
|
* Types
|
|
8
8
|
*/
|
|
9
9
|
import type { Fix as HTMLFix } from './markdown-to-html.js';
|
|
10
|
-
export type RenderHTMLRules =
|
|
11
|
-
declare const renderHTMLFromMarkdown: ({ content, rules, }: {
|
|
10
|
+
export type RenderHTMLRules = Array<HTMLFix>;
|
|
11
|
+
declare const renderHTMLFromMarkdown: ({ content, rules, extension, }: {
|
|
12
12
|
content: string;
|
|
13
13
|
rules?: RenderHTMLRules;
|
|
14
|
+
extension?: boolean;
|
|
14
15
|
}) => string;
|
|
15
16
|
declare const renderMarkdownFromHTML: ({ content }: {
|
|
16
17
|
content: string;
|
|
@@ -5,8 +5,8 @@ import HTMLToMarkdown from './html-to-markdown.js';
|
|
|
5
5
|
import MarkdownToHTML from './markdown-to-html.js';
|
|
6
6
|
const defaultMarkdownConverter = new MarkdownToHTML();
|
|
7
7
|
const defaultHTMLConverter = new HTMLToMarkdown();
|
|
8
|
-
const renderHTMLFromMarkdown = ({ content, rules
|
|
9
|
-
return defaultMarkdownConverter.render({ content, rules });
|
|
8
|
+
const renderHTMLFromMarkdown = ({ content, rules, extension, }) => {
|
|
9
|
+
return defaultMarkdownConverter.render({ content, rules, extension });
|
|
10
10
|
};
|
|
11
11
|
const renderMarkdownFromHTML = ({ content }) => {
|
|
12
12
|
return defaultHTMLConverter.render({ content });
|
|
@@ -6,19 +6,21 @@ import MarkdownIt from 'markdown-it';
|
|
|
6
6
|
* Types
|
|
7
7
|
*/
|
|
8
8
|
import type { Options } from 'markdown-it';
|
|
9
|
-
export type Fix = 'list' | 'paragraph';
|
|
9
|
+
export type Fix = 'list' | 'paragraph' | 'listItem';
|
|
10
10
|
export default class MarkdownToHTML {
|
|
11
11
|
markdownConverter: MarkdownIt;
|
|
12
12
|
constructor(options?: Options);
|
|
13
13
|
/**
|
|
14
14
|
* Renders HTML from Markdown content with specified processing rules.
|
|
15
|
-
* @param {object} options
|
|
16
|
-
* @param {string} options.content
|
|
17
|
-
* @param {string} options.rules
|
|
18
|
-
* @
|
|
15
|
+
* @param {object} options - The options to use when rendering the HTML content
|
|
16
|
+
* @param {string} options.content - The Markdown content to render
|
|
17
|
+
* @param {string} options.rules - The rules to apply to the rendered content
|
|
18
|
+
* @param {boolean} options.extension - Whether to apply the extension-specific rules
|
|
19
|
+
* @returns {string} The rendered HTML content
|
|
19
20
|
*/
|
|
20
|
-
render({ content, rules }: {
|
|
21
|
+
render({ content, rules, extension, }: {
|
|
21
22
|
content: string;
|
|
22
|
-
rules: Array<Fix
|
|
23
|
+
rules: Array<Fix>;
|
|
24
|
+
extension?: boolean;
|
|
23
25
|
}): string;
|
|
24
26
|
}
|
|
@@ -2,12 +2,44 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import MarkdownIt from 'markdown-it';
|
|
5
|
+
const addListComments = (content) => {
|
|
6
|
+
return (content
|
|
7
|
+
// First remove any existing Gutenberg comments to avoid duplicates
|
|
8
|
+
.replaceAll('<!-- wp:list-item -->', '')
|
|
9
|
+
.replaceAll('<!-- /wp:list-item -->', '')
|
|
10
|
+
.replaceAll('<!-- wp:list -->', '')
|
|
11
|
+
.replaceAll('<!-- /wp:list -->', '')
|
|
12
|
+
// Add Gutenberg comments to <li> tags
|
|
13
|
+
.replaceAll('<li>', '<!-- wp:list-item --><li>')
|
|
14
|
+
.replaceAll('</li>', '</li><!-- /wp:list-item -->')
|
|
15
|
+
// Add Gutenberg comments to <ol> tags
|
|
16
|
+
.replaceAll('<ol>', '<!-- wp:list {"ordered":true} --><ol>')
|
|
17
|
+
.replaceAll('</ol>', '</ol><!-- /wp:list -->')
|
|
18
|
+
// Add Gutenberg comments to <ul> tags
|
|
19
|
+
.replaceAll('<ul>', '<!-- wp:list --><ul>')
|
|
20
|
+
.replaceAll('</ul>', '</ul><!-- /wp:list -->'));
|
|
21
|
+
};
|
|
5
22
|
const fixes = {
|
|
6
|
-
list: (content) => {
|
|
23
|
+
list: (content, extension = false) => {
|
|
7
24
|
// Fix list indentation
|
|
8
|
-
|
|
25
|
+
const fixedIndentation = content
|
|
26
|
+
.replace(/<li>\s+<p>/g, '<li>')
|
|
27
|
+
.replace(/<\/p>\s+<\/li>/g, '</li>');
|
|
28
|
+
return extension ? addListComments(fixedIndentation) : fixedIndentation;
|
|
29
|
+
},
|
|
30
|
+
listItem: (content, extension = false) => {
|
|
31
|
+
if (!extension) {
|
|
32
|
+
return content;
|
|
33
|
+
}
|
|
34
|
+
return addListComments(content
|
|
35
|
+
// Remove wrapping <ul> or <ol> tag
|
|
36
|
+
.replace(/^<[ou]l>\s*/g, '')
|
|
37
|
+
.replace(/\s*<\/[ou]l>\s*$/g, ''));
|
|
9
38
|
},
|
|
10
|
-
paragraph: (content) => {
|
|
39
|
+
paragraph: (content, extension = false) => {
|
|
40
|
+
if (!extension) {
|
|
41
|
+
return content;
|
|
42
|
+
}
|
|
11
43
|
// Fix encoding of <br /> tags
|
|
12
44
|
return content.replaceAll(/\s*<br \/>\s*/g, '<br />');
|
|
13
45
|
},
|
|
@@ -15,6 +47,8 @@ const fixes = {
|
|
|
15
47
|
const defaultMarkdownItOptions = {
|
|
16
48
|
breaks: true,
|
|
17
49
|
};
|
|
50
|
+
// The rules used by the AI Assistant block
|
|
51
|
+
const assistantBlockRules = ['list'];
|
|
18
52
|
export default class MarkdownToHTML {
|
|
19
53
|
markdownConverter;
|
|
20
54
|
constructor(options = defaultMarkdownItOptions) {
|
|
@@ -22,16 +56,16 @@ export default class MarkdownToHTML {
|
|
|
22
56
|
}
|
|
23
57
|
/**
|
|
24
58
|
* Renders HTML from Markdown content with specified processing rules.
|
|
25
|
-
* @param {object} options
|
|
26
|
-
* @param {string} options.content
|
|
27
|
-
* @param {string} options.rules
|
|
28
|
-
* @
|
|
59
|
+
* @param {object} options - The options to use when rendering the HTML content
|
|
60
|
+
* @param {string} options.content - The Markdown content to render
|
|
61
|
+
* @param {string} options.rules - The rules to apply to the rendered content
|
|
62
|
+
* @param {boolean} options.extension - Whether to apply the extension-specific rules
|
|
63
|
+
* @returns {string} The rendered HTML content
|
|
29
64
|
*/
|
|
30
|
-
render({ content, rules =
|
|
65
|
+
render({ content, rules = assistantBlockRules, extension = false, }) {
|
|
31
66
|
const rendered = this.markdownConverter.render(content);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return fixes[rule](renderedContent);
|
|
67
|
+
return rules.reduce((renderedContent, rule) => {
|
|
68
|
+
return fixes[rule](renderedContent, extension);
|
|
35
69
|
}, rendered);
|
|
36
70
|
}
|
|
37
71
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-ai-client",
|
|
4
|
-
"version": "0.14.
|
|
4
|
+
"version": "0.14.3",
|
|
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": {
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
"main": "./build/index.js",
|
|
43
43
|
"types": "./build/index.d.ts",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@automattic/jetpack-base-styles": "^0.6.
|
|
46
|
-
"@automattic/jetpack-connection": "^0.33.
|
|
47
|
-
"@automattic/jetpack-shared-extension-utils": "^0.14.
|
|
45
|
+
"@automattic/jetpack-base-styles": "^0.6.26",
|
|
46
|
+
"@automattic/jetpack-connection": "^0.33.14",
|
|
47
|
+
"@automattic/jetpack-shared-extension-utils": "^0.14.14",
|
|
48
48
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
49
49
|
"@types/react": "18.3.1",
|
|
50
|
-
"@wordpress/api-fetch": "
|
|
51
|
-
"@wordpress/block-editor": "
|
|
52
|
-
"@wordpress/components": "
|
|
53
|
-
"@wordpress/compose": "
|
|
54
|
-
"@wordpress/data": "
|
|
55
|
-
"@wordpress/element": "
|
|
56
|
-
"@wordpress/i18n": "
|
|
57
|
-
"@wordpress/icons": "
|
|
58
|
-
"
|
|
50
|
+
"@wordpress/api-fetch": "7.0.0",
|
|
51
|
+
"@wordpress/block-editor": "13.0.0",
|
|
52
|
+
"@wordpress/components": "28.0.0",
|
|
53
|
+
"@wordpress/compose": "7.0.0",
|
|
54
|
+
"@wordpress/data": "10.0.0",
|
|
55
|
+
"@wordpress/element": "6.0.0",
|
|
56
|
+
"@wordpress/i18n": "5.0.0",
|
|
57
|
+
"@wordpress/icons": "10.0.0",
|
|
58
|
+
"clsx": "2.1.1",
|
|
59
59
|
"debug": "4.3.4",
|
|
60
60
|
"markdown-it": "14.0.0",
|
|
61
61
|
"react": "18.3.1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { PlainText } from '@wordpress/block-editor';
|
|
5
|
-
import
|
|
5
|
+
import clsx from 'clsx';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
/**
|
|
8
8
|
* Internal dependencies
|
|
@@ -54,14 +54,14 @@ export default function AIControl( {
|
|
|
54
54
|
}: AIControlProps ): ReactElement {
|
|
55
55
|
return (
|
|
56
56
|
<div
|
|
57
|
-
className={
|
|
57
|
+
className={ clsx( 'jetpack-components-ai-control__container-wrapper', className ) }
|
|
58
58
|
ref={ wrapperRef }
|
|
59
59
|
>
|
|
60
60
|
{ error }
|
|
61
61
|
<div className="jetpack-components-ai-control__container">
|
|
62
62
|
{ banner }
|
|
63
63
|
<div
|
|
64
|
-
className={
|
|
64
|
+
className={ clsx( 'jetpack-components-ai-control__wrapper', {
|
|
65
65
|
'is-transparent': isTransparent,
|
|
66
66
|
} ) }
|
|
67
67
|
>
|
|
@@ -31,6 +31,7 @@ type ExtensionAIControlProps = {
|
|
|
31
31
|
error?: RequestingErrorProps;
|
|
32
32
|
requestsRemaining?: number;
|
|
33
33
|
showUpgradeMessage?: boolean;
|
|
34
|
+
upgradeUrl?: string;
|
|
34
35
|
wrapperRef?: React.MutableRefObject< HTMLDivElement | null >;
|
|
35
36
|
onChange?: ( newValue: string ) => void;
|
|
36
37
|
onSend?: ( currentValue: string ) => void;
|
|
@@ -61,6 +62,7 @@ export function ExtensionAIControl(
|
|
|
61
62
|
error,
|
|
62
63
|
requestsRemaining,
|
|
63
64
|
showUpgradeMessage = false,
|
|
65
|
+
upgradeUrl,
|
|
64
66
|
wrapperRef,
|
|
65
67
|
onChange,
|
|
66
68
|
onSend,
|
|
@@ -210,11 +212,16 @@ export function ExtensionAIControl(
|
|
|
210
212
|
code={ error.code }
|
|
211
213
|
onTryAgainClick={ tryAgainHandler }
|
|
212
214
|
onUpgradeClick={ upgradeHandler }
|
|
215
|
+
upgradeUrl={ upgradeUrl }
|
|
213
216
|
/>
|
|
214
217
|
);
|
|
215
218
|
} else if ( showUpgradeMessage ) {
|
|
216
219
|
message = (
|
|
217
|
-
<UpgradeMessage
|
|
220
|
+
<UpgradeMessage
|
|
221
|
+
requestsRemaining={ requestsRemaining }
|
|
222
|
+
onUpgradeClick={ upgradeHandler }
|
|
223
|
+
upgradeUrl={ upgradeUrl }
|
|
224
|
+
/>
|
|
218
225
|
);
|
|
219
226
|
} else if ( showGuideLine ) {
|
|
220
227
|
message = <GuidelineMessage />;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { Spinner } from '@wordpress/components';
|
|
5
|
-
import
|
|
5
|
+
import clsx from 'clsx';
|
|
6
6
|
/*
|
|
7
7
|
* Types
|
|
8
8
|
*/
|
|
@@ -26,7 +26,7 @@ export type AiStatusIndicatorProps = {
|
|
|
26
26
|
export default function AiStatusIndicator( { state }: AiStatusIndicatorProps ): React.ReactElement {
|
|
27
27
|
return (
|
|
28
28
|
<div
|
|
29
|
-
className={
|
|
29
|
+
className={ clsx( 'jetpack-ai-status-indicator__icon-wrapper', {
|
|
30
30
|
[ `is-${ state }` ]: true,
|
|
31
31
|
} ) }
|
|
32
32
|
>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { ExternalLink, Button } from '@wordpress/components';
|
|
5
5
|
import { __, sprintf } from '@wordpress/i18n';
|
|
6
6
|
import { Icon, check, arrowRight } from '@wordpress/icons';
|
|
7
|
-
import
|
|
7
|
+
import clsx from 'clsx';
|
|
8
8
|
/**
|
|
9
9
|
* Internal dependencies
|
|
10
10
|
*/
|
|
@@ -45,6 +45,7 @@ export type UpgradeMessageProps = {
|
|
|
45
45
|
requestsRemaining: number;
|
|
46
46
|
severity?: MessageSeverityProp;
|
|
47
47
|
onUpgradeClick: OnUpgradeClick;
|
|
48
|
+
upgradeUrl?: string;
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
export type ErrorMessageProps = {
|
|
@@ -52,6 +53,7 @@ export type ErrorMessageProps = {
|
|
|
52
53
|
code?: SuggestionErrorCode;
|
|
53
54
|
onTryAgainClick: () => void;
|
|
54
55
|
onUpgradeClick: OnUpgradeClick;
|
|
56
|
+
upgradeUrl?: string;
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
const messageIconsMap = {
|
|
@@ -76,7 +78,7 @@ export default function Message( {
|
|
|
76
78
|
}: MessageProps ): React.ReactElement {
|
|
77
79
|
return (
|
|
78
80
|
<div
|
|
79
|
-
className={
|
|
81
|
+
className={ clsx(
|
|
80
82
|
'jetpack-ai-assistant__message',
|
|
81
83
|
`jetpack-ai-assistant__message-severity-${ severity }`
|
|
82
84
|
) }
|
|
@@ -122,6 +124,7 @@ export function UpgradeMessage( {
|
|
|
122
124
|
requestsRemaining,
|
|
123
125
|
severity,
|
|
124
126
|
onUpgradeClick,
|
|
127
|
+
upgradeUrl,
|
|
125
128
|
}: UpgradeMessageProps ): React.ReactElement {
|
|
126
129
|
let messageSeverity = severity;
|
|
127
130
|
|
|
@@ -134,11 +137,16 @@ export function UpgradeMessage( {
|
|
|
134
137
|
<span>
|
|
135
138
|
{ sprintf(
|
|
136
139
|
// translators: %1$d: number of requests remaining
|
|
137
|
-
__( 'You have %1$d
|
|
140
|
+
__( 'You have %1$d requests remaining.', 'jetpack-ai-client' ),
|
|
138
141
|
requestsRemaining
|
|
139
142
|
) }
|
|
140
143
|
</span>
|
|
141
|
-
<Button
|
|
144
|
+
<Button
|
|
145
|
+
variant="link"
|
|
146
|
+
onClick={ onUpgradeClick }
|
|
147
|
+
href={ upgradeUrl }
|
|
148
|
+
target={ upgradeUrl ? '_blank' : null }
|
|
149
|
+
>
|
|
142
150
|
{ __( 'Upgrade now', 'jetpack-ai-client' ) }
|
|
143
151
|
</Button>
|
|
144
152
|
</Message>
|
|
@@ -156,6 +164,7 @@ export function ErrorMessage( {
|
|
|
156
164
|
code,
|
|
157
165
|
onTryAgainClick,
|
|
158
166
|
onUpgradeClick,
|
|
167
|
+
upgradeUrl,
|
|
159
168
|
}: ErrorMessageProps ): React.ReactElement {
|
|
160
169
|
const errorMessage = error || __( 'Something went wrong', 'jetpack-ai-client' );
|
|
161
170
|
|
|
@@ -169,7 +178,12 @@ export function ErrorMessage( {
|
|
|
169
178
|
) }
|
|
170
179
|
</span>
|
|
171
180
|
{ code === ERROR_QUOTA_EXCEEDED ? (
|
|
172
|
-
<Button
|
|
181
|
+
<Button
|
|
182
|
+
variant="link"
|
|
183
|
+
onClick={ onUpgradeClick }
|
|
184
|
+
href={ upgradeUrl }
|
|
185
|
+
target={ upgradeUrl ? '_blank' : null }
|
|
186
|
+
>
|
|
173
187
|
{ __( 'Upgrade now', 'jetpack-ai-client' ) }
|
|
174
188
|
</Button>
|
|
175
189
|
) : (
|
|
@@ -154,9 +154,9 @@ const getStableDiffusionImageGenerationPrompt = async (
|
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
const useImageGenerator = () => {
|
|
157
|
-
const executeImageGeneration = async function (
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
const executeImageGeneration = async function (
|
|
158
|
+
parameters: object
|
|
159
|
+
): Promise< ImageGenerationResponse > {
|
|
160
160
|
let token = '';
|
|
161
161
|
|
|
162
162
|
try {
|
|
@@ -259,6 +259,7 @@ const useImageGenerator = () => {
|
|
|
259
259
|
return {
|
|
260
260
|
generateImage,
|
|
261
261
|
generateImageWithStableDiffusion,
|
|
262
|
+
generateImageWithParameters: executeImageGeneration,
|
|
262
263
|
};
|
|
263
264
|
};
|
|
264
265
|
|
|
@@ -52,7 +52,7 @@ Example:
|
|
|
52
52
|
import { renderHTMLFromMarkdown } from '@automattic/jetpack-ai-client';
|
|
53
53
|
|
|
54
54
|
const markdownContent = '**Hello world**';
|
|
55
|
-
const htmlContent = renderHTMLFromMarkdown( { content: markdownContent, rules: '
|
|
55
|
+
const htmlContent = renderHTMLFromMarkdown( { content: markdownContent, rules: [ 'list' ], extension: false } ); // [ 'list' ] and false are default values
|
|
56
56
|
// <p><strong>Hello world</strong></p>\n
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -71,4 +71,5 @@ const htmlContent = renderer.render( { content: markdownContent, rules } );
|
|
|
71
71
|
// <p><strong>Hello world</strong></p>\n
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
The `rules` array supports the following values: `'list'`, `'paragraph'` and `'list-item'`. Further specific fixes can be added when necessary.
|
|
75
|
+
Due to different implementations between the Jetpack AI Assistant block and its extensions, the `extension` flag is used to apply context-specific fixes.
|
|
@@ -11,16 +11,18 @@ import type { Fix as HTMLFix } from './markdown-to-html.js';
|
|
|
11
11
|
const defaultMarkdownConverter = new MarkdownToHTML();
|
|
12
12
|
const defaultHTMLConverter = new HTMLToMarkdown();
|
|
13
13
|
|
|
14
|
-
export type RenderHTMLRules =
|
|
14
|
+
export type RenderHTMLRules = Array< HTMLFix >;
|
|
15
15
|
|
|
16
16
|
const renderHTMLFromMarkdown = ( {
|
|
17
17
|
content,
|
|
18
|
-
rules
|
|
18
|
+
rules,
|
|
19
|
+
extension,
|
|
19
20
|
}: {
|
|
20
21
|
content: string;
|
|
21
22
|
rules?: RenderHTMLRules;
|
|
23
|
+
extension?: boolean;
|
|
22
24
|
} ) => {
|
|
23
|
-
return defaultMarkdownConverter.render( { content, rules } );
|
|
25
|
+
return defaultMarkdownConverter.render( { content, rules, extension } );
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
const renderMarkdownFromHTML = ( { content }: { content: string } ) => {
|
|
@@ -7,17 +7,57 @@ import MarkdownIt from 'markdown-it';
|
|
|
7
7
|
*/
|
|
8
8
|
import type { Options } from 'markdown-it';
|
|
9
9
|
|
|
10
|
-
export type Fix = 'list' | 'paragraph';
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export type Fix = 'list' | 'paragraph' | 'listItem';
|
|
11
|
+
|
|
12
|
+
const addListComments = ( content: string ) => {
|
|
13
|
+
return (
|
|
14
|
+
content
|
|
15
|
+
// First remove any existing Gutenberg comments to avoid duplicates
|
|
16
|
+
.replaceAll( '<!-- wp:list-item -->', '' )
|
|
17
|
+
.replaceAll( '<!-- /wp:list-item -->', '' )
|
|
18
|
+
.replaceAll( '<!-- wp:list -->', '' )
|
|
19
|
+
.replaceAll( '<!-- /wp:list -->', '' )
|
|
20
|
+
// Add Gutenberg comments to <li> tags
|
|
21
|
+
.replaceAll( '<li>', '<!-- wp:list-item --><li>' )
|
|
22
|
+
.replaceAll( '</li>', '</li><!-- /wp:list-item -->' )
|
|
23
|
+
// Add Gutenberg comments to <ol> tags
|
|
24
|
+
.replaceAll( '<ol>', '<!-- wp:list {"ordered":true} --><ol>' )
|
|
25
|
+
.replaceAll( '</ol>', '</ol><!-- /wp:list -->' )
|
|
26
|
+
// Add Gutenberg comments to <ul> tags
|
|
27
|
+
.replaceAll( '<ul>', '<!-- wp:list --><ul>' )
|
|
28
|
+
.replaceAll( '</ul>', '</ul><!-- /wp:list -->' )
|
|
29
|
+
);
|
|
13
30
|
};
|
|
14
31
|
|
|
32
|
+
type Fixes = {
|
|
33
|
+
[ key in Fix ]: ( content: string, extension?: boolean ) => string;
|
|
34
|
+
};
|
|
15
35
|
const fixes: Fixes = {
|
|
16
|
-
list: ( content: string ) => {
|
|
36
|
+
list: ( content: string, extension = false ) => {
|
|
17
37
|
// Fix list indentation
|
|
18
|
-
|
|
38
|
+
const fixedIndentation = content
|
|
39
|
+
.replace( /<li>\s+<p>/g, '<li>' )
|
|
40
|
+
.replace( /<\/p>\s+<\/li>/g, '</li>' );
|
|
41
|
+
|
|
42
|
+
return extension ? addListComments( fixedIndentation ) : fixedIndentation;
|
|
19
43
|
},
|
|
20
|
-
|
|
44
|
+
listItem: ( content: string, extension = false ) => {
|
|
45
|
+
if ( ! extension ) {
|
|
46
|
+
return content;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return addListComments(
|
|
50
|
+
content
|
|
51
|
+
// Remove wrapping <ul> or <ol> tag
|
|
52
|
+
.replace( /^<[ou]l>\s*/g, '' )
|
|
53
|
+
.replace( /\s*<\/[ou]l>\s*$/g, '' )
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
paragraph: ( content: string, extension = false ) => {
|
|
57
|
+
if ( ! extension ) {
|
|
58
|
+
return content;
|
|
59
|
+
}
|
|
60
|
+
|
|
21
61
|
// Fix encoding of <br /> tags
|
|
22
62
|
return content.replaceAll( /\s*<br \/>\s*/g, '<br />' );
|
|
23
63
|
},
|
|
@@ -27,6 +67,9 @@ const defaultMarkdownItOptions: Options = {
|
|
|
27
67
|
breaks: true,
|
|
28
68
|
};
|
|
29
69
|
|
|
70
|
+
// The rules used by the AI Assistant block
|
|
71
|
+
const assistantBlockRules: Array< Fix > = [ 'list' ];
|
|
72
|
+
|
|
30
73
|
export default class MarkdownToHTML {
|
|
31
74
|
markdownConverter: MarkdownIt;
|
|
32
75
|
|
|
@@ -36,17 +79,25 @@ export default class MarkdownToHTML {
|
|
|
36
79
|
|
|
37
80
|
/**
|
|
38
81
|
* Renders HTML from Markdown content with specified processing rules.
|
|
39
|
-
* @param {object} options
|
|
40
|
-
* @param {string} options.content
|
|
41
|
-
* @param {string} options.rules
|
|
42
|
-
* @
|
|
82
|
+
* @param {object} options - The options to use when rendering the HTML content
|
|
83
|
+
* @param {string} options.content - The Markdown content to render
|
|
84
|
+
* @param {string} options.rules - The rules to apply to the rendered content
|
|
85
|
+
* @param {boolean} options.extension - Whether to apply the extension-specific rules
|
|
86
|
+
* @returns {string} The rendered HTML content
|
|
43
87
|
*/
|
|
44
|
-
render( {
|
|
88
|
+
render( {
|
|
89
|
+
content,
|
|
90
|
+
rules = assistantBlockRules,
|
|
91
|
+
extension = false,
|
|
92
|
+
}: {
|
|
93
|
+
content: string;
|
|
94
|
+
rules: Array< Fix >;
|
|
95
|
+
extension?: boolean;
|
|
96
|
+
} ): string {
|
|
45
97
|
const rendered = this.markdownConverter.render( content );
|
|
46
|
-
const rulesToApply = rules === 'all' ? Object.keys( fixes ) : rules;
|
|
47
98
|
|
|
48
|
-
return
|
|
49
|
-
return fixes[ rule ]( renderedContent );
|
|
99
|
+
return rules.reduce( ( renderedContent, rule ) => {
|
|
100
|
+
return fixes[ rule ]( renderedContent, extension );
|
|
50
101
|
}, rendered );
|
|
51
102
|
}
|
|
52
103
|
}
|