@automattic/jetpack-ai-client 0.14.6 → 0.16.0
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 +17 -0
- package/build/ask-question/sync.d.ts +2 -8
- package/build/ask-question/sync.js +20 -19
- package/build/hooks/use-image-generator/index.js +1 -1
- package/build/hooks/use-save-to-media-library/index.d.ts +12 -0
- package/build/hooks/use-save-to-media-library/index.js +74 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +5 -0
- package/build/libs/index.d.ts +1 -1
- package/build/libs/index.js +1 -1
- package/build/libs/markdown/index.d.ts +2 -2
- package/build/libs/markdown/index.js +2 -2
- package/build/libs/markdown/markdown-to-html.d.ts +8 -1
- package/build/libs/markdown/markdown-to-html.js +10 -1
- package/build/logo-generator/assets/icons/ai.d.ts +6 -0
- package/build/logo-generator/assets/icons/ai.js +8 -0
- package/build/logo-generator/assets/icons/check.d.ts +6 -0
- package/build/logo-generator/assets/icons/check.js +8 -0
- package/build/logo-generator/assets/icons/logo.d.ts +6 -0
- package/build/logo-generator/assets/icons/logo.js +8 -0
- package/build/logo-generator/assets/icons/media.d.ts +6 -0
- package/build/logo-generator/assets/icons/media.js +8 -0
- package/build/logo-generator/components/feature-fetch-failure-screen.d.ts +8 -0
- package/build/logo-generator/components/feature-fetch-failure-screen.js +10 -0
- package/build/logo-generator/components/first-load-screen.d.ts +5 -0
- package/build/logo-generator/components/first-load-screen.js +16 -0
- package/build/logo-generator/components/generator-modal.d.ts +7 -0
- package/build/logo-generator/components/generator-modal.js +177 -0
- package/build/logo-generator/components/history-carousel.d.ts +6 -0
- package/build/logo-generator/components/history-carousel.js +36 -0
- package/build/logo-generator/components/image-loader.d.ts +7 -0
- package/build/logo-generator/components/image-loader.js +12 -0
- package/build/logo-generator/components/logo-presenter.d.ts +4 -0
- package/build/logo-generator/components/logo-presenter.js +100 -0
- package/build/logo-generator/components/prompt.d.ts +5 -0
- package/build/logo-generator/components/prompt.js +96 -0
- package/build/logo-generator/components/upgrade-nudge.d.ts +2 -0
- package/build/logo-generator/components/upgrade-nudge.js +30 -0
- package/build/logo-generator/components/upgrade-screen.d.ts +9 -0
- package/build/logo-generator/components/upgrade-screen.js +24 -0
- package/build/logo-generator/components/visit-site-banner.d.ts +9 -0
- package/build/logo-generator/components/visit-site-banner.js +16 -0
- package/build/logo-generator/constants.d.ts +16 -0
- package/build/logo-generator/constants.js +19 -0
- package/build/logo-generator/hooks/use-checkout.d.ts +4 -0
- package/build/logo-generator/hooks/use-checkout.js +26 -0
- package/build/logo-generator/hooks/use-logo-generator.d.ts +46 -0
- package/build/logo-generator/hooks/use-logo-generator.js +286 -0
- package/build/logo-generator/hooks/use-request-errors.d.ts +16 -0
- package/build/logo-generator/hooks/use-request-errors.js +46 -0
- package/build/logo-generator/index.d.ts +1 -0
- package/build/logo-generator/index.js +1 -0
- package/build/logo-generator/lib/logo-storage.d.ts +58 -0
- package/build/logo-generator/lib/logo-storage.js +123 -0
- package/build/logo-generator/lib/media-exists.d.ts +12 -0
- package/build/logo-generator/lib/media-exists.js +33 -0
- package/build/logo-generator/lib/set-site-logo.d.ts +13 -0
- package/build/logo-generator/lib/set-site-logo.js +26 -0
- package/build/logo-generator/lib/wpcom-limited-request.d.ts +7 -0
- package/build/logo-generator/lib/wpcom-limited-request.js +25 -0
- package/build/logo-generator/store/actions.d.ts +105 -0
- package/build/logo-generator/store/actions.js +193 -0
- package/build/logo-generator/store/constants.d.ts +44 -0
- package/build/logo-generator/store/constants.js +44 -0
- package/build/logo-generator/store/index.d.ts +1 -0
- package/build/logo-generator/store/index.js +19 -0
- package/build/logo-generator/store/initial-state.d.ts +3 -0
- package/build/logo-generator/store/initial-state.js +40 -0
- package/build/logo-generator/store/reducer.d.ts +347 -0
- package/build/logo-generator/store/reducer.js +293 -0
- package/build/logo-generator/store/selectors.d.ts +119 -0
- package/build/logo-generator/store/selectors.js +173 -0
- package/build/logo-generator/store/types.d.ts +164 -0
- package/build/logo-generator/store/types.js +1 -0
- package/build/logo-generator/types.d.ts +84 -0
- package/build/logo-generator/types.js +1 -0
- package/build/types.d.ts +6 -0
- package/package.json +5 -3
- package/src/ask-question/sync.ts +22 -27
- package/src/hooks/use-image-generator/index.ts +1 -1
- package/src/hooks/use-save-to-media-library/index.ts +95 -0
- package/src/index.ts +6 -0
- package/src/libs/index.ts +1 -0
- package/src/libs/markdown/index.ts +2 -2
- package/src/libs/markdown/markdown-to-html.ts +20 -3
- package/src/logo-generator/assets/icons/ai.tsx +21 -0
- package/src/logo-generator/assets/icons/check.tsx +23 -0
- package/src/logo-generator/assets/icons/icons.scss +5 -0
- package/src/logo-generator/assets/icons/logo.tsx +23 -0
- package/src/logo-generator/assets/icons/media.tsx +24 -0
- package/src/logo-generator/assets/images/jetpack-logo.svg +4 -0
- package/src/logo-generator/assets/images/loader.gif +0 -0
- package/src/logo-generator/assets/index.d.ts +3 -0
- package/src/logo-generator/components/feature-fetch-failure-screen.tsx +35 -0
- package/src/logo-generator/components/first-load-screen.scss +12 -0
- package/src/logo-generator/components/first-load-screen.tsx +32 -0
- package/src/logo-generator/components/generator-modal.scss +92 -0
- package/src/logo-generator/components/generator-modal.tsx +280 -0
- package/src/logo-generator/components/history-carousel.scss +36 -0
- package/src/logo-generator/components/history-carousel.tsx +57 -0
- package/src/logo-generator/components/image-loader.tsx +22 -0
- package/src/logo-generator/components/logo-presenter.scss +116 -0
- package/src/logo-generator/components/logo-presenter.tsx +219 -0
- package/src/logo-generator/components/prompt.scss +102 -0
- package/src/logo-generator/components/prompt.tsx +211 -0
- package/src/logo-generator/components/upgrade-nudge.scss +43 -0
- package/src/logo-generator/components/upgrade-nudge.tsx +58 -0
- package/src/logo-generator/components/upgrade-screen.tsx +67 -0
- package/src/logo-generator/components/visit-site-banner.scss +29 -0
- package/src/logo-generator/components/visit-site-banner.tsx +54 -0
- package/src/logo-generator/constants.ts +22 -0
- package/src/logo-generator/hooks/use-checkout.ts +37 -0
- package/src/logo-generator/hooks/use-logo-generator.ts +389 -0
- package/src/logo-generator/hooks/use-request-errors.ts +70 -0
- package/src/logo-generator/index.ts +1 -0
- package/src/logo-generator/lib/logo-storage.ts +166 -0
- package/src/logo-generator/lib/media-exists.ts +42 -0
- package/src/logo-generator/lib/set-site-logo.ts +32 -0
- package/src/logo-generator/lib/wpcom-limited-request.ts +30 -0
- package/src/logo-generator/store/actions.ts +251 -0
- package/src/logo-generator/store/constants.ts +49 -0
- package/src/logo-generator/store/index.ts +25 -0
- package/src/logo-generator/store/initial-state.ts +43 -0
- package/src/logo-generator/store/reducer.ts +387 -0
- package/src/logo-generator/store/selectors.ts +201 -0
- package/src/logo-generator/store/types.ts +207 -0
- package/src/logo-generator/types.ts +99 -0
- package/src/types.ts +8 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
|
|
5
|
+
import { Button, Icon } from '@wordpress/components';
|
|
6
|
+
import { useDispatch } from '@wordpress/data';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
import debugFactory from 'debug';
|
|
9
|
+
/**
|
|
10
|
+
* Internal dependencies
|
|
11
|
+
*/
|
|
12
|
+
import CheckIcon from '../assets/icons/check.js';
|
|
13
|
+
import LogoIcon from '../assets/icons/logo.js';
|
|
14
|
+
import MediaIcon from '../assets/icons/media.js';
|
|
15
|
+
import { EVENT_SAVE, EVENT_USE } from '../constants.js';
|
|
16
|
+
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
17
|
+
import useRequestErrors from '../hooks/use-request-errors.js';
|
|
18
|
+
import { updateLogo } from '../lib/logo-storage.js';
|
|
19
|
+
import { STORE_NAME } from '../store/index.js';
|
|
20
|
+
import { ImageLoader } from './image-loader.js';
|
|
21
|
+
import './logo-presenter.scss';
|
|
22
|
+
/**
|
|
23
|
+
* Types
|
|
24
|
+
*/
|
|
25
|
+
import type { Logo } from '../store/types.js';
|
|
26
|
+
import type { LogoPresenterProps } from '../types.js';
|
|
27
|
+
import type React from 'react';
|
|
28
|
+
|
|
29
|
+
const debug = debugFactory( 'jetpack-ai-calypso:logo-presenter' );
|
|
30
|
+
|
|
31
|
+
const SaveInLibraryButton: React.FC< { siteId: string } > = ( { siteId } ) => {
|
|
32
|
+
const { tracks } = useAnalytics();
|
|
33
|
+
const { recordEvent: recordTracksEvent } = tracks;
|
|
34
|
+
const {
|
|
35
|
+
saveLogo,
|
|
36
|
+
selectedLogo,
|
|
37
|
+
isSavingLogoToLibrary: saving,
|
|
38
|
+
logos,
|
|
39
|
+
selectedLogoIndex,
|
|
40
|
+
context,
|
|
41
|
+
} = useLogoGenerator();
|
|
42
|
+
const saved = !! selectedLogo?.mediaId;
|
|
43
|
+
|
|
44
|
+
const { loadLogoHistory } = useDispatch( STORE_NAME );
|
|
45
|
+
|
|
46
|
+
const handleClick = async () => {
|
|
47
|
+
if ( ! saved && ! saving ) {
|
|
48
|
+
recordTracksEvent( EVENT_SAVE, {
|
|
49
|
+
context,
|
|
50
|
+
logos_count: logos.length,
|
|
51
|
+
selected_logo: selectedLogoIndex ? selectedLogoIndex + 1 : 0,
|
|
52
|
+
} );
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const savedLogo = await saveLogo( selectedLogo );
|
|
56
|
+
|
|
57
|
+
// Update localStorage
|
|
58
|
+
updateLogo( {
|
|
59
|
+
siteId,
|
|
60
|
+
url: selectedLogo.url,
|
|
61
|
+
newUrl: savedLogo.mediaURL,
|
|
62
|
+
mediaId: savedLogo.mediaId,
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
// Update state
|
|
66
|
+
loadLogoHistory( siteId );
|
|
67
|
+
} catch ( error ) {
|
|
68
|
+
debug( 'Error saving logo', error );
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const savingLabel = __( 'Saving…', 'jetpack-ai-client' );
|
|
74
|
+
const savedLabel = __( 'Saved', 'jetpack-ai-client' );
|
|
75
|
+
|
|
76
|
+
return ! saving && ! saved ? (
|
|
77
|
+
<Button className="jetpack-ai-logo-generator-modal-presenter__action" onClick={ handleClick }>
|
|
78
|
+
<Icon icon={ <MediaIcon /> } />
|
|
79
|
+
<span className="action-text">{ __( 'Save in Library', 'jetpack-ai-client' ) }</span>
|
|
80
|
+
</Button>
|
|
81
|
+
) : (
|
|
82
|
+
<button className="jetpack-ai-logo-generator-modal-presenter__action">
|
|
83
|
+
<Icon icon={ saving ? <MediaIcon /> : <CheckIcon /> } />
|
|
84
|
+
<span className="action-text">{ saving ? savingLabel : savedLabel }</span>
|
|
85
|
+
</button>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const UseOnSiteButton: React.FC< { onApplyLogo: ( mediaId: number ) => void } > = ( {
|
|
90
|
+
onApplyLogo,
|
|
91
|
+
} ) => {
|
|
92
|
+
const { tracks } = useAnalytics();
|
|
93
|
+
const { recordEvent: recordTracksEvent } = tracks;
|
|
94
|
+
const { isSavingLogoToLibrary, selectedLogo, logos, selectedLogoIndex, context } =
|
|
95
|
+
useLogoGenerator();
|
|
96
|
+
|
|
97
|
+
const handleClick = async () => {
|
|
98
|
+
if ( ! isSavingLogoToLibrary ) {
|
|
99
|
+
recordTracksEvent( EVENT_USE, {
|
|
100
|
+
context,
|
|
101
|
+
logos_count: logos.length,
|
|
102
|
+
selected_logo: selectedLogoIndex != null ? selectedLogoIndex + 1 : 0,
|
|
103
|
+
} );
|
|
104
|
+
|
|
105
|
+
onApplyLogo?.( selectedLogo?.mediaId );
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Button
|
|
111
|
+
className="jetpack-ai-logo-generator-modal-presenter__action"
|
|
112
|
+
onClick={ handleClick }
|
|
113
|
+
disabled={ isSavingLogoToLibrary || ! selectedLogo?.mediaId }
|
|
114
|
+
>
|
|
115
|
+
<Icon icon={ <LogoIcon /> } />
|
|
116
|
+
<span className="action-text">{ __( 'Use on block', 'jetpack-ai-client' ) }</span>
|
|
117
|
+
</Button>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const LogoLoading: React.FC = () => {
|
|
122
|
+
return (
|
|
123
|
+
<>
|
|
124
|
+
<ImageLoader className="jetpack-ai-logo-generator-modal-presenter__logo" />
|
|
125
|
+
<span className="jetpack-ai-logo-generator-modal-presenter__loading-text">
|
|
126
|
+
{ __( 'Generating new logo…', 'jetpack-ai-client' ) }
|
|
127
|
+
</span>
|
|
128
|
+
</>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const LogoReady: React.FC< {
|
|
133
|
+
siteId: string;
|
|
134
|
+
logo: Logo;
|
|
135
|
+
onApplyLogo: ( mediaId: number ) => void;
|
|
136
|
+
} > = ( { siteId, logo, onApplyLogo } ) => {
|
|
137
|
+
return (
|
|
138
|
+
<>
|
|
139
|
+
<img
|
|
140
|
+
src={ logo.url }
|
|
141
|
+
alt={ logo.description }
|
|
142
|
+
className="jetpack-ai-logo-generator-modal-presenter__logo"
|
|
143
|
+
/>
|
|
144
|
+
<div className="jetpack-ai-logo-generator-modal-presenter__action-wrapper">
|
|
145
|
+
<span className="jetpack-ai-logo-generator-modal-presenter__description">
|
|
146
|
+
{ logo.description }
|
|
147
|
+
</span>
|
|
148
|
+
<div className="jetpack-ai-logo-generator-modal-presenter__actions">
|
|
149
|
+
<SaveInLibraryButton siteId={ siteId } />
|
|
150
|
+
<UseOnSiteButton onApplyLogo={ onApplyLogo } />
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const LogoUpdated: React.FC< { logo: Logo } > = ( { logo } ) => {
|
|
158
|
+
return (
|
|
159
|
+
<>
|
|
160
|
+
<img
|
|
161
|
+
src={ logo.url }
|
|
162
|
+
alt={ logo.description }
|
|
163
|
+
className="jetpack-ai-logo-generator-modal-presenter__logo"
|
|
164
|
+
/>
|
|
165
|
+
<div className="jetpack-ai-logo-generator-modal-presenter__success-wrapper">
|
|
166
|
+
<Icon icon={ <CheckIcon /> } />
|
|
167
|
+
<span>{ __( 'Your new logo was set to the block!', 'jetpack-ai-client' ) }</span>
|
|
168
|
+
</div>
|
|
169
|
+
</>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const LogoPresenter: React.FC< LogoPresenterProps > = ( {
|
|
174
|
+
logo = null,
|
|
175
|
+
loading = false,
|
|
176
|
+
onApplyLogo,
|
|
177
|
+
logoAccepted = false,
|
|
178
|
+
siteId,
|
|
179
|
+
} ) => {
|
|
180
|
+
const { isRequestingImage } = useLogoGenerator();
|
|
181
|
+
const { saveToLibraryError, logoUpdateError } = useRequestErrors();
|
|
182
|
+
|
|
183
|
+
if ( ! logo ) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let logoContent: React.ReactNode;
|
|
188
|
+
|
|
189
|
+
if ( loading || isRequestingImage ) {
|
|
190
|
+
logoContent = <LogoLoading />;
|
|
191
|
+
} else if ( logoAccepted ) {
|
|
192
|
+
logoContent = <LogoUpdated logo={ logo } />;
|
|
193
|
+
} else {
|
|
194
|
+
logoContent = (
|
|
195
|
+
<LogoReady siteId={ String( siteId ) } logo={ logo } onApplyLogo={ onApplyLogo } />
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<div className="jetpack-ai-logo-generator-modal-presenter__wrapper">
|
|
201
|
+
<div className="jetpack-ai-logo-generator-modal-presenter">
|
|
202
|
+
<div className="jetpack-ai-logo-generator-modal-presenter__content">{ logoContent }</div>
|
|
203
|
+
{ ! logoAccepted && (
|
|
204
|
+
<div className="jetpack-ai-logo-generator-modal-presenter__rectangle" />
|
|
205
|
+
) }
|
|
206
|
+
</div>
|
|
207
|
+
{ saveToLibraryError && (
|
|
208
|
+
<div className="jetpack-ai-logo-generator__prompt-error">
|
|
209
|
+
{ __( 'Error saving the logo to your library. Please try again.', 'jetpack-ai-client' ) }
|
|
210
|
+
</div>
|
|
211
|
+
) }
|
|
212
|
+
{ logoUpdateError && (
|
|
213
|
+
<div className="jetpack-ai-logo-generator__prompt-error">
|
|
214
|
+
{ __( 'Error applying the logo to your site. Please try again.', 'jetpack-ai-client' ) }
|
|
215
|
+
</div>
|
|
216
|
+
) }
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
@import '@automattic/jetpack-base-styles/root-variables';
|
|
2
|
+
|
|
3
|
+
.jetpack-ai-logo-generator__prompt {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: 8px;
|
|
7
|
+
font-size: var(--font-body-small);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.jetpack-ai-logo-generator__prompt-header {
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: space-between;
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
align-self: stretch;
|
|
15
|
+
|
|
16
|
+
.jetpack-ai-logo-generator__prompt-label {
|
|
17
|
+
font-weight: 500;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.jetpack-ai-logo-generator__prompt-actions {
|
|
21
|
+
display: flex;
|
|
22
|
+
font-size: var(--font-body-extra-small);
|
|
23
|
+
line-height: 20px;
|
|
24
|
+
|
|
25
|
+
.jetpack-ai-logo-generator-icon {
|
|
26
|
+
margin-right: 4px;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.jetpack-ai-logo-generator__prompt-query {
|
|
32
|
+
display: flex;
|
|
33
|
+
padding: 8px 8px 8px var(--grid-unit-15, 16px);
|
|
34
|
+
justify-content: space-between;
|
|
35
|
+
align-items: flex-end;
|
|
36
|
+
align-self: stretch;
|
|
37
|
+
border-radius: calc(4px * 2);
|
|
38
|
+
border: 1px solid var(--studio-gray-10, #ccc);
|
|
39
|
+
background: var(--studio-white, #fff);
|
|
40
|
+
gap: 8px;
|
|
41
|
+
|
|
42
|
+
@media (min-width: 700px) {
|
|
43
|
+
gap: 48px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.prompt-query__input {
|
|
47
|
+
border: 0;
|
|
48
|
+
resize: none;
|
|
49
|
+
flex-grow: 1;
|
|
50
|
+
padding: 6px 0;
|
|
51
|
+
vertical-align: baseline;
|
|
52
|
+
color: var(--studio-gray-100);
|
|
53
|
+
line-height: 1.6;
|
|
54
|
+
word-break: break-word;
|
|
55
|
+
|
|
56
|
+
&:focus,
|
|
57
|
+
&:active {
|
|
58
|
+
outline: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&[contentEditable="false"] {
|
|
62
|
+
color: var(--studio-gray-50, #646970);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&[data-placeholder]:empty::before {
|
|
66
|
+
content: attr(data-placeholder);
|
|
67
|
+
color: var(--studio-gray-50, #646970);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&[data-placeholder]:empty:focus::before {
|
|
71
|
+
content: "";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.jetpack-ai-logo-generator__prompt-footer {
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
gap: 8px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.jetpack-ai-logo-generator__prompt-requests {
|
|
83
|
+
color: var(--studio-gray-50, #646970);
|
|
84
|
+
font-size: var(--font-body-extra-small);
|
|
85
|
+
line-height: 21px;
|
|
86
|
+
display: flex;
|
|
87
|
+
|
|
88
|
+
& .prompt-footer__icon {
|
|
89
|
+
height: 20px;
|
|
90
|
+
width: 20px;
|
|
91
|
+
|
|
92
|
+
path {
|
|
93
|
+
fill: var(--studio-gray-20, #a7aaad);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.jetpack-ai-logo-generator__prompt-error {
|
|
99
|
+
color: var(--studio-red-50, #d63638);
|
|
100
|
+
font-size: var(--font-body-extra-small);
|
|
101
|
+
line-height: 21px;
|
|
102
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
|
|
5
|
+
import { Button, Tooltip } from '@wordpress/components';
|
|
6
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
7
|
+
import { Icon, info } from '@wordpress/icons';
|
|
8
|
+
import debugFactory from 'debug';
|
|
9
|
+
import { useCallback, useEffect, useState, useRef } from 'react';
|
|
10
|
+
/**
|
|
11
|
+
* Internal dependencies
|
|
12
|
+
*/
|
|
13
|
+
import AiIcon from '../assets/icons/ai.js';
|
|
14
|
+
import {
|
|
15
|
+
EVENT_GENERATE,
|
|
16
|
+
MINIMUM_PROMPT_LENGTH,
|
|
17
|
+
EVENT_UPGRADE,
|
|
18
|
+
EVENT_PLACEMENT_INPUT_FOOTER,
|
|
19
|
+
} from '../constants.js';
|
|
20
|
+
import { useCheckout } from '../hooks/use-checkout.js';
|
|
21
|
+
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
22
|
+
import useRequestErrors from '../hooks/use-request-errors.js';
|
|
23
|
+
import { UpgradeNudge } from './upgrade-nudge.js';
|
|
24
|
+
import './prompt.scss';
|
|
25
|
+
|
|
26
|
+
const debug = debugFactory( 'jetpack-ai-calypso:prompt-box' );
|
|
27
|
+
|
|
28
|
+
export const Prompt: React.FC< { initialPrompt?: string } > = ( { initialPrompt = '' } ) => {
|
|
29
|
+
const { tracks } = useAnalytics();
|
|
30
|
+
const { recordEvent: recordTracksEvent } = tracks;
|
|
31
|
+
const [ prompt, setPrompt ] = useState< string >( initialPrompt );
|
|
32
|
+
const [ requestsRemaining, setRequestsRemaining ] = useState( 0 );
|
|
33
|
+
const { enhancePromptFetchError, logoFetchError } = useRequestErrors();
|
|
34
|
+
const { nextTierCheckoutURL: checkoutUrl, hasNextTier } = useCheckout();
|
|
35
|
+
const hasPrompt = prompt?.length >= MINIMUM_PROMPT_LENGTH;
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
generateLogo,
|
|
39
|
+
enhancePrompt,
|
|
40
|
+
setIsEnhancingPrompt,
|
|
41
|
+
isBusy,
|
|
42
|
+
isEnhancingPrompt,
|
|
43
|
+
site,
|
|
44
|
+
getAiAssistantFeature,
|
|
45
|
+
requireUpgrade,
|
|
46
|
+
context,
|
|
47
|
+
} = useLogoGenerator();
|
|
48
|
+
|
|
49
|
+
const enhancingLabel = __( 'Enhancing…', 'jetpack-ai-client' );
|
|
50
|
+
const enhanceLabel = __( 'Enhance prompt', 'jetpack-ai-client' );
|
|
51
|
+
const enhanceButtonLabel = isEnhancingPrompt ? enhancingLabel : enhanceLabel;
|
|
52
|
+
|
|
53
|
+
const inputRef = useRef< HTMLDivElement | null >( null );
|
|
54
|
+
|
|
55
|
+
const onEnhance = useCallback( async () => {
|
|
56
|
+
debug( 'Enhancing prompt', prompt );
|
|
57
|
+
setIsEnhancingPrompt( true );
|
|
58
|
+
recordTracksEvent( EVENT_GENERATE, { context, tool: 'enhance-prompt' } );
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const enhancedPrompt = await enhancePrompt( { prompt } );
|
|
62
|
+
setPrompt( enhancedPrompt );
|
|
63
|
+
setIsEnhancingPrompt( false );
|
|
64
|
+
} catch ( error ) {
|
|
65
|
+
debug( 'Error enhancing prompt', error );
|
|
66
|
+
setIsEnhancingPrompt( false );
|
|
67
|
+
}
|
|
68
|
+
}, [ context, enhancePrompt, prompt, setIsEnhancingPrompt ] );
|
|
69
|
+
|
|
70
|
+
const featureData = getAiAssistantFeature( String( site?.id || '' ) );
|
|
71
|
+
|
|
72
|
+
const currentLimit = featureData?.currentTier?.value || 0;
|
|
73
|
+
const currentUsage = featureData?.usagePeriod?.requestsCount || 0;
|
|
74
|
+
const isUnlimited = currentLimit === 1;
|
|
75
|
+
|
|
76
|
+
useEffect( () => {
|
|
77
|
+
if ( currentLimit - currentUsage <= 0 ) {
|
|
78
|
+
setRequestsRemaining( 0 );
|
|
79
|
+
} else {
|
|
80
|
+
setRequestsRemaining( currentLimit - currentUsage );
|
|
81
|
+
}
|
|
82
|
+
}, [ currentLimit, currentUsage ] );
|
|
83
|
+
|
|
84
|
+
useEffect( () => {
|
|
85
|
+
// Update prompt text node after enhancement
|
|
86
|
+
if ( inputRef.current && inputRef.current.textContent !== prompt ) {
|
|
87
|
+
inputRef.current.textContent = prompt;
|
|
88
|
+
}
|
|
89
|
+
}, [ prompt ] );
|
|
90
|
+
|
|
91
|
+
const onGenerate = useCallback( async () => {
|
|
92
|
+
recordTracksEvent( EVENT_GENERATE, { context, tool: 'image' } );
|
|
93
|
+
generateLogo( { prompt } );
|
|
94
|
+
}, [ context, generateLogo, prompt ] );
|
|
95
|
+
|
|
96
|
+
const onPromptInput = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
97
|
+
setPrompt( event.target.textContent || '' );
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const onPromptPaste = ( event: React.ClipboardEvent< HTMLInputElement > ) => {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
|
|
103
|
+
// Paste plain text only
|
|
104
|
+
const text = event.clipboardData.getData( 'text/plain' );
|
|
105
|
+
|
|
106
|
+
const selection = window.getSelection();
|
|
107
|
+
if ( ! selection || ! selection.rangeCount ) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
selection.deleteFromDocument();
|
|
111
|
+
const range = selection.getRangeAt( 0 );
|
|
112
|
+
range.insertNode( document.createTextNode( text ) );
|
|
113
|
+
selection.collapseToEnd();
|
|
114
|
+
|
|
115
|
+
setPrompt( inputRef.current?.textContent || '' );
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const onUpgradeClick = () => {
|
|
119
|
+
recordTracksEvent( EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_INPUT_FOOTER } );
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<div className="jetpack-ai-logo-generator__prompt">
|
|
124
|
+
<div className="jetpack-ai-logo-generator__prompt-header">
|
|
125
|
+
<div className="jetpack-ai-logo-generator__prompt-label">
|
|
126
|
+
{ __( 'Describe your site:', 'jetpack-ai-client' ) }
|
|
127
|
+
</div>
|
|
128
|
+
<div className="jetpack-ai-logo-generator__prompt-actions">
|
|
129
|
+
<Button
|
|
130
|
+
variant="link"
|
|
131
|
+
disabled={ isBusy || requireUpgrade || ! hasPrompt }
|
|
132
|
+
onClick={ onEnhance }
|
|
133
|
+
>
|
|
134
|
+
<AiIcon />
|
|
135
|
+
<span>{ enhanceButtonLabel }</span>
|
|
136
|
+
</Button>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div className="jetpack-ai-logo-generator__prompt-query">
|
|
140
|
+
<div
|
|
141
|
+
ref={ inputRef }
|
|
142
|
+
contentEditable={ ! isBusy && ! requireUpgrade }
|
|
143
|
+
// The content editable div is expected to be updated by the enhance prompt, so warnings are suppressed
|
|
144
|
+
suppressContentEditableWarning
|
|
145
|
+
className="prompt-query__input"
|
|
146
|
+
onInput={ onPromptInput }
|
|
147
|
+
onPaste={ onPromptPaste }
|
|
148
|
+
data-placeholder={ __(
|
|
149
|
+
'Describe your site or simply ask for a logo specifying some details about it',
|
|
150
|
+
'jetpack-ai-client'
|
|
151
|
+
) }
|
|
152
|
+
></div>
|
|
153
|
+
<Button
|
|
154
|
+
variant="primary"
|
|
155
|
+
className="jetpack-ai-logo-generator__prompt-submit"
|
|
156
|
+
onClick={ onGenerate }
|
|
157
|
+
disabled={ isBusy || requireUpgrade || ! hasPrompt }
|
|
158
|
+
>
|
|
159
|
+
{ __( 'Generate', 'jetpack-ai-client' ) }
|
|
160
|
+
</Button>
|
|
161
|
+
</div>
|
|
162
|
+
<div className="jetpack-ai-logo-generator__prompt-footer">
|
|
163
|
+
{ ! isUnlimited && ! requireUpgrade && (
|
|
164
|
+
<div className="jetpack-ai-logo-generator__prompt-requests">
|
|
165
|
+
<div>
|
|
166
|
+
{ sprintf(
|
|
167
|
+
// translators: %u is the number of requests
|
|
168
|
+
__( '%u requests remaining.', 'jetpack-ai-client' ),
|
|
169
|
+
requestsRemaining
|
|
170
|
+
) }
|
|
171
|
+
</div>
|
|
172
|
+
{ hasNextTier && (
|
|
173
|
+
<>
|
|
174
|
+
|
|
175
|
+
<Button
|
|
176
|
+
variant="link"
|
|
177
|
+
href={ checkoutUrl }
|
|
178
|
+
target="_blank"
|
|
179
|
+
onClick={ onUpgradeClick }
|
|
180
|
+
>
|
|
181
|
+
{ __( 'Upgrade', 'jetpack-ai-client' ) }
|
|
182
|
+
</Button>
|
|
183
|
+
</>
|
|
184
|
+
) }
|
|
185
|
+
|
|
186
|
+
<Tooltip
|
|
187
|
+
text={ __(
|
|
188
|
+
'Logo generation costs 10 requests; prompt enhancement costs 1 request each',
|
|
189
|
+
'jetpack-ai-client'
|
|
190
|
+
) }
|
|
191
|
+
placement="bottom"
|
|
192
|
+
>
|
|
193
|
+
<Icon className="prompt-footer__icon" icon={ info } />
|
|
194
|
+
</Tooltip>
|
|
195
|
+
</div>
|
|
196
|
+
) }
|
|
197
|
+
{ ! isUnlimited && requireUpgrade && <UpgradeNudge /> }
|
|
198
|
+
{ enhancePromptFetchError && (
|
|
199
|
+
<div className="jetpack-ai-logo-generator__prompt-error">
|
|
200
|
+
{ __( 'Error enhancing prompt. Please try again.', 'jetpack-ai-client' ) }
|
|
201
|
+
</div>
|
|
202
|
+
) }
|
|
203
|
+
{ logoFetchError && (
|
|
204
|
+
<div className="jetpack-ai-logo-generator__prompt-error">
|
|
205
|
+
{ __( 'Error generating logo. Please try again.', 'jetpack-ai-client' ) }
|
|
206
|
+
</div>
|
|
207
|
+
) }
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
@import '@automattic/jetpack-base-styles/root-variables';
|
|
2
|
+
|
|
3
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper {
|
|
4
|
+
display: flex;
|
|
5
|
+
justify-content: space-between;
|
|
6
|
+
align-items: center;
|
|
7
|
+
font-size: var(--font-body-small);
|
|
8
|
+
background: var(--jp-black);
|
|
9
|
+
padding: 8px 16px;
|
|
10
|
+
border-radius: 2px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .jetpack-upgrade-plan-banner__banner-description {
|
|
14
|
+
color: var(--jp-white);
|
|
15
|
+
line-height: 21px;
|
|
16
|
+
word-wrap: break-word;
|
|
17
|
+
vertical-align: middle;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .jetpack-upgrade-plan-banner__icon {
|
|
21
|
+
width: 24px;
|
|
22
|
+
height: 24px;
|
|
23
|
+
position: relative;
|
|
24
|
+
vertical-align: middle;
|
|
25
|
+
margin-right: 8px;
|
|
26
|
+
|
|
27
|
+
path {
|
|
28
|
+
fill: var(--jp-gray-30);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button {
|
|
33
|
+
height: auto;
|
|
34
|
+
line-height: 20px;
|
|
35
|
+
font-size: var(--font-body-extra-small);
|
|
36
|
+
font-weight: 600;
|
|
37
|
+
padding: 4px 8px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.jetpack-upgrade-plan-banner .jetpack-upgrade-plan-banner__wrapper .components-button.is-primary {
|
|
41
|
+
background: var(--jp-white);
|
|
42
|
+
color: var(--jp-black);
|
|
43
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
|
|
5
|
+
import { Button } from '@wordpress/components';
|
|
6
|
+
import { createInterpolateElement } from '@wordpress/element';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
import { Icon, warning } from '@wordpress/icons';
|
|
9
|
+
/**
|
|
10
|
+
* Internal dependencies
|
|
11
|
+
*/
|
|
12
|
+
import { EVENT_PLACEMENT_UPGRADE_PROMPT, EVENT_UPGRADE } from '../constants.js';
|
|
13
|
+
import { useCheckout } from '../hooks/use-checkout.js';
|
|
14
|
+
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
15
|
+
import './upgrade-nudge.scss';
|
|
16
|
+
|
|
17
|
+
export const UpgradeNudge = () => {
|
|
18
|
+
const { tracks } = useAnalytics();
|
|
19
|
+
const { recordEvent: recordTracksEvent } = tracks;
|
|
20
|
+
const buttonText = __( 'Upgrade', 'jetpack-ai-client' );
|
|
21
|
+
const upgradeMessage = createInterpolateElement(
|
|
22
|
+
__(
|
|
23
|
+
'Not enough requests left to generate a logo. <strong>Upgrade now to increase it.</strong>',
|
|
24
|
+
'jetpack-ai-client'
|
|
25
|
+
),
|
|
26
|
+
{
|
|
27
|
+
strong: <strong />,
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const { nextTierCheckoutURL: checkoutUrl } = useCheckout();
|
|
32
|
+
const { context } = useLogoGenerator();
|
|
33
|
+
|
|
34
|
+
const handleUpgradeClick = () => {
|
|
35
|
+
recordTracksEvent( EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_UPGRADE_PROMPT } );
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="jetpack-upgrade-plan-banner">
|
|
40
|
+
<div className="jetpack-upgrade-plan-banner__wrapper">
|
|
41
|
+
<div>
|
|
42
|
+
<Icon className="jetpack-upgrade-plan-banner__icon" icon={ warning } />
|
|
43
|
+
<span className="jetpack-upgrade-plan-banner__banner-description">
|
|
44
|
+
{ upgradeMessage }
|
|
45
|
+
</span>
|
|
46
|
+
</div>
|
|
47
|
+
<Button
|
|
48
|
+
href={ checkoutUrl }
|
|
49
|
+
target="_blank"
|
|
50
|
+
className="is-primary"
|
|
51
|
+
onClick={ handleUpgradeClick }
|
|
52
|
+
>
|
|
53
|
+
{ buttonText }
|
|
54
|
+
</Button>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useAnalytics } from '@automattic/jetpack-shared-extension-utils';
|
|
5
|
+
import { Button } from '@wordpress/components';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { EVENT_PLACEMENT_FREE_USER_SCREEN, EVENT_UPGRADE } from '../constants.js';
|
|
11
|
+
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
12
|
+
/**
|
|
13
|
+
* Types
|
|
14
|
+
*/
|
|
15
|
+
import type React from 'react';
|
|
16
|
+
|
|
17
|
+
export const UpgradeScreen: React.FC< {
|
|
18
|
+
onCancel: () => void;
|
|
19
|
+
upgradeURL: string;
|
|
20
|
+
reason: 'feature' | 'requests';
|
|
21
|
+
} > = ( { onCancel, upgradeURL, reason } ) => {
|
|
22
|
+
const { tracks } = useAnalytics();
|
|
23
|
+
const { recordEvent: recordTracksEvent } = tracks;
|
|
24
|
+
const upgradeMessageFeature = __(
|
|
25
|
+
'Upgrade your Jetpack AI for access to exclusive features, including logo generation. This upgrade will also increase the amount of requests you can use in all AI-powered features.',
|
|
26
|
+
'jetpack-ai-client'
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const upgradeMessageRequests = __(
|
|
30
|
+
'Not enough requests left to generate a logo. Upgrade your Jetpack AI to increase the amount of requests you can use in all AI-powered features.',
|
|
31
|
+
'jetpack-ai-client'
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const { context } = useLogoGenerator();
|
|
35
|
+
|
|
36
|
+
const handleUpgradeClick = () => {
|
|
37
|
+
recordTracksEvent( EVENT_UPGRADE, { context, placement: EVENT_PLACEMENT_FREE_USER_SCREEN } );
|
|
38
|
+
onCancel();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="jetpack-ai-logo-generator-modal__notice-message-wrapper">
|
|
43
|
+
<div className="jetpack-ai-logo-generator-modal__notice-message">
|
|
44
|
+
<span className="jetpack-ai-logo-generator-modal__loading-message">
|
|
45
|
+
{ reason === 'feature' ? upgradeMessageFeature : upgradeMessageRequests }
|
|
46
|
+
</span>
|
|
47
|
+
|
|
48
|
+
<Button variant="link" href="https://jetpack.com/ai/" target="_blank">
|
|
49
|
+
{ __( 'Learn more', 'jetpack-ai-client' ) }
|
|
50
|
+
</Button>
|
|
51
|
+
</div>
|
|
52
|
+
<div className="jetpack-ai-logo-generator-modal__notice-actions">
|
|
53
|
+
<Button variant="tertiary" onClick={ onCancel }>
|
|
54
|
+
{ __( 'Cancel', 'jetpack-ai-client' ) }
|
|
55
|
+
</Button>
|
|
56
|
+
<Button
|
|
57
|
+
variant="primary"
|
|
58
|
+
href={ upgradeURL }
|
|
59
|
+
target="_blank"
|
|
60
|
+
onClick={ handleUpgradeClick }
|
|
61
|
+
>
|
|
62
|
+
{ __( 'Upgrade', 'jetpack-ai-client' ) }
|
|
63
|
+
</Button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|