diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c805cc..0a12cbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.10.1-alpha] - unreleased +## [0.11.0] - 2024-04-01 +### Added +- AI Client: include prompt to generate featured image based on post content. [#36591] +- Support different responses in image hook [#36626] -This is an alpha version! The changes listed here are not final. +### Fixed +- AI Client: fix a bug where quick prompts would not work after getting suggested content [#36651] +- AI Client: set request content type as JSON on image generation hook and use rectangular images instead of square images. [#36620] +## [0.10.1] - 2024-03-27 ### Changed -- Updated package dependencies. +- Updated package dependencies. [#36539, #36585] ## [0.10.0] - 2024-03-18 ### Added @@ -266,7 +272,8 @@ This is an alpha version! The changes listed here are not final. - Updated package dependencies. [#31659] - Updated package dependencies. [#31785] -[0.10.1-alpha]: https://github.com/Automattic/jetpack-ai-client/compare/v0.10.0...v0.10.1-alpha +[0.11.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.10.1...v0.11.0 +[0.10.1]: https://github.com/Automattic/jetpack-ai-client/compare/v0.10.0...v0.10.1 [0.10.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.9.0...v0.10.0 [0.9.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.2...v0.9.0 [0.8.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.8.1...v0.8.2 diff --git a/build/components/ai-control/index.js b/build/components/ai-control/index.js index 5df6fa6..d1899f3 100644 --- a/build/components/ai-control/index.js +++ b/build/components/ai-control/index.js @@ -9,8 +9,8 @@ import { useImperativeHandle, useRef, useEffect, useCallback } from '@wordpress/ import { __ } from '@wordpress/i18n'; import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons'; import classNames from 'classnames'; -import { forwardRef } from 'react'; -import React from 'react'; +import debugFactory from 'debug'; +import React, { forwardRef } from 'react'; /** * Internal dependencies */ @@ -19,6 +19,7 @@ import AiStatusIndicator from '../ai-status-indicator/index.js'; import { GuidelineMessage } from './message.js'; // eslint-disable-next-line @typescript-eslint/no-empty-function const noop = () => { }; +const debug = debugFactory('jetpack-ai-client:ai-control'); /** * AI Control component. * @@ -35,10 +36,7 @@ export function AIControl({ disabled = false, value = '', placeholder = '', show if (editRequest) { promptUserInputRef?.current?.focus(); } - if (!editRequest && lastValue !== null && value !== lastValue) { - onChange?.(lastValue); - } - }, [editRequest, lastValue, value]); + }, [editRequest]); const sendRequest = useCallback(() => { setLastValue(value); setEditRequest(false); @@ -62,6 +60,7 @@ export function AIControl({ disabled = false, value = '', placeholder = '', show onDiscard?.(); }, []); const cancelEdit = useCallback(() => { + debug('cancelEdit, revert to last value', lastValue); onChange(lastValue || ''); setEditRequest(false); }, [lastValue]); diff --git a/build/hooks/use-image-generator/index.d.ts b/build/hooks/use-image-generator/index.d.ts index 33643cc..90d9474 100644 --- a/build/hooks/use-image-generator/index.d.ts +++ b/build/hooks/use-image-generator/index.d.ts @@ -1,10 +1,12 @@ declare const useImageGenerator: () => { - generateImage: ({ feature, }: { + generateImage: ({ feature, postContent, responseFormat, }: { feature: string; + postContent: string; + responseFormat?: 'url' | 'b64_json'; }) => Promise<{ - data: Array<{ - url: string; - }>; + data: { + [key: string]: string; + }[]; }>; }; export default useImageGenerator; diff --git a/build/hooks/use-image-generator/index.js b/build/hooks/use-image-generator/index.js index d842630..8127845 100644 --- a/build/hooks/use-image-generator/index.js +++ b/build/hooks/use-image-generator/index.js @@ -8,7 +8,7 @@ import debugFactory from 'debug'; import requestJwt from '../../jwt/index.js'; const debug = debugFactory('ai-client:use-image-generator'); const useImageGenerator = () => { - const generateImage = async function ({ feature, }) { + const generateImage = async function ({ feature, postContent, responseFormat = 'url', }) { let token = ''; try { token = (await requestJwt()).token; @@ -19,16 +19,30 @@ const useImageGenerator = () => { } try { debug('Generating image'); - // TODO: Find a proper prompt for the image generation - const imageGenerationPrompt = ``; + // TODO: fine tune the prompt as we move forward + const imageGenerationPrompt = `I need a cover image for a blog post. +Before creating the image, identify the main topic of the content and only represent it. +Do not represent the whole content in one image, keep it simple and just represent one single idea. +Do not add details, detailed explanations or highlights from the content, just represent the main idea as if it was a photograph. +Do not use collages or compositions with multiple elements or scenes. Stick to one single scene. Do not compose unrealistic scenes. +If the content describes facts, objects or concepts from the real world, represent them on a realistic style and do not make unreal compositions. +If the content is more abstract, use a more abstract style to represent the main idea. +Make sure the light and the style are visually appealing. +Do not add text to the image. + +This is the post content: + +` + postContent; const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-image'; const body = { prompt: imageGenerationPrompt, - response_format: 'url', + response_format: responseFormat, feature, + size: '1792x1024', }; const headers = { Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', }; const data = await fetch(URL, { method: 'POST', diff --git a/package.json b/package.json index 8028453..74b3e44 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@automattic/jetpack-ai-client", - "version": "0.10.1-alpha", + "version": "0.11.0", "description": "A JS client for consuming Jetpack AI services", "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme", "bugs": { @@ -18,7 +18,8 @@ "build": "pnpm run clean && pnpm run compile-ts", "clean": "rm -rf build/", "compile-ts": "tsc --pretty", - "test": "NODE_OPTIONS=--experimental-vm-modules jest" + "test": "NODE_OPTIONS=--experimental-vm-modules jest", + "watch": "tsc --watch --pretty" }, "type": "module", "devDependencies": { @@ -38,9 +39,9 @@ "main": "./build/index.js", "types": "./build/index.d.ts", "dependencies": { - "@automattic/jetpack-base-styles": "^0.6.20-alpha", - "@automattic/jetpack-connection": "^0.33.5-alpha", - "@automattic/jetpack-shared-extension-utils": "^0.14.8-alpha", + "@automattic/jetpack-base-styles": "^0.6.20", + "@automattic/jetpack-connection": "^0.33.5", + "@automattic/jetpack-shared-extension-utils": "^0.14.8", "@microsoft/fetch-event-source": "2.0.1", "@types/react": "18.2.61", "@wordpress/api-fetch": "6.51.0", diff --git a/src/components/ai-control/index.tsx b/src/components/ai-control/index.tsx index 739bf50..0f99c97 100644 --- a/src/components/ai-control/index.tsx +++ b/src/components/ai-control/index.tsx @@ -8,8 +8,8 @@ import { useImperativeHandle, useRef, useEffect, useCallback } from '@wordpress/ import { __ } from '@wordpress/i18n'; import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons'; import classNames from 'classnames'; -import { forwardRef } from 'react'; -import React from 'react'; +import debugFactory from 'debug'; +import React, { forwardRef } from 'react'; /** * Internal dependencies */ @@ -45,6 +45,8 @@ type AiControlProps = { // eslint-disable-next-line @typescript-eslint/no-empty-function const noop = () => {}; +const debug = debugFactory( 'jetpack-ai-client:ai-control' ); + /** * AI Control component. * @@ -84,11 +86,7 @@ export function AIControl( if ( editRequest ) { promptUserInputRef?.current?.focus(); } - - if ( ! editRequest && lastValue !== null && value !== lastValue ) { - onChange?.( lastValue ); - } - }, [ editRequest, lastValue, value ] ); + }, [ editRequest ] ); const sendRequest = useCallback( () => { setLastValue( value ); @@ -119,6 +117,7 @@ export function AIControl( }, [] ); const cancelEdit = useCallback( () => { + debug( 'cancelEdit, revert to last value', lastValue ); onChange( lastValue || '' ); setEditRequest( false ); }, [ lastValue ] ); diff --git a/src/hooks/use-image-generator/index.ts b/src/hooks/use-image-generator/index.ts index 6b8dbd8..7de4eb9 100644 --- a/src/hooks/use-image-generator/index.ts +++ b/src/hooks/use-image-generator/index.ts @@ -12,9 +12,13 @@ const debug = debugFactory( 'ai-client:use-image-generator' ); const useImageGenerator = () => { const generateImage = async function ( { feature, + postContent, + responseFormat = 'url', }: { feature: string; - } ): Promise< { data: Array< { url: string } > } > { + postContent: string; + responseFormat?: 'url' | 'b64_json'; + } ): Promise< { data: Array< { [ key: string ]: string } > } > { let token = ''; try { @@ -27,19 +31,34 @@ const useImageGenerator = () => { try { debug( 'Generating image' ); - // TODO: Find a proper prompt for the image generation - const imageGenerationPrompt = ``; + // TODO: fine tune the prompt as we move forward + const imageGenerationPrompt = + `I need a cover image for a blog post. +Before creating the image, identify the main topic of the content and only represent it. +Do not represent the whole content in one image, keep it simple and just represent one single idea. +Do not add details, detailed explanations or highlights from the content, just represent the main idea as if it was a photograph. +Do not use collages or compositions with multiple elements or scenes. Stick to one single scene. Do not compose unrealistic scenes. +If the content describes facts, objects or concepts from the real world, represent them on a realistic style and do not make unreal compositions. +If the content is more abstract, use a more abstract style to represent the main idea. +Make sure the light and the style are visually appealing. +Do not add text to the image. + +This is the post content: + +` + postContent; const URL = 'https://public-api.wordpress.com/wpcom/v2/jetpack-ai-image'; const body = { prompt: imageGenerationPrompt, - response_format: 'url', + response_format: responseFormat, feature, + size: '1792x1024', }; const headers = { Authorization: `Bearer ${ token }`, + 'Content-Type': 'application/json', }; const data = await fetch( URL, { @@ -48,7 +67,7 @@ const useImageGenerator = () => { body: JSON.stringify( body ), } ).then( response => response.json() ); - return data as { data: { url: string }[] }; + return data as { data: { [ key: string ]: string }[] }; } catch ( error ) { return; }