diff --git a/src/photo/ai.ts b/src/photo/ai/index.ts similarity index 91% rename from src/photo/ai.ts rename to src/photo/ai/index.ts index 6e074de3..b6fa7d07 100644 --- a/src/photo/ai.ts +++ b/src/photo/ai/index.ts @@ -3,7 +3,7 @@ import { streamOpenAiImageQuery } from '@/services/openai'; export type ImageQuery = 'title' | 'caption' | 'tags' | 'description'; export const IMAGE_QUERIES: Record = { - title: 'What is the title of this image?', + title: 'Provide a short title for this image', caption: 'What is the caption of this image?', tags: 'Describe this image three or less comma-separated keywords', description: 'Describe this image in detail', diff --git a/src/photo/ai/useImageQuery.ts b/src/photo/ai/useImageQuery.ts new file mode 100644 index 00000000..2893f930 --- /dev/null +++ b/src/photo/ai/useImageQuery.ts @@ -0,0 +1,32 @@ +import { useCallback, useState } from 'react'; +import { streamImageQueryAction } from '../actions'; +import { readStreamableValue } from 'ai/rsc'; +import { ImageQuery } from '.'; + +export default function useImageQuery( + imageBase64: string | undefined, + query: ImageQuery, +) { + const [text, setText] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const request = useCallback(async () => { + if (imageBase64) { + setIsLoading(true); + const textStream = await streamImageQueryAction( + imageBase64 ?? '', + query, + ); + for await (const text of readStreamableValue(textStream)) { + setText(text ?? ''); + } + setIsLoading(false); + } + }, [imageBase64, query]); + + return [ + request, + text, + isLoading, + ] as const; +}; diff --git a/src/photo/form/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx index 321ab467..24bf6b20 100644 --- a/src/photo/form/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -31,6 +31,7 @@ import { Tags, sortTagsObjectWithoutFavs } from '@/tag'; import { formatCount, formatCountDescriptive } from '@/utility/string'; import { readStreamableValue } from 'ai/rsc'; import Spinner from '@/components/Spinner'; +import useImageQuery from '../ai/useImageQuery'; const THUMBNAIL_SIZE = 300; @@ -122,24 +123,72 @@ export default function PhotoForm({ } }, []); - const [aiTags, setAiTags] = useState(''); - const [isLoadingAi, setIsLoadingAi] = useState(false); + const [ + requestTitle, + title, + isLoadingTitle, + ] = useImageQuery(imageData, 'title'); + + const [aiTags, setAiTags] = useState({ + two: '', + three: '', + }); + const [isLoadingAi, setIsLoadingAi] = useState({ + two: false, + three: false, + }); return (
- +
+ + + +
}

- AI RESPONSE: {aiTags} {isLoadingAi && <> + AI RESPONSE 01: {title} {isLoadingTitle && <> + + + + } +

+

+ AI RESPONSE 02: {aiTags.two} {isLoadingAi.two && <> + + + + } +

+

+ AI RESPONSE 01: {aiTags.three} {isLoadingAi.three && <>