@automattic/jetpack-ai-client 0.25.7 → 0.26.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.
Files changed (227) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build/components/ai-icon/index.d.ts +10 -0
  3. package/build/components/ai-icon/index.js +16 -0
  4. package/build/components/ai-image/components/ai-image-modal.d.ts +57 -0
  5. package/build/components/ai-image/components/ai-image-modal.js +92 -0
  6. package/build/components/ai-image/components/carrousel.d.ts +28 -0
  7. package/build/components/ai-image/components/carrousel.js +69 -0
  8. package/build/components/ai-image/components/usage-counter.d.ts +16 -0
  9. package/build/components/ai-image/components/usage-counter.js +29 -0
  10. package/build/components/ai-image/featured-image.d.ts +17 -0
  11. package/build/components/ai-image/featured-image.js +278 -0
  12. package/build/components/ai-image/general-purpose-image.d.ts +23 -0
  13. package/build/components/ai-image/general-purpose-image.js +184 -0
  14. package/build/components/ai-image/hooks/use-ai-image.d.ts +48 -0
  15. package/build/components/ai-image/hooks/use-ai-image.js +219 -0
  16. package/build/components/ai-image/hooks/use-site-type.d.ts +6 -0
  17. package/build/components/ai-image/hooks/use-site-type.js +23 -0
  18. package/build/components/ai-image/index.d.ts +4 -0
  19. package/build/components/ai-image/index.js +4 -0
  20. package/build/components/ai-image/types.d.ts +16 -0
  21. package/build/components/ai-image/types.js +6 -0
  22. package/build/{ai-client/src/components → components}/index.d.ts +4 -0
  23. package/build/{ai-client/src/components → components}/index.js +4 -0
  24. package/build/components/modal/index.d.ts +18 -0
  25. package/build/components/modal/index.js +23 -0
  26. package/build/components/quota-exceeded-message/index.d.ts +13 -0
  27. package/build/components/quota-exceeded-message/index.js +152 -0
  28. package/build/components/quota-exceeded-message/light-nudge.d.ts +11 -0
  29. package/build/components/quota-exceeded-message/light-nudge.js +8 -0
  30. package/build/{ai-client/src/constants.d.ts → constants.d.ts} +3 -0
  31. package/build/{ai-client/src/constants.js → constants.js} +4 -0
  32. package/build/hooks/use-ai-checkout/index.d.ts +13 -0
  33. package/build/hooks/use-ai-checkout/index.js +41 -0
  34. package/build/hooks/use-ai-feature/index.d.ts +33 -0
  35. package/build/hooks/use-ai-feature/index.js +37 -0
  36. package/build/hooks/use-post-content.d.ts +5 -0
  37. package/build/hooks/use-post-content.js +20 -0
  38. package/build/hooks/use-save-to-media-library.d.ts +12 -0
  39. package/build/hooks/use-save-to-media-library.js +74 -0
  40. package/build/{ai-client/src/index.d.ts → index.d.ts} +3 -0
  41. package/build/{ai-client/src/index.js → index.js} +3 -0
  42. package/build/{ai-client/src/logo-generator → logo-generator}/components/upgrade-screen.js +1 -1
  43. package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-fair-usage-notice-message.js +1 -1
  44. package/package.json +20 -14
  45. package/src/components/ai-icon/index.tsx +39 -0
  46. package/src/components/ai-image/components/ai-image-modal.scss +88 -0
  47. package/src/components/ai-image/components/ai-image-modal.tsx +240 -0
  48. package/src/components/ai-image/components/carrousel.scss +163 -0
  49. package/src/components/ai-image/components/carrousel.tsx +217 -0
  50. package/src/components/ai-image/components/usage-counter.scss +19 -0
  51. package/src/components/ai-image/components/usage-counter.tsx +54 -0
  52. package/src/components/ai-image/featured-image.tsx +439 -0
  53. package/src/components/ai-image/general-purpose-image.tsx +303 -0
  54. package/src/components/ai-image/hooks/use-ai-image.ts +339 -0
  55. package/src/components/ai-image/hooks/use-site-type.ts +26 -0
  56. package/src/components/ai-image/index.ts +10 -0
  57. package/src/components/ai-image/style.scss +95 -0
  58. package/src/components/ai-image/types.ts +19 -0
  59. package/src/components/index.ts +12 -0
  60. package/src/components/modal/index.tsx +70 -0
  61. package/src/components/modal/style.scss +45 -0
  62. package/src/components/quota-exceeded-message/index.tsx +319 -0
  63. package/src/components/quota-exceeded-message/light-nudge.tsx +38 -0
  64. package/src/components/quota-exceeded-message/style.scss +35 -0
  65. package/src/constants.ts +5 -0
  66. package/src/hooks/use-ai-checkout/index.ts +65 -0
  67. package/src/hooks/use-ai-feature/Readme.md +20 -0
  68. package/src/hooks/use-ai-feature/index.ts +62 -0
  69. package/src/hooks/use-post-content.ts +27 -0
  70. package/src/hooks/use-save-to-media-library.ts +100 -0
  71. package/src/index.ts +3 -0
  72. package/src/logo-generator/components/upgrade-screen.tsx +1 -1
  73. package/src/logo-generator/hooks/use-fair-usage-notice-message.tsx +1 -1
  74. package/build/components/tools/jp-redirect/index.d.ts +0 -20
  75. package/build/components/tools/jp-redirect/index.js +0 -50
  76. package/build/components/tools/jp-redirect/types.d.ts +0 -39
  77. package/build/components/tools/jp-redirect/types.js +0 -1
  78. /package/build/{ai-client/src/api-fetch → api-fetch}/index.d.ts +0 -0
  79. /package/build/{ai-client/src/api-fetch → api-fetch}/index.js +0 -0
  80. /package/build/{ai-client/src/ask-question → ask-question}/index.d.ts +0 -0
  81. /package/build/{ai-client/src/ask-question → ask-question}/index.js +0 -0
  82. /package/build/{ai-client/src/ask-question → ask-question}/sync.d.ts +0 -0
  83. /package/build/{ai-client/src/ask-question → ask-question}/sync.js +0 -0
  84. /package/build/{ai-client/src/audio-transcription → audio-transcription}/index.d.ts +0 -0
  85. /package/build/{ai-client/src/audio-transcription → audio-transcription}/index.js +0 -0
  86. /package/build/{ai-client/src/components → components}/ai-control/ai-control.d.ts +0 -0
  87. /package/build/{ai-client/src/components → components}/ai-control/ai-control.js +0 -0
  88. /package/build/{ai-client/src/components → components}/ai-control/block-ai-control.d.ts +0 -0
  89. /package/build/{ai-client/src/components → components}/ai-control/block-ai-control.js +0 -0
  90. /package/build/{ai-client/src/components → components}/ai-control/extension-ai-control.d.ts +0 -0
  91. /package/build/{ai-client/src/components → components}/ai-control/extension-ai-control.js +0 -0
  92. /package/build/{ai-client/src/components → components}/ai-control/index.d.ts +0 -0
  93. /package/build/{ai-client/src/components → components}/ai-control/index.js +0 -0
  94. /package/build/{ai-client/src/components → components}/ai-feedback/index.d.ts +0 -0
  95. /package/build/{ai-client/src/components → components}/ai-feedback/index.js +0 -0
  96. /package/build/{ai-client/src/components → components}/ai-modal-footer/index.d.ts +0 -0
  97. /package/build/{ai-client/src/components → components}/ai-modal-footer/index.js +0 -0
  98. /package/build/{ai-client/src/components → components}/ai-status-indicator/index.d.ts +0 -0
  99. /package/build/{ai-client/src/components → components}/ai-status-indicator/index.js +0 -0
  100. /package/build/{ai-client/src/components → components}/audio-duration-display/index.d.ts +0 -0
  101. /package/build/{ai-client/src/components → components}/audio-duration-display/index.js +0 -0
  102. /package/build/{ai-client/src/components → components}/audio-duration-display/lib/media.d.ts +0 -0
  103. /package/build/{ai-client/src/components → components}/audio-duration-display/lib/media.js +0 -0
  104. /package/build/{ai-client/src/components → components}/message/index.d.ts +0 -0
  105. /package/build/{ai-client/src/components → components}/message/index.js +0 -0
  106. /package/build/{ai-client/src/data-flow → data-flow}/context.d.ts +0 -0
  107. /package/build/{ai-client/src/data-flow → data-flow}/context.js +0 -0
  108. /package/build/{ai-client/src/data-flow → data-flow}/index.d.ts +0 -0
  109. /package/build/{ai-client/src/data-flow → data-flow}/index.js +0 -0
  110. /package/build/{ai-client/src/data-flow → data-flow}/use-ai-context.d.ts +0 -0
  111. /package/build/{ai-client/src/data-flow → data-flow}/use-ai-context.js +0 -0
  112. /package/build/{ai-client/src/data-flow → data-flow}/with-ai-assistant-data.d.ts +0 -0
  113. /package/build/{ai-client/src/data-flow → data-flow}/with-ai-assistant-data.js +0 -0
  114. /package/build/{ai-client/src/hooks → hooks}/use-ai-suggestions/index.d.ts +0 -0
  115. /package/build/{ai-client/src/hooks → hooks}/use-ai-suggestions/index.js +0 -0
  116. /package/build/{ai-client/src/hooks → hooks}/use-audio-transcription/index.d.ts +0 -0
  117. /package/build/{ai-client/src/hooks → hooks}/use-audio-transcription/index.js +0 -0
  118. /package/build/{ai-client/src/hooks → hooks}/use-audio-validation/index.d.ts +0 -0
  119. /package/build/{ai-client/src/hooks → hooks}/use-audio-validation/index.js +0 -0
  120. /package/build/{ai-client/src/hooks → hooks}/use-image-generator/constants.d.ts +0 -0
  121. /package/build/{ai-client/src/hooks → hooks}/use-image-generator/constants.js +0 -0
  122. /package/build/{ai-client/src/hooks → hooks}/use-image-generator/index.d.ts +0 -0
  123. /package/build/{ai-client/src/hooks → hooks}/use-image-generator/index.js +0 -0
  124. /package/build/{ai-client/src/hooks → hooks}/use-media-recording/index.d.ts +0 -0
  125. /package/build/{ai-client/src/hooks → hooks}/use-media-recording/index.js +0 -0
  126. /package/build/{ai-client/src/hooks → hooks}/use-save-to-media-library/index.d.ts +0 -0
  127. /package/build/{ai-client/src/hooks → hooks}/use-save-to-media-library/index.js +0 -0
  128. /package/build/{ai-client/src/hooks → hooks}/use-transcription-post-processing/index.d.ts +0 -0
  129. /package/build/{ai-client/src/hooks → hooks}/use-transcription-post-processing/index.js +0 -0
  130. /package/build/{ai-client/src/icons → icons}/ai-assistant.d.ts +0 -0
  131. /package/build/{ai-client/src/icons → icons}/ai-assistant.js +0 -0
  132. /package/build/{ai-client/src/icons → icons}/error-exclamation.d.ts +0 -0
  133. /package/build/{ai-client/src/icons → icons}/error-exclamation.js +0 -0
  134. /package/build/{ai-client/src/icons → icons}/index.d.ts +0 -0
  135. /package/build/{ai-client/src/icons → icons}/index.js +0 -0
  136. /package/build/{ai-client/src/icons → icons}/mic.d.ts +0 -0
  137. /package/build/{ai-client/src/icons → icons}/mic.js +0 -0
  138. /package/build/{ai-client/src/icons → icons}/origami-plane.d.ts +0 -0
  139. /package/build/{ai-client/src/icons → icons}/origami-plane.js +0 -0
  140. /package/build/{ai-client/src/icons → icons}/player-pause.d.ts +0 -0
  141. /package/build/{ai-client/src/icons → icons}/player-pause.js +0 -0
  142. /package/build/{ai-client/src/icons → icons}/player-play.d.ts +0 -0
  143. /package/build/{ai-client/src/icons → icons}/player-play.js +0 -0
  144. /package/build/{ai-client/src/icons → icons}/player-stop.d.ts +0 -0
  145. /package/build/{ai-client/src/icons → icons}/player-stop.js +0 -0
  146. /package/build/{ai-client/src/icons → icons}/speak-tone.d.ts +0 -0
  147. /package/build/{ai-client/src/icons → icons}/speak-tone.js +0 -0
  148. /package/build/{ai-client/src/jwt → jwt}/index.d.ts +0 -0
  149. /package/build/{ai-client/src/jwt → jwt}/index.js +0 -0
  150. /package/build/{ai-client/src/libs → libs}/index.d.ts +0 -0
  151. /package/build/{ai-client/src/libs → libs}/index.js +0 -0
  152. /package/build/{ai-client/src/libs → libs}/map-action-to-human-text.d.ts +0 -0
  153. /package/build/{ai-client/src/libs → libs}/map-action-to-human-text.js +0 -0
  154. /package/build/{ai-client/src/libs → libs}/markdown/html-to-markdown.d.ts +0 -0
  155. /package/build/{ai-client/src/libs → libs}/markdown/html-to-markdown.js +0 -0
  156. /package/build/{ai-client/src/libs → libs}/markdown/index.d.ts +0 -0
  157. /package/build/{ai-client/src/libs → libs}/markdown/index.js +0 -0
  158. /package/build/{ai-client/src/libs → libs}/markdown/markdown-to-html.d.ts +0 -0
  159. /package/build/{ai-client/src/libs → libs}/markdown/markdown-to-html.js +0 -0
  160. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/ai.d.ts +0 -0
  161. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/ai.js +0 -0
  162. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/check.d.ts +0 -0
  163. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/check.js +0 -0
  164. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/logo.d.ts +0 -0
  165. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/logo.js +0 -0
  166. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/media.d.ts +0 -0
  167. /package/build/{ai-client/src/logo-generator → logo-generator}/assets/icons/media.js +0 -0
  168. /package/build/{ai-client/src/logo-generator → logo-generator}/components/fair-usage-notice.d.ts +0 -0
  169. /package/build/{ai-client/src/logo-generator → logo-generator}/components/fair-usage-notice.js +0 -0
  170. /package/build/{ai-client/src/logo-generator → logo-generator}/components/feature-fetch-failure-screen.d.ts +0 -0
  171. /package/build/{ai-client/src/logo-generator → logo-generator}/components/feature-fetch-failure-screen.js +0 -0
  172. /package/build/{ai-client/src/logo-generator → logo-generator}/components/first-load-screen.d.ts +0 -0
  173. /package/build/{ai-client/src/logo-generator → logo-generator}/components/first-load-screen.js +0 -0
  174. /package/build/{ai-client/src/logo-generator → logo-generator}/components/generator-modal.d.ts +0 -0
  175. /package/build/{ai-client/src/logo-generator → logo-generator}/components/generator-modal.js +0 -0
  176. /package/build/{ai-client/src/logo-generator → logo-generator}/components/history-carousel.d.ts +0 -0
  177. /package/build/{ai-client/src/logo-generator → logo-generator}/components/history-carousel.js +0 -0
  178. /package/build/{ai-client/src/logo-generator → logo-generator}/components/image-loader.d.ts +0 -0
  179. /package/build/{ai-client/src/logo-generator → logo-generator}/components/image-loader.js +0 -0
  180. /package/build/{ai-client/src/logo-generator → logo-generator}/components/logo-presenter.d.ts +0 -0
  181. /package/build/{ai-client/src/logo-generator → logo-generator}/components/logo-presenter.js +0 -0
  182. /package/build/{ai-client/src/logo-generator → logo-generator}/components/prompt.d.ts +0 -0
  183. /package/build/{ai-client/src/logo-generator → logo-generator}/components/prompt.js +0 -0
  184. /package/build/{ai-client/src/logo-generator → logo-generator}/components/upgrade-nudge.d.ts +0 -0
  185. /package/build/{ai-client/src/logo-generator → logo-generator}/components/upgrade-nudge.js +0 -0
  186. /package/build/{ai-client/src/logo-generator → logo-generator}/components/upgrade-screen.d.ts +0 -0
  187. /package/build/{ai-client/src/logo-generator → logo-generator}/components/visit-site-banner.d.ts +0 -0
  188. /package/build/{ai-client/src/logo-generator → logo-generator}/components/visit-site-banner.js +0 -0
  189. /package/build/{ai-client/src/logo-generator → logo-generator}/constants.d.ts +0 -0
  190. /package/build/{ai-client/src/logo-generator → logo-generator}/constants.js +0 -0
  191. /package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-checkout.d.ts +0 -0
  192. /package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-checkout.js +0 -0
  193. /package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-fair-usage-notice-message.d.ts +0 -0
  194. /package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-logo-generator.d.ts +0 -0
  195. /package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-logo-generator.js +0 -0
  196. /package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-request-errors.d.ts +0 -0
  197. /package/build/{ai-client/src/logo-generator → logo-generator}/hooks/use-request-errors.js +0 -0
  198. /package/build/{ai-client/src/logo-generator → logo-generator}/index.d.ts +0 -0
  199. /package/build/{ai-client/src/logo-generator → logo-generator}/index.js +0 -0
  200. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/logo-storage.d.ts +0 -0
  201. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/logo-storage.js +0 -0
  202. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/media-exists.d.ts +0 -0
  203. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/media-exists.js +0 -0
  204. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/set-site-logo.d.ts +0 -0
  205. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/set-site-logo.js +0 -0
  206. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/wpcom-limited-request.d.ts +0 -0
  207. /package/build/{ai-client/src/logo-generator → logo-generator}/lib/wpcom-limited-request.js +0 -0
  208. /package/build/{ai-client/src/logo-generator → logo-generator}/store/actions.d.ts +0 -0
  209. /package/build/{ai-client/src/logo-generator → logo-generator}/store/actions.js +0 -0
  210. /package/build/{ai-client/src/logo-generator → logo-generator}/store/constants.d.ts +0 -0
  211. /package/build/{ai-client/src/logo-generator → logo-generator}/store/constants.js +0 -0
  212. /package/build/{ai-client/src/logo-generator → logo-generator}/store/index.d.ts +0 -0
  213. /package/build/{ai-client/src/logo-generator → logo-generator}/store/index.js +0 -0
  214. /package/build/{ai-client/src/logo-generator → logo-generator}/store/initial-state.d.ts +0 -0
  215. /package/build/{ai-client/src/logo-generator → logo-generator}/store/initial-state.js +0 -0
  216. /package/build/{ai-client/src/logo-generator → logo-generator}/store/reducer.d.ts +0 -0
  217. /package/build/{ai-client/src/logo-generator → logo-generator}/store/reducer.js +0 -0
  218. /package/build/{ai-client/src/logo-generator → logo-generator}/store/selectors.d.ts +0 -0
  219. /package/build/{ai-client/src/logo-generator → logo-generator}/store/selectors.js +0 -0
  220. /package/build/{ai-client/src/logo-generator → logo-generator}/store/types.d.ts +0 -0
  221. /package/build/{ai-client/src/logo-generator → logo-generator}/store/types.js +0 -0
  222. /package/build/{ai-client/src/logo-generator → logo-generator}/types.d.ts +0 -0
  223. /package/build/{ai-client/src/logo-generator → logo-generator}/types.js +0 -0
  224. /package/build/{ai-client/src/suggestions-event-source → suggestions-event-source}/index.d.ts +0 -0
  225. /package/build/{ai-client/src/suggestions-event-source → suggestions-event-source}/index.js +0 -0
  226. /package/build/{ai-client/src/types.d.ts → types.d.ts} +0 -0
  227. /package/build/{ai-client/src/types.js → types.js} +0 -0
@@ -0,0 +1,19 @@
1
+ @import '@automattic/jetpack-base-styles/style';
2
+
3
+ .ai-assistant-featured-image__usage-counter {
4
+ color: var(--studio-gray-50, #646970);
5
+ font-size: 12px;
6
+ display: flex;
7
+ flex-direction: column;
8
+
9
+ & > span {
10
+ display: flex;
11
+ justify-content: space-between;
12
+ align-items: center;
13
+ gap: 8px;
14
+ }
15
+
16
+ &-no-limit {
17
+ color: red;
18
+ }
19
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { createInterpolateElement } from '@wordpress/element';
5
+ import { __, sprintf } from '@wordpress/i18n';
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import './usage-counter.scss';
10
+
11
+ type UsageCounterProps = {
12
+ currentLimit: number;
13
+ currentUsage: number;
14
+ cost: number;
15
+ };
16
+
17
+ /**
18
+ * UsageCounter component
19
+ * @param {UsageCounterProps} props - The component properties.
20
+ * @return {React.ReactElement} - rendered component.
21
+ */
22
+ export default function UsageCounter( { currentLimit, currentUsage, cost }: UsageCounterProps ) {
23
+ const requestsBalance = currentLimit - currentUsage;
24
+
25
+ const requestsNeeded = createInterpolateElement(
26
+ // Translators: %d is the cost of one image.
27
+ sprintf( __( 'Requests needed: <counter>%d</counter>', 'jetpack-ai-client' ), cost ),
28
+ {
29
+ counter: <span />,
30
+ }
31
+ );
32
+ const requestsAvailable = createInterpolateElement(
33
+ sprintf(
34
+ // Translators: %d is the current requests balance.
35
+ __( 'Requests available: <counter>%d</counter>', 'jetpack-ai-client' ),
36
+ requestsBalance
37
+ ),
38
+ {
39
+ counter:
40
+ requestsBalance < cost ? (
41
+ <span className="ai-assistant-featured-image__usage-counter-no-limit" />
42
+ ) : (
43
+ <strong />
44
+ ),
45
+ }
46
+ );
47
+
48
+ return (
49
+ <div className="ai-assistant-featured-image__usage-counter">
50
+ <span>{ requestsNeeded }</span>
51
+ <span>{ requestsAvailable }</span>
52
+ </div>
53
+ );
54
+ }
@@ -0,0 +1,439 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import {
5
+ useAnalytics,
6
+ PLAN_TYPE_UNLIMITED,
7
+ usePlanType,
8
+ } from '@automattic/jetpack-shared-extension-utils';
9
+ import { Button } from '@wordpress/components';
10
+ import { useDispatch, useSelect } from '@wordpress/data';
11
+ import { store as editorStore } from '@wordpress/editor';
12
+ import { useCallback, useState } from '@wordpress/element';
13
+ import { __, sprintf } from '@wordpress/i18n';
14
+ import debugFactory from 'debug';
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import './style.scss';
19
+ import { PLACEMENT_JETPACK_SIDEBAR, PLACEMENT_DOCUMENT_SETTINGS } from '../../constants.js';
20
+ import useAiFeature from '../../hooks/use-ai-feature/index.js';
21
+ import { ImageStyle } from '../../hooks/use-image-generator/constants.js';
22
+ import usePostContent from '../../hooks/use-post-content.js';
23
+ import useSaveToMediaLibrary from '../../hooks/use-save-to-media-library.js';
24
+ import AiImageModal from './components/ai-image-modal.js';
25
+ import useAiImage from './hooks/use-ai-image.js';
26
+ import useSiteType from './hooks/use-site-type.js';
27
+ import {
28
+ FEATURED_IMAGE_FEATURE_NAME,
29
+ IMAGE_GENERATION_MODEL_STABLE_DIFFUSION,
30
+ IMAGE_GENERATION_MODEL_DALL_E_3,
31
+ PLACEMENT_MEDIA_SOURCE_DROPDOWN,
32
+ } from './types.js';
33
+ import type { ImageResponse } from './hooks/use-ai-image.js';
34
+ import type { EditorSelectors } from './types.js';
35
+
36
+ const debug = debugFactory( 'jetpack-ai-client:featured-image' );
37
+
38
+ type FeaturedImageProps = {
39
+ busy: boolean;
40
+ disabled: boolean;
41
+ placement: string;
42
+ onClose?: () => void;
43
+ };
44
+
45
+ /**
46
+ * FeaturedImage component
47
+ * @param {FeaturedImageProps} props - The component properties.
48
+ * @return {React.ReactElement} - rendered component.
49
+ */
50
+ export default function FeaturedImage( {
51
+ busy,
52
+ disabled,
53
+ placement,
54
+ onClose = () => {},
55
+ }: FeaturedImageProps ) {
56
+ const [ isFeaturedImageModalVisible, setIsFeaturedImageModalVisible ] = useState(
57
+ placement === PLACEMENT_MEDIA_SOURCE_DROPDOWN
58
+ );
59
+ const siteType = useSiteType();
60
+ const postContent = usePostContent();
61
+ const { postTitle, postFeaturedMediaId, isEditorPanelOpened } = useSelect( select => {
62
+ return {
63
+ postTitle: select( editorStore ).getEditedPostAttribute( 'title' ),
64
+ postFeaturedMediaId: select( editorStore ).getEditedPostAttribute( 'featured_media' ),
65
+ isEditorPanelOpened:
66
+ select( editorStore ).isEditorPanelOpened ??
67
+ ( select( 'core/edit-post' ) as EditorSelectors ).isEditorPanelOpened,
68
+ };
69
+ }, [] );
70
+
71
+ const { saveToMediaLibrary } = useSaveToMediaLibrary();
72
+ const { tracks } = useAnalytics();
73
+ const { recordEvent } = tracks;
74
+ const [ requestStyle, setRequestStyle ] = useState< ImageStyle >( null );
75
+ const [ prompt, setPrompt ] = useState( '' );
76
+
77
+ // Editor actions
78
+ const { enableComplementaryArea } = useDispatch( 'core/interface' );
79
+ const { clearSelectedBlock } = useDispatch( 'core/block-editor' );
80
+ const { toggleEditorPanelOpened: toggleEditorPanelOpenedFromEditPost } =
81
+ useDispatch( 'core/edit-post' );
82
+ const { editPost, toggleEditorPanelOpened: toggleEditorPanelOpenedFromEditor } =
83
+ useDispatch( editorStore );
84
+
85
+ // Get feature data
86
+ const { requireUpgrade, requestsCount, requestsLimit, currentTier, costs } = useAiFeature();
87
+ const planType = usePlanType( currentTier );
88
+ const featuredImageCost = costs?.[ FEATURED_IMAGE_FEATURE_NAME ]?.activeModel ?? 10;
89
+ const featuredImageActiveModel =
90
+ featuredImageCost === costs?.[ FEATURED_IMAGE_FEATURE_NAME ]?.stableDiffusion
91
+ ? IMAGE_GENERATION_MODEL_STABLE_DIFFUSION
92
+ : IMAGE_GENERATION_MODEL_DALL_E_3;
93
+ const isUnlimited = planType === PLAN_TYPE_UNLIMITED;
94
+ const requestsBalance = requestsLimit - requestsCount;
95
+ const notEnoughRequests = requestsBalance < featuredImageCost;
96
+
97
+ // Handle deprecation and move of toggle action from edit-post.
98
+ // https://github.com/WordPress/gutenberg/blob/fe4d8cb936df52945c01c1863f7b87b58b7cc69f/packages/edit-post/CHANGELOG.md?plain=1#L19
99
+ const toggleEditorPanelOpened =
100
+ toggleEditorPanelOpenedFromEditor ?? toggleEditorPanelOpenedFromEditPost;
101
+
102
+ const {
103
+ pointer,
104
+ current,
105
+ setCurrent,
106
+ processImageGeneration,
107
+ handlePreviousImage,
108
+ handleNextImage,
109
+ currentImage,
110
+ currentPointer,
111
+ images,
112
+ imageStyles,
113
+ guessStyle,
114
+ } = useAiImage( {
115
+ autoStart: false,
116
+ cost: featuredImageCost,
117
+ type: 'featured-image-generation',
118
+ feature: FEATURED_IMAGE_FEATURE_NAME,
119
+ previousMediaId: postFeaturedMediaId,
120
+ } );
121
+
122
+ const handleModalClose = useCallback( () => {
123
+ setIsFeaturedImageModalVisible( false );
124
+ onClose?.();
125
+ }, [ onClose ] );
126
+
127
+ const handleModalOpen = useCallback( () => {
128
+ setIsFeaturedImageModalVisible( true );
129
+ }, [] );
130
+
131
+ /**
132
+ * Handle the guess style for the image. It is reworked here to include the post content.
133
+ */
134
+ const handleGuessStyle = useCallback(
135
+ userPrompt => {
136
+ const content = postTitle + '\n\n' + postContent;
137
+ return guessStyle( userPrompt, 'featured-image-guess-style', content );
138
+ },
139
+ [ postContent, postTitle, guessStyle ]
140
+ );
141
+
142
+ const handleGenerate = useCallback(
143
+ ( {
144
+ userPrompt,
145
+ style,
146
+ }: {
147
+ userPrompt?: string;
148
+ style?: string;
149
+ } ): Promise< void | ImageResponse > => {
150
+ // track the generate image event
151
+ recordEvent( 'jetpack_ai_featured_image_generation_generate_image', {
152
+ placement,
153
+ model: featuredImageActiveModel,
154
+ site_type: siteType,
155
+ style,
156
+ userPrompt,
157
+ } );
158
+
159
+ setIsFeaturedImageModalVisible( true );
160
+ return processImageGeneration( {
161
+ userPrompt,
162
+ postContent: postTitle + '\n\n' + postContent,
163
+ notEnoughRequests,
164
+ style,
165
+ } ).catch( error => {
166
+ recordEvent( 'jetpack_ai_featured_image_generation_error', {
167
+ placement,
168
+ error: error?.message,
169
+ model: featuredImageActiveModel,
170
+ site_type: siteType,
171
+ style,
172
+ } );
173
+ } );
174
+ },
175
+ [
176
+ recordEvent,
177
+ placement,
178
+ featuredImageActiveModel,
179
+ siteType,
180
+ processImageGeneration,
181
+ postContent,
182
+ notEnoughRequests,
183
+ postTitle,
184
+ ]
185
+ );
186
+
187
+ const handleFirstGenerate = useCallback( async () => {
188
+ currentPointer.generating = true;
189
+ const guessedStyle = await handleGuessStyle( '' );
190
+ setRequestStyle( guessedStyle );
191
+
192
+ const response = await handleGenerate( { userPrompt: '', style: guessedStyle } );
193
+ if ( response ) {
194
+ debug( 'handleFirstGenerate', response.revisedPrompt );
195
+ setPrompt( response.revisedPrompt || '' );
196
+ }
197
+ }, [ currentPointer, handleGenerate, handleGuessStyle ] );
198
+
199
+ const handleRegenerate = useCallback(
200
+ ( { userPrompt, style }: { userPrompt?: string; style?: string } ) => {
201
+ // track the regenerate image event
202
+ recordEvent( 'jetpack_ai_featured_image_generation_generate_another_image', {
203
+ placement,
204
+ model: featuredImageActiveModel,
205
+ site_type: siteType,
206
+ style: style,
207
+ } );
208
+
209
+ setCurrent( () => images.length );
210
+ processImageGeneration( {
211
+ userPrompt,
212
+ postContent: postTitle + '\n\n' + postContent,
213
+ notEnoughRequests,
214
+ style,
215
+ } ).catch( error => {
216
+ recordEvent( 'jetpack_ai_featured_image_generation_error', {
217
+ placement,
218
+ error: error?.message,
219
+ model: featuredImageActiveModel,
220
+ site_type: siteType,
221
+ style,
222
+ userPrompt,
223
+ } );
224
+ } );
225
+ },
226
+ [
227
+ recordEvent,
228
+ placement,
229
+ featuredImageActiveModel,
230
+ siteType,
231
+ setCurrent,
232
+ processImageGeneration,
233
+ postTitle,
234
+ postContent,
235
+ notEnoughRequests,
236
+ images,
237
+ ]
238
+ );
239
+
240
+ const handleTryAgain = useCallback(
241
+ ( { userPrompt, style }: { userPrompt?: string; style?: string } ) => {
242
+ // track the try again event
243
+ recordEvent( 'jetpack_ai_featured_image_generation_try_again', {
244
+ placement,
245
+ model: featuredImageActiveModel,
246
+ site_type: siteType,
247
+ style,
248
+ } );
249
+
250
+ processImageGeneration( {
251
+ userPrompt,
252
+ postContent: postTitle + '\n\n' + postContent,
253
+ notEnoughRequests,
254
+ style,
255
+ } ).catch( error => {
256
+ recordEvent( 'jetpack_ai_featured_image_generation_error', {
257
+ placement,
258
+ error: error?.message,
259
+ model: featuredImageActiveModel,
260
+ site_type: siteType,
261
+ style,
262
+ } );
263
+ } );
264
+ },
265
+ [
266
+ recordEvent,
267
+ placement,
268
+ featuredImageActiveModel,
269
+ siteType,
270
+ processImageGeneration,
271
+ postContent,
272
+ notEnoughRequests,
273
+ postTitle,
274
+ ]
275
+ );
276
+
277
+ const triggerComplementaryArea = useCallback( () => {
278
+ // clear any block selection, because selected blocks have precedence on settings sidebar
279
+ clearSelectedBlock();
280
+ return enableComplementaryArea( 'core/edit-post', 'edit-post/document' );
281
+ }, [ clearSelectedBlock, enableComplementaryArea ] );
282
+
283
+ const handleAccept = useCallback( () => {
284
+ // track the accept/use image event
285
+ recordEvent( 'jetpack_ai_featured_image_generation_use_image', {
286
+ placement,
287
+ model: featuredImageActiveModel,
288
+ site_type: siteType,
289
+ } );
290
+
291
+ const setAsFeaturedImage = image => {
292
+ editPost( { featured_media: image } );
293
+ handleModalClose();
294
+
295
+ // Open the featured image panel for users to see the new image.
296
+ setTimeout( () => {
297
+ const isFeaturedImagePanelOpened = isEditorPanelOpened( 'featured-image' );
298
+ const isPostStatusPanelOpened = isEditorPanelOpened( 'post-status' );
299
+
300
+ // open the complementary area and then trigger the featured image panel.
301
+ triggerComplementaryArea().then( () => {
302
+ if ( ! isFeaturedImagePanelOpened ) {
303
+ toggleEditorPanelOpened( 'featured-image' );
304
+ }
305
+ // handle the case where the featured image panel is not present
306
+ if ( ! isPostStatusPanelOpened ) {
307
+ toggleEditorPanelOpened( 'post-status' );
308
+ }
309
+ } );
310
+ }, 500 );
311
+ };
312
+
313
+ // If the image is already in the media library, use it directly, if it failed for some reason
314
+ // save it to the media library and then use it.
315
+ if ( currentImage?.libraryId ) {
316
+ setAsFeaturedImage( currentImage?.libraryId );
317
+ } else {
318
+ saveToMediaLibrary( currentImage?.image )
319
+ .then( image => {
320
+ setAsFeaturedImage( image?.id );
321
+ } )
322
+ .catch( error => {
323
+ recordEvent( 'jetpack_ai_featured_image_saving_error', {
324
+ placement,
325
+ error: error?.message,
326
+ model: featuredImageActiveModel,
327
+ site_type: siteType,
328
+ } );
329
+ } );
330
+ }
331
+ }, [
332
+ recordEvent,
333
+ placement,
334
+ featuredImageActiveModel,
335
+ siteType,
336
+ currentImage?.libraryId,
337
+ currentImage?.image,
338
+ editPost,
339
+ handleModalClose,
340
+ isEditorPanelOpened,
341
+ triggerComplementaryArea,
342
+ toggleEditorPanelOpened,
343
+ saveToMediaLibrary,
344
+ ] );
345
+
346
+ const generateAgainText = __( 'Generate another image', 'jetpack-ai-client' );
347
+ const generateText = __( 'Generate', 'jetpack-ai-client' );
348
+
349
+ const hasContent = postContent.trim?.() || postTitle.trim?.() ? true : false;
350
+ const hasPrompt = hasContent ? prompt.length >= 0 : prompt.length >= 3;
351
+ const disableInput = notEnoughRequests || currentPointer?.generating || requireUpgrade;
352
+ const disableAction = disableInput || ( ! hasContent && ! hasPrompt );
353
+
354
+ const upgradeDescription = notEnoughRequests
355
+ ? sprintf(
356
+ // Translators: %d is the cost of generating a featured image.
357
+ __(
358
+ "Featured image generation costs %d requests per image. You don't have enough requests to generate another image.",
359
+ 'jetpack-ai-client'
360
+ ),
361
+ featuredImageCost
362
+ )
363
+ : null;
364
+
365
+ const acceptButton = (
366
+ <Button
367
+ onClick={ handleAccept }
368
+ variant="primary"
369
+ disabled={
370
+ ! currentImage?.image ||
371
+ currentImage?.generating ||
372
+ currentImage?.libraryId === postFeaturedMediaId
373
+ }
374
+ >
375
+ { __( 'Set as featured image', 'jetpack-ai-client' ) }
376
+ </Button>
377
+ );
378
+
379
+ return (
380
+ <>
381
+ { ( placement === PLACEMENT_JETPACK_SIDEBAR ||
382
+ placement === PLACEMENT_DOCUMENT_SETTINGS ) && (
383
+ <>
384
+ <p className="jetpack-ai-assistant__help-text">
385
+ { __( 'Based on your post content.', 'jetpack-ai-client' ) }
386
+ </p>
387
+ <Button
388
+ onClick={ handleModalOpen }
389
+ isBusy={ busy }
390
+ disabled={ disabled || notEnoughRequests }
391
+ variant="secondary"
392
+ __next40pxDefaultSize
393
+ >
394
+ { __( 'Generate image', 'jetpack-ai-client' ) }
395
+ </Button>
396
+ </>
397
+ ) }
398
+ <AiImageModal
399
+ postContent={ hasContent }
400
+ autoStart={ hasContent && ! postFeaturedMediaId }
401
+ autoStartAction={ handleFirstGenerate }
402
+ images={ images }
403
+ currentIndex={ current }
404
+ title={ __( 'Generate a featured image with AI', 'jetpack-ai-client' ) }
405
+ cost={ featuredImageCost }
406
+ open={ isFeaturedImageModalVisible }
407
+ placement={ placement }
408
+ onClose={ handleModalClose }
409
+ onTryAgain={ handleTryAgain }
410
+ onGenerate={
411
+ pointer?.current > 0 || postFeaturedMediaId ? handleRegenerate : handleGenerate
412
+ }
413
+ generating={ currentPointer?.generating }
414
+ notEnoughRequests={ notEnoughRequests }
415
+ requireUpgrade={ requireUpgrade }
416
+ upgradeDescription={ upgradeDescription }
417
+ currentLimit={ requestsLimit }
418
+ currentUsage={ requestsCount }
419
+ isUnlimited={ isUnlimited }
420
+ hasError={ Boolean( currentPointer?.error ) }
421
+ handlePreviousImage={ handlePreviousImage }
422
+ handleNextImage={ handleNextImage }
423
+ acceptButton={ acceptButton }
424
+ generateButtonLabel={ pointer?.current > 0 ? generateAgainText : generateText }
425
+ instructionsPlaceholder={ __(
426
+ "Describe the featured image you'd like to create and select a style.",
427
+ 'jetpack-ai-client'
428
+ ) }
429
+ imageStyles={ imageStyles }
430
+ onGuessStyle={ handleGuessStyle }
431
+ prompt={ prompt }
432
+ setPrompt={ setPrompt }
433
+ initialStyle={ requestStyle }
434
+ inputDisabled={ disableInput }
435
+ actionDisabled={ disableAction }
436
+ />
437
+ </>
438
+ );
439
+ }