From 03963b736a3f67a87c5d7aab1bada9abbb701aeb Mon Sep 17 00:00:00 2001 From: jialin Date: Wed, 12 Nov 2025 20:46:53 +0800 Subject: [PATCH] chore: create, edit image handler --- src/components/auto-image/single-image.tsx | 26 ++-- src/pages/playground/apis/index.ts | 36 ++++++ src/pages/playground/components/thumb-img.tsx | 2 +- src/pages/playground/hooks/use-text-image.ts | 111 ++++-------------- src/utils/fetch-chunk-data.ts | 13 +- 5 files changed, 86 insertions(+), 102 deletions(-) diff --git a/src/components/auto-image/single-image.tsx b/src/components/auto-image/single-image.tsx index 3a1a0e3b..8f51024f 100644 --- a/src/components/auto-image/single-image.tsx +++ b/src/components/auto-image/single-image.tsx @@ -1,5 +1,5 @@ -import { CloseCircleOutlined } from '@ant-design/icons'; -import { Progress, ProgressProps } from 'antd'; +import { CloseCircleOutlined, LoadingOutlined } from '@ant-design/icons'; +import { Progress, ProgressProps, Spin } from 'antd'; import classNames from 'classnames'; import { round } from 'lodash'; import ResizeObserver from 'rc-resize-observer'; @@ -94,6 +94,17 @@ const SingleImage: React.FC = (props) => { const handleOnLoad = React.useCallback(async () => {}, []); + const renderProgress = () => { + {round(progress, 0)}%} + trailColor="var(--ant-color-fill-secondary)" + />; + }; + return (
= (props) => { overflow: 'hidden' }} > - ( - {round(progress, 0)}% - )} - trailColor="var(--ant-color-fill-secondary)" + } /> ) : ( diff --git a/src/pages/playground/apis/index.ts b/src/pages/playground/apis/index.ts index 4206db87..b5f64add 100644 --- a/src/pages/playground/apis/index.ts +++ b/src/pages/playground/apis/index.ts @@ -1,4 +1,5 @@ import { GPUSTACK_API_BASE_URL } from '@/config/settings'; +import { createFormData, errorHandler } from '@/utils/fetch-chunk-data'; import { request } from '@umijs/max'; export const OPENAI_COMPATIBLE = 'v1-openai'; @@ -94,6 +95,41 @@ export const createImages = async ( return res.json(); }; +// =========== edit image ============ +export const editImage = async (params: { + data?: any; + signal?: AbortSignal; +}) => { + const response = await fetch(EDIT_IMAGE_API, { + method: 'POST', + body: createFormData(params.data), + signal: params.signal + }); + if (!response.ok) { + return await errorHandler(response); + } + return response.json(); +}; + +export const createImage = async (params: { + data?: any; + signal?: AbortSignal; +}) => { + const response = await fetch(CREAT_IMAGE_API, { + method: 'POST', + body: JSON.stringify(params.data), + signal: params.signal, + headers: { + 'Content-Type': 'application/json' + } + }); + + if (!response.ok) { + return await errorHandler(response); + } + return response.json(); +}; + // ============ audio ============ export const textToSpeech = async (params: any, options?: any) => { const res = await fetch(AUDIO_TEXT_TO_SPEECH_API, { diff --git a/src/pages/playground/components/thumb-img.tsx b/src/pages/playground/components/thumb-img.tsx index 02e379e5..356037d1 100644 --- a/src/pages/playground/components/thumb-img.tsx +++ b/src/pages/playground/components/thumb-img.tsx @@ -213,4 +213,4 @@ const ThumbImg: React.FC<{ ); }; -export default React.memo(ThumbImg); +export default ThumbImg; diff --git a/src/pages/playground/hooks/use-text-image.ts b/src/pages/playground/hooks/use-text-image.ts index 0b3ef0f2..f9b769c4 100644 --- a/src/pages/playground/hooks/use-text-image.ts +++ b/src/pages/playground/hooks/use-text-image.ts @@ -1,13 +1,14 @@ import useOverlayScroller from '@/hooks/use-overlay-scroller'; import { extractErrorMessage, promptList } from '@/pages/playground/config'; -import { - fetchChunkedData, - fetchChunkedDataPostFormData -} from '@/utils/fetch-chunk-data'; import { useIntl } from '@umijs/max'; import _ from 'lodash'; import { useEffect, useRef, useState } from 'react'; -import { CREAT_IMAGE_API, EDIT_IMAGE_API } from '../apis'; +import { + CREAT_IMAGE_API, + EDIT_IMAGE_API, + createImage, + editImage +} from '../apis'; const ODD_STRING = 'AAAABJRU5ErkJgg==='; @@ -126,21 +127,21 @@ export default function useTextImage(props: any) { let result: any = {}; if (API === CREAT_IMAGE_API) { - result = await fetchChunkedData({ + result = await createImage({ data: parameters, - url: `${API}?t=${Date.now()}`, signal: requestToken.current.signal }); } if (API === EDIT_IMAGE_API) { - result = await fetchChunkedDataPostFormData({ + result = await editImage({ data: parameters, - url: `${API}?t=${Date.now()}`, signal: requestToken.current.signal }); } + console.log('result:', result); + if (result.error) { setTokenResult({ error: true, @@ -149,18 +150,19 @@ export default function useTextImage(props: any) { setImageList([]); return; } - console.log('result:', result); + + // If the request ID has changed, ignore this chunk if (requestIdRef.current !== currentRequestId) { - // If the request ID has changed, ignore this chunk - // return; + return; } - // if (result?.error) { - // setTokenResult({ - // error: true, - // errorMessage: extractErrorMessage(result) - // }); - // return; - // } + if (result?.error) { + setTokenResult({ + error: true, + errorMessage: extractErrorMessage(result) + }); + return; + } + result?.data?.forEach((item: any, index: number) => { const imgItem = newImageList[index]; @@ -168,8 +170,6 @@ export default function useTextImage(props: any) { imgItem.dataUrl = `data:image/png;base64,${item.b64_json}`; } - const progress = 100; - newImageList[index] = { dataUrl: imgItem.dataUrl, height: imgSize[1], @@ -178,76 +178,13 @@ export default function useTextImage(props: any) { maxWidth: `${imgSize[0]}px`, uid: imgItem.uid, span: imgItem.span, - loading: _.get(parameters, chunkFields) ? progress < 100 : false, - preview: API === CREAT_IMAGE_API ? progress >= 100 : false, - progress: progress + loading: false, + preview: true, + progress: 100 }; }); console.log('newImageList:', newImageList); - // if ( - // done && - // !chunk?.error && - // newImageList.some((item) => item.progress < 100) - // ) { - // setTokenResult({ - // error: true, - // errorMessage: intl.formatMessage({ - // id: 'playground.image.generate.error' - // }) - // }); - // } setImageList([...newImageList]); - // const { reader, decoder } = result; - // streamReaderRef.current = reader; - // await readStreamData(reader, decoder, (chunk: any, done?: boolean) => { - // console.log('chunk done:', chunk, done); - // if (requestIdRef.current !== currentRequestId) { - // // If the request ID has changed, ignore this chunk - // return; - // } - // if (chunk?.error) { - // setTokenResult({ - // error: true, - // errorMessage: extractErrorMessage(chunk) - // }); - // return; - // } - // chunk?.data?.forEach((item: any) => { - // const imgItem = newImageList[item.index]; - // if (item.b64_json && _.get(parameters, chunkFields)) { - // imgItem.dataUrl += removeBase64Suffix(item.b64_json, ODD_STRING); - // } else if (item.b64_json) { - // imgItem.dataUrl = `data:image/png;base64,${removeBase64Suffix(item.b64_json, ODD_STRING)}`; - // } - // const progress = item.progress; - - // newImageList[item.index] = { - // dataUrl: imgItem.dataUrl, - // height: imgSize[1], - // width: imgSize[0], - // maxHeight: `${imgSize[1]}px`, - // maxWidth: `${imgSize[0]}px`, - // uid: imgItem.uid, - // span: imgItem.span, - // loading: _.get(parameters, chunkFields) ? progress < 100 : false, - // preview: API === CREAT_IMAGE_API ? progress >= 100 : false, - // progress: progress - // }; - // }); - // if ( - // done && - // !chunk?.error && - // newImageList.some((item) => item.progress < 100) - // ) { - // setTokenResult({ - // error: true, - // errorMessage: intl.formatMessage({ - // id: 'playground.image.generate.error' - // }) - // }); - // } - // setImageList([...newImageList]); - // }); } catch (error) { console.log('error:', error); updateRequestId(); diff --git a/src/utils/fetch-chunk-data.ts b/src/utils/fetch-chunk-data.ts index 422c5550..5694d99a 100644 --- a/src/utils/fetch-chunk-data.ts +++ b/src/utils/fetch-chunk-data.ts @@ -32,7 +32,7 @@ const extractJSON = ( return { results, remaining }; }; -const errorHandler = async (res: any) => { +export const errorHandler = async (res: any) => { try { const data = await res.json(); return { @@ -78,10 +78,17 @@ export const fetchChunkedData = async (params: { if (!response.ok) { return await errorHandler(response); } - return response.json(); + const reader = response?.body?.getReader(); + const decoder = new TextDecoder('utf-8', { + fatal: true + }); + return { + reader, + decoder + }; }; -const createFormData = (data: any): FormData => { +export const createFormData = (data: any): FormData => { const formData = new FormData(); const appendToFormData = (key: string, value: any) => {