refactor(api-server): narrow puzzle state surface
This commit is contained in:
@@ -14,6 +14,7 @@ export type CreativeImageInputReferenceImage = {
|
||||
id: string;
|
||||
label: string;
|
||||
imageSrc: string;
|
||||
assetObjectId?: string | null;
|
||||
};
|
||||
|
||||
export type CreativeImageInputPanelLabels = {
|
||||
|
||||
@@ -1956,6 +1956,8 @@ function buildPuzzleCompileActionFromFormPayload(
|
||||
...(pictureDescription ? { pictureDescription } : {}),
|
||||
referenceImageSrc: payload?.referenceImageSrc || null,
|
||||
referenceImageSrcs: payload?.referenceImageSrcs ?? [],
|
||||
referenceImageAssetObjectId: payload?.referenceImageAssetObjectId ?? null,
|
||||
referenceImageAssetObjectIds: payload?.referenceImageAssetObjectIds ?? [],
|
||||
imageModel: payload?.imageModel ?? null,
|
||||
aiRedraw: payload?.aiRedraw ?? true,
|
||||
candidateCount: 1,
|
||||
@@ -1978,6 +1980,8 @@ function buildPuzzleFormPayloadFromSession(
|
||||
pictureDescription,
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: null,
|
||||
aiRedraw: true,
|
||||
};
|
||||
@@ -2008,6 +2012,8 @@ function buildPuzzleFormPayloadFromAction(
|
||||
? (payload.referenceImageSrc ?? null)
|
||||
: (payload.referenceImageSrc ?? null),
|
||||
referenceImageSrcs: payload.referenceImageSrcs ?? [],
|
||||
referenceImageAssetObjectId: payload.referenceImageAssetObjectId ?? null,
|
||||
referenceImageAssetObjectIds: payload.referenceImageAssetObjectIds ?? [],
|
||||
imageModel:
|
||||
payload.action === 'compile_puzzle_draft'
|
||||
? (payload.imageModel ?? null)
|
||||
@@ -5542,6 +5548,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
pictureDescription: payload.pictureDescription ?? '',
|
||||
referenceImageSrc: payload.referenceImageSrc ?? null,
|
||||
referenceImageSrcs: payload.referenceImageSrcs ?? [],
|
||||
referenceImageAssetObjectId:
|
||||
payload.referenceImageAssetObjectId ?? null,
|
||||
referenceImageAssetObjectIds:
|
||||
payload.referenceImageAssetObjectIds ?? [],
|
||||
imageModel: payload.imageModel ?? null,
|
||||
aiRedraw: payload.aiRedraw ?? true,
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ vi.mock('../ResolvedAssetImage', () => ({
|
||||
vi.mock('../../services/puzzle-works/puzzleAssetClient', () => ({
|
||||
puzzleAssetClient: {
|
||||
listHistoryAssets: vi.fn(),
|
||||
uploadReferenceImage: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -90,6 +91,19 @@ beforeEach(() => {
|
||||
if (!Element.prototype.scrollIntoView) {
|
||||
Element.prototype.scrollIntoView = () => {};
|
||||
}
|
||||
vi.mocked(puzzleAssetClient.uploadReferenceImage).mockImplementation(
|
||||
async ({ file }) => ({
|
||||
assetObjectId: `asset-reference-${file.name}`,
|
||||
assetKind: 'puzzle_cover_image',
|
||||
objectKey: `generated-puzzle-assets/reference/${file.name}`,
|
||||
imageSrc: `/generated-puzzle-assets/reference/${file.name}`,
|
||||
ownerUserId: 'user-1',
|
||||
profileId: null,
|
||||
entityId: null,
|
||||
createdAt: '1713686400.000000Z',
|
||||
updatedAt: '1713686400.000000Z',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -190,6 +204,8 @@ test('puzzle workspace submits the work form instead of agent chat', () => {
|
||||
pictureDescription: '一只猫在雨夜灯牌下回头。',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: true,
|
||||
});
|
||||
@@ -325,8 +341,10 @@ test('puzzle workspace selects a history image from the upload card', async () =
|
||||
expect(onCreateFromForm).toHaveBeenCalledWith({
|
||||
seedText: '保留历史图里的主体,改成晴天花园。',
|
||||
pictureDescription: '保留历史图里的主体,改成晴天花园。',
|
||||
referenceImageSrc: '/generated-puzzle-assets/history/image.png',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: 'asset-history-1',
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: true,
|
||||
});
|
||||
@@ -384,6 +402,8 @@ test('puzzle workspace falls back to compile action for restored sessions', () =
|
||||
promptText: '潮雾中的灯塔与断桥',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: true,
|
||||
candidateCount: 1,
|
||||
@@ -484,6 +504,8 @@ test('puzzle workspace restores form draft fields and autosaves edits', () => {
|
||||
pictureDescription: '旧街灯牌下的猫和发光雨伞。',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: true,
|
||||
});
|
||||
@@ -528,8 +550,10 @@ test('puzzle workspace hides prompt and cost when AI redraw is off', async () =>
|
||||
expect(onCreateFromForm).toHaveBeenCalledWith({
|
||||
seedText: 'first-level.png',
|
||||
pictureDescription: 'first-level.png',
|
||||
referenceImageSrc: uploadedDataUrl,
|
||||
referenceImageSrc: '/generated-puzzle-assets/reference/first-level.png',
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: 'asset-reference-first-level.png',
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: false,
|
||||
});
|
||||
@@ -584,6 +608,8 @@ test('puzzle workspace submits history image when AI redraw is off', async () =>
|
||||
pictureDescription: '历史素材 · image.png',
|
||||
referenceImageSrc: '/generated-puzzle-assets/history/image.png',
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: 'asset-history-1',
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: false,
|
||||
});
|
||||
@@ -593,6 +619,17 @@ test('puzzle workspace submits uploaded reference image when AI redraw is on', a
|
||||
const onCreateFromForm = vi.fn();
|
||||
const uploadedDataUrl = 'data:image/png;base64,uploaded-square';
|
||||
stubReferenceImageUpload(uploadedDataUrl);
|
||||
vi.mocked(puzzleAssetClient.uploadReferenceImage).mockResolvedValue({
|
||||
assetObjectId: 'asset-reference-main-1',
|
||||
assetKind: 'puzzle_cover_image',
|
||||
objectKey: 'generated-puzzle-assets/reference/main-1.png',
|
||||
imageSrc: '/generated-puzzle-assets/reference/main-1.png',
|
||||
ownerUserId: 'user-1',
|
||||
profileId: null,
|
||||
entityId: null,
|
||||
createdAt: '1713686400.000000Z',
|
||||
updatedAt: '1713686400.000000Z',
|
||||
});
|
||||
|
||||
render(
|
||||
<PuzzleAgentWorkspace
|
||||
@@ -612,6 +649,9 @@ test('puzzle workspace submits uploaded reference image when AI redraw is on', a
|
||||
await waitFor(() => {
|
||||
expect(screen.getByAltText('拼图图片')).toBeTruthy();
|
||||
});
|
||||
expect(puzzleAssetClient.uploadReferenceImage).toHaveBeenCalledWith({
|
||||
file: expect.any(File),
|
||||
});
|
||||
fireEvent.change(screen.getByLabelText('画面AI重绘要求(提示词)'), {
|
||||
target: { value: '保留上传画面的主体和构图,改成雨夜灯街。' },
|
||||
});
|
||||
@@ -621,8 +661,101 @@ test('puzzle workspace submits uploaded reference image when AI redraw is on', a
|
||||
expect(onCreateFromForm).toHaveBeenCalledWith({
|
||||
seedText: '保留上传画面的主体和构图,改成雨夜灯街。',
|
||||
pictureDescription: '保留上传画面的主体和构图,改成雨夜灯街。',
|
||||
referenceImageSrc: uploadedDataUrl,
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: 'asset-reference-main-1',
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('puzzle workspace uploads prompt references as asset object ids', async () => {
|
||||
const onCreateFromForm = vi.fn();
|
||||
const uploadedSources = [
|
||||
'data:image/png;base64,reference-1',
|
||||
'data:image/png;base64,reference-2',
|
||||
];
|
||||
let readIndex = 0;
|
||||
stubReferenceImageUpload(uploadedSources[0] ?? 'data:image/png;base64,reference-1');
|
||||
class MockFileReader {
|
||||
result: string | null = null;
|
||||
onload: null | (() => void) = null;
|
||||
onerror: null | (() => void) = null;
|
||||
|
||||
readAsDataURL() {
|
||||
this.result = uploadedSources[readIndex] ?? uploadedSources[0] ?? '';
|
||||
readIndex += 1;
|
||||
this.onload?.();
|
||||
}
|
||||
}
|
||||
vi.stubGlobal('FileReader', MockFileReader as unknown as typeof FileReader);
|
||||
vi.mocked(puzzleAssetClient.uploadReferenceImage)
|
||||
.mockResolvedValueOnce({
|
||||
assetObjectId: 'asset-reference-prompt-1',
|
||||
assetKind: 'puzzle_cover_image',
|
||||
objectKey: 'generated-puzzle-assets/reference/prompt-1.png',
|
||||
imageSrc: '/generated-puzzle-assets/reference/prompt-1.png',
|
||||
ownerUserId: 'user-1',
|
||||
profileId: null,
|
||||
entityId: null,
|
||||
createdAt: '1713686400.000000Z',
|
||||
updatedAt: '1713686400.000000Z',
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
assetObjectId: 'asset-reference-prompt-2',
|
||||
assetKind: 'puzzle_cover_image',
|
||||
objectKey: 'generated-puzzle-assets/reference/prompt-2.png',
|
||||
imageSrc: '/generated-puzzle-assets/reference/prompt-2.png',
|
||||
ownerUserId: 'user-1',
|
||||
profileId: null,
|
||||
entityId: null,
|
||||
createdAt: '1713686400.000000Z',
|
||||
updatedAt: '1713686400.000000Z',
|
||||
});
|
||||
|
||||
render(
|
||||
<PuzzleAgentWorkspace
|
||||
session={null}
|
||||
onBack={() => {}}
|
||||
onSubmitMessage={() => {}}
|
||||
onExecuteAction={() => {}}
|
||||
onCreateFromForm={onCreateFromForm}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.change(screen.getByLabelText('画面描述'), {
|
||||
target: { value: '一只猫在雨夜灯牌下回头。' },
|
||||
});
|
||||
fireEvent.change(screen.getByLabelText('上传参考图', { selector: 'input' }), {
|
||||
target: {
|
||||
files: uploadedSources.map(
|
||||
(_source, index) =>
|
||||
new File(['x'], `reference-${index + 1}.png`, {
|
||||
type: 'image/png',
|
||||
}),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByRole('button', { name: /预览参考图/u })).toHaveLength(
|
||||
2,
|
||||
);
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: /生成拼图游戏草稿/u }));
|
||||
confirmPuzzlePointCost();
|
||||
|
||||
expect(onCreateFromForm).toHaveBeenCalledWith({
|
||||
seedText: '一只猫在雨夜灯牌下回头。',
|
||||
pictureDescription: '一只猫在雨夜灯牌下回头。',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [
|
||||
'asset-reference-prompt-1',
|
||||
'asset-reference-prompt-2',
|
||||
],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: true,
|
||||
});
|
||||
@@ -705,7 +838,15 @@ test('puzzle workspace uploads prompt reference images from the description box'
|
||||
seedText: '一只猫在雨夜灯牌下回头。',
|
||||
pictureDescription: '一只猫在雨夜灯牌下回头。',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: uploadedSources.slice(0, 5),
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [
|
||||
'asset-reference-reference-1.png',
|
||||
'asset-reference-reference-2.png',
|
||||
'asset-reference-reference-3.png',
|
||||
'asset-reference-reference-4.png',
|
||||
'asset-reference-reference-5.png',
|
||||
],
|
||||
imageModel: 'gpt-image-2',
|
||||
aiRedraw: true,
|
||||
});
|
||||
|
||||
@@ -16,9 +16,11 @@ import { getPuzzleHistoryAssetReferenceLabel } from '../../services/puzzle-works
|
||||
import {
|
||||
cropPuzzleReferenceImageDataUrl,
|
||||
isPuzzleReferenceImageSquare,
|
||||
puzzleReferenceImageDataUrlToFile,
|
||||
readPuzzleReferenceImageAsDataUrl,
|
||||
readPuzzleReferenceImageForUpload,
|
||||
} from '../../services/puzzleReferenceImage';
|
||||
import { puzzleAssetClient } from '../../services/puzzle-works/puzzleAssetClient';
|
||||
import {
|
||||
CreativeImageInputPanel,
|
||||
type CreativeImageInputReferenceImage,
|
||||
@@ -54,6 +56,7 @@ type PuzzleAgentWorkspaceProps = {
|
||||
type PuzzleFormState = {
|
||||
pictureDescription: string;
|
||||
referenceImageSrc: string;
|
||||
referenceImageAssetObjectId: string;
|
||||
referenceImageLabel: string;
|
||||
referenceImageSrcs: CreativeImageInputReferenceImage[];
|
||||
imageModel: PuzzleImageModelId;
|
||||
@@ -63,6 +66,7 @@ type PuzzleFormState = {
|
||||
const EMPTY_FORM_STATE: PuzzleFormState = {
|
||||
pictureDescription: '',
|
||||
referenceImageSrc: '',
|
||||
referenceImageAssetObjectId: '',
|
||||
referenceImageLabel: '',
|
||||
referenceImageSrcs: [],
|
||||
imageModel: PUZZLE_IMAGE_MODEL_GPT_IMAGE_2,
|
||||
@@ -74,6 +78,7 @@ const PUZZLE_PROMPT_REFERENCE_IMAGE_LIMIT = 5;
|
||||
type PuzzleImageCropState = {
|
||||
source: string;
|
||||
label: string;
|
||||
fileName: string;
|
||||
imageSize: { width: number; height: number };
|
||||
cropRect: SquareImageCropRect;
|
||||
error: string | null;
|
||||
@@ -97,11 +102,14 @@ function resolveInitialFormState(
|
||||
return {
|
||||
pictureDescription: formDraft.pictureDescription ?? '',
|
||||
referenceImageSrc: initialFormPayload?.referenceImageSrc ?? '',
|
||||
referenceImageAssetObjectId:
|
||||
initialFormPayload?.referenceImageAssetObjectId ?? '',
|
||||
referenceImageLabel: initialFormPayload?.referenceImageSrc
|
||||
? '已选择拼图图片'
|
||||
: '',
|
||||
referenceImageSrcs: createPuzzlePromptReferenceImagesFromSources(
|
||||
initialFormPayload?.referenceImageSrcs,
|
||||
initialFormPayload?.referenceImageAssetObjectIds,
|
||||
),
|
||||
imageModel: normalizePuzzleImageModel(initialFormPayload?.imageModel),
|
||||
aiRedraw: initialFormPayload?.aiRedraw ?? true,
|
||||
@@ -115,11 +123,14 @@ function resolveInitialFormState(
|
||||
initialFormPayload.seedText ??
|
||||
'',
|
||||
referenceImageSrc: initialFormPayload.referenceImageSrc ?? '',
|
||||
referenceImageAssetObjectId:
|
||||
initialFormPayload.referenceImageAssetObjectId ?? '',
|
||||
referenceImageLabel: initialFormPayload.referenceImageSrc
|
||||
? '已选择拼图图片'
|
||||
: '',
|
||||
referenceImageSrcs: createPuzzlePromptReferenceImagesFromSources(
|
||||
initialFormPayload.referenceImageSrcs,
|
||||
initialFormPayload.referenceImageAssetObjectIds,
|
||||
),
|
||||
imageModel: normalizePuzzleImageModel(initialFormPayload.imageModel),
|
||||
aiRedraw: initialFormPayload.aiRedraw ?? true,
|
||||
@@ -138,6 +149,7 @@ function resolveInitialFormState(
|
||||
session.seedText ||
|
||||
'',
|
||||
referenceImageSrc: '',
|
||||
referenceImageAssetObjectId: '',
|
||||
referenceImageLabel: '',
|
||||
referenceImageSrcs: [],
|
||||
imageModel: PUZZLE_IMAGE_MODEL_GPT_IMAGE_2,
|
||||
@@ -166,14 +178,46 @@ function normalizePuzzlePromptReferenceSources(
|
||||
|
||||
function createPuzzlePromptReferenceImagesFromSources(
|
||||
sources: readonly string[] | null | undefined,
|
||||
assetObjectIds: readonly string[] | null | undefined = [],
|
||||
): CreativeImageInputReferenceImage[] {
|
||||
return normalizePuzzlePromptReferenceSources(sources).map(
|
||||
const assetIds = normalizePuzzleAssetObjectIds(assetObjectIds);
|
||||
const sourceImages = normalizePuzzlePromptReferenceSources(sources).map(
|
||||
(imageSrc, index) => ({
|
||||
id: `restored:${index}:${imageSrc}`,
|
||||
label: `参考图 ${index + 1}`,
|
||||
imageSrc,
|
||||
assetObjectId: assetIds[index] ?? null,
|
||||
}),
|
||||
);
|
||||
if (sourceImages.length > 0) {
|
||||
return sourceImages;
|
||||
}
|
||||
|
||||
return assetIds.map((assetObjectId, index) => ({
|
||||
id: `restored-asset:${index}:${assetObjectId}`,
|
||||
label: `参考图 ${index + 1}`,
|
||||
imageSrc: '',
|
||||
assetObjectId,
|
||||
}));
|
||||
}
|
||||
|
||||
function normalizePuzzleAssetObjectIds(
|
||||
assetObjectIds: readonly (string | null | undefined)[] | null | undefined,
|
||||
) {
|
||||
const normalizedIds: string[] = [];
|
||||
for (const assetObjectId of assetObjectIds ?? []) {
|
||||
const normalized = assetObjectId?.trim() ?? '';
|
||||
if (
|
||||
normalized &&
|
||||
!normalizedIds.some((current) => current === normalized)
|
||||
) {
|
||||
normalizedIds.push(normalized);
|
||||
}
|
||||
if (normalizedIds.length >= PUZZLE_PROMPT_REFERENCE_IMAGE_LIMIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return normalizedIds;
|
||||
}
|
||||
|
||||
function addPuzzlePromptReferenceImage(
|
||||
@@ -256,6 +300,21 @@ export function PuzzleAgentWorkspace({
|
||||
),
|
||||
[formState.referenceImageSrc, formState.referenceImageSrcs],
|
||||
);
|
||||
const promptReferenceAssetObjectIds = useMemo(
|
||||
() =>
|
||||
formState.referenceImageSrc
|
||||
? []
|
||||
: normalizePuzzleAssetObjectIds(
|
||||
formState.referenceImageSrcs.map((image) => image.assetObjectId),
|
||||
),
|
||||
[formState.referenceImageSrc, formState.referenceImageSrcs],
|
||||
);
|
||||
const mainReferenceImageSrcForPayload =
|
||||
formState.referenceImageAssetObjectId && formState.aiRedraw
|
||||
? null
|
||||
: formState.referenceImageSrc || null;
|
||||
const promptReferenceImageSrcsForPayload =
|
||||
promptReferenceAssetObjectIds.length > 0 ? [] : promptReferenceImageSrcs;
|
||||
const canSubmit = formState.aiRedraw
|
||||
? Boolean(pictureDescription) && !isBusy
|
||||
: Boolean(formState.referenceImageSrc) && !isBusy;
|
||||
@@ -263,16 +322,21 @@ export function PuzzleAgentWorkspace({
|
||||
() => ({
|
||||
seedText: pictureDescription,
|
||||
pictureDescription,
|
||||
referenceImageSrc: formState.referenceImageSrc || null,
|
||||
referenceImageSrcs: promptReferenceImageSrcs,
|
||||
referenceImageSrc: mainReferenceImageSrcForPayload,
|
||||
referenceImageSrcs: promptReferenceImageSrcsForPayload,
|
||||
referenceImageAssetObjectId:
|
||||
formState.referenceImageAssetObjectId || null,
|
||||
referenceImageAssetObjectIds: promptReferenceAssetObjectIds,
|
||||
imageModel: formState.imageModel,
|
||||
aiRedraw: formState.aiRedraw,
|
||||
}),
|
||||
[
|
||||
formState.aiRedraw,
|
||||
formState.referenceImageSrc,
|
||||
formState.referenceImageAssetObjectId,
|
||||
formState.imageModel,
|
||||
promptReferenceImageSrcs,
|
||||
mainReferenceImageSrcForPayload,
|
||||
promptReferenceAssetObjectIds,
|
||||
promptReferenceImageSrcsForPayload,
|
||||
pictureDescription,
|
||||
],
|
||||
);
|
||||
@@ -280,6 +344,8 @@ export function PuzzleAgentWorkspace({
|
||||
autosavePayload.pictureDescription,
|
||||
autosavePayload.referenceImageSrc,
|
||||
autosavePayload.referenceImageSrcs,
|
||||
autosavePayload.referenceImageAssetObjectId,
|
||||
autosavePayload.referenceImageAssetObjectIds,
|
||||
autosavePayload.aiRedraw,
|
||||
autosavePayload.imageModel,
|
||||
]);
|
||||
@@ -333,6 +399,7 @@ export function PuzzleAgentWorkspace({
|
||||
setCropState({
|
||||
source: uploadImage.dataUrl,
|
||||
label: file.name.trim() || '本地拼图图片',
|
||||
fileName: file.name.trim() || 'puzzle-reference.jpg',
|
||||
imageSize,
|
||||
cropRect: buildCenteredSquareImageCropRect(imageSize),
|
||||
error: null,
|
||||
@@ -342,9 +409,11 @@ export function PuzzleAgentWorkspace({
|
||||
return;
|
||||
}
|
||||
|
||||
const asset = await puzzleAssetClient.uploadReferenceImage({ file });
|
||||
setFormState((current) => ({
|
||||
...current,
|
||||
referenceImageSrc: uploadImage.dataUrl,
|
||||
referenceImageSrc: asset.imageSrc || uploadImage.dataUrl,
|
||||
referenceImageAssetObjectId: asset.assetObjectId,
|
||||
referenceImageLabel: file.name.trim() || '本地拼图图片',
|
||||
}));
|
||||
setReferenceImageError(null);
|
||||
@@ -372,11 +441,18 @@ export function PuzzleAgentWorkspace({
|
||||
|
||||
try {
|
||||
const images = await Promise.all(
|
||||
files.slice(0, remainingSlots).map(async (file, index) => ({
|
||||
id: `prompt-upload:${Date.now()}:${index}:${file.name}`,
|
||||
label: file.name.trim() || `参考图 ${index + 1}`,
|
||||
imageSrc: await readPuzzleReferenceImageAsDataUrl(file),
|
||||
})),
|
||||
files.slice(0, remainingSlots).map(async (file, index) => {
|
||||
const [imageSrc, asset] = await Promise.all([
|
||||
readPuzzleReferenceImageAsDataUrl(file),
|
||||
puzzleAssetClient.uploadReferenceImage({ file }),
|
||||
]);
|
||||
return {
|
||||
id: `prompt-upload:${Date.now()}:${index}:${file.name}`,
|
||||
label: file.name.trim() || `参考图 ${index + 1}`,
|
||||
imageSrc: asset.imageSrc || imageSrc,
|
||||
assetObjectId: asset.assetObjectId,
|
||||
};
|
||||
}),
|
||||
);
|
||||
setFormState((current) => ({
|
||||
...current,
|
||||
@@ -439,9 +515,15 @@ export function PuzzleAgentWorkspace({
|
||||
cropY: currentCropState.cropRect.y,
|
||||
cropSize: currentCropState.cropRect.size,
|
||||
});
|
||||
const file = puzzleReferenceImageDataUrlToFile(
|
||||
dataUrl,
|
||||
currentCropState.fileName,
|
||||
);
|
||||
const asset = await puzzleAssetClient.uploadReferenceImage({ file });
|
||||
setFormState((current) => ({
|
||||
...current,
|
||||
referenceImageSrc: dataUrl,
|
||||
referenceImageSrc: asset.imageSrc || dataUrl,
|
||||
referenceImageAssetObjectId: asset.assetObjectId,
|
||||
referenceImageLabel: currentCropState.label,
|
||||
}));
|
||||
setCropState(null);
|
||||
@@ -482,8 +564,11 @@ export function PuzzleAgentWorkspace({
|
||||
const payload = {
|
||||
seedText: payloadPictureDescription,
|
||||
pictureDescription: payloadPictureDescription,
|
||||
referenceImageSrc: formState.referenceImageSrc || null,
|
||||
referenceImageSrcs: promptReferenceImageSrcs,
|
||||
referenceImageSrc: mainReferenceImageSrcForPayload,
|
||||
referenceImageSrcs: promptReferenceImageSrcsForPayload,
|
||||
referenceImageAssetObjectId:
|
||||
formState.referenceImageAssetObjectId || null,
|
||||
referenceImageAssetObjectIds: promptReferenceAssetObjectIds,
|
||||
imageModel: formState.imageModel,
|
||||
aiRedraw: formState.aiRedraw,
|
||||
};
|
||||
@@ -499,8 +584,11 @@ export function PuzzleAgentWorkspace({
|
||||
action: 'compile_puzzle_draft',
|
||||
promptText: payloadPictureDescription,
|
||||
pictureDescription: payloadPictureDescription,
|
||||
referenceImageSrc: formState.referenceImageSrc || null,
|
||||
referenceImageSrcs: promptReferenceImageSrcs,
|
||||
referenceImageSrc: mainReferenceImageSrcForPayload,
|
||||
referenceImageSrcs: promptReferenceImageSrcsForPayload,
|
||||
referenceImageAssetObjectId:
|
||||
formState.referenceImageAssetObjectId || null,
|
||||
referenceImageAssetObjectIds: promptReferenceAssetObjectIds,
|
||||
imageModel: formState.imageModel,
|
||||
aiRedraw: formState.aiRedraw,
|
||||
candidateCount: 1,
|
||||
@@ -510,6 +598,7 @@ export function PuzzleAgentWorkspace({
|
||||
setFormState((current) => ({
|
||||
...current,
|
||||
referenceImageSrc: '',
|
||||
referenceImageAssetObjectId: '',
|
||||
referenceImageLabel: '',
|
||||
aiRedraw: true,
|
||||
}));
|
||||
@@ -645,6 +734,7 @@ export function PuzzleAgentWorkspace({
|
||||
setFormState((current) => ({
|
||||
...current,
|
||||
referenceImageSrc: asset.imageSrc,
|
||||
referenceImageAssetObjectId: asset.assetObjectId,
|
||||
referenceImageLabel: getPuzzleHistoryAssetReferenceLabel(
|
||||
asset.imageSrc,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user