This commit is contained in:
2026-05-01 22:16:01 +08:00
parent 8d46c05129
commit 33dd105630
36 changed files with 1999 additions and 236 deletions

View File

@@ -26,6 +26,11 @@ import {
} from '../../services/puzzle-works/puzzleAssetClient';
import { readPuzzleReferenceImageAsDataUrl } from '../../services/puzzleReferenceImage';
import { useAuthUi } from '../auth/AuthUiContext';
import {
PUZZLE_IMAGE_MODEL_ORIGINAL,
type PuzzleImageModelId,
} from '../puzzle-agent/puzzleImageModelOptions';
import { PuzzleImageModelPicker } from '../puzzle-agent/PuzzleImageModelPicker';
import { ResolvedAssetImage } from '../ResolvedAssetImage';
type PuzzleResultViewProps = {
@@ -80,7 +85,9 @@ function resolveLevelFormalImageSrc(level: PuzzleDraftLevel) {
);
}
function buildFallbackLevelFromDraft(draft: PuzzleResultDraft): PuzzleDraftLevel {
function buildFallbackLevelFromDraft(
draft: PuzzleResultDraft,
): PuzzleDraftLevel {
return {
levelId: 'puzzle-level-1',
levelName: draft.levelName || '',
@@ -143,7 +150,9 @@ function createDraftEditState(draft: PuzzleResultDraft): DraftEditState {
};
}
function createBlankPuzzleLevel(existingLevels: PuzzleDraftLevel[]): PuzzleDraftLevel {
function createBlankPuzzleLevel(
existingLevels: PuzzleDraftLevel[],
): PuzzleDraftLevel {
const nextIndex = existingLevels.length + 1;
return {
levelId: `puzzle-level-${Date.now()}-${nextIndex}`,
@@ -200,7 +209,9 @@ function buildPublishReady(
...(levels.length > 0 ? [] : ['至少需要一个拼图关卡。']),
...levels.flatMap((level, index) => [
...(level.levelName.trim() ? [] : [`${index + 1}关名称不能为空。`]),
...(resolveLevelFormalImageSrc(level) ? [] : [`${index + 1}关缺少正式图。`]),
...(resolveLevelFormalImageSrc(level)
? []
: [`${index + 1}关缺少正式图。`]),
]),
];
@@ -574,6 +585,7 @@ function PuzzleLevelDetailDialog({
levelId: string,
promptText?: string | null,
referenceImageSrc?: string | null,
imageModel?: PuzzleImageModelId | null,
) => void;
onLevelChange: (nextLevel: PuzzleDraftLevel) => void;
onStartTestRun?: (level: PuzzleDraftLevel) => void;
@@ -581,8 +593,13 @@ function PuzzleLevelDetailDialog({
const platformTheme = useAuthUi()?.platformTheme ?? 'light';
const [referenceImageSrc, setReferenceImageSrc] = useState('');
const [referenceImageLabel, setReferenceImageLabel] = useState('');
const [referenceImageError, setReferenceImageError] = useState<string | null>(null);
const [referenceImageError, setReferenceImageError] = useState<string | null>(
null,
);
const [isHistoryPickerOpen, setIsHistoryPickerOpen] = useState(false);
const [imageModel, setImageModel] = useState<PuzzleImageModelId>(
PUZZLE_IMAGE_MODEL_ORIGINAL,
);
const formalImageSrc = resolveLevelFormalImageSrc(level);
const hasFormalImage = Boolean(formalImageSrc);
@@ -704,6 +721,11 @@ function PuzzleLevelDetailDialog({
className="w-full resize-none rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/90 px-4 py-3 pb-16 text-sm leading-6 text-[var(--platform-text-strong)] outline-none"
aria-label="画面描述"
/>
<PuzzleImageModelPicker
value={imageModel}
disabled={isBusy}
onChange={setImageModel}
/>
<label
className={`absolute bottom-3 right-3 inline-flex h-10 w-10 cursor-pointer items-center justify-center rounded-full border border-amber-300/70 bg-white/96 text-amber-700 shadow-sm transition hover:bg-amber-50 ${isBusy ? 'cursor-not-allowed opacity-55' : ''}`}
title={referenceImageSrc ? '更换参考图' : '添加参考图'}
@@ -787,6 +809,7 @@ function PuzzleLevelDetailDialog({
level.levelId,
level.pictureDescription.trim() || undefined,
referenceImageSrc || undefined,
imageModel,
);
}}
className="inline-flex w-full items-center justify-center gap-2 rounded-full bg-amber-600 px-4 py-3 text-sm font-bold text-white disabled:opacity-45"
@@ -836,7 +859,9 @@ function PuzzlePublishDialog({
}) {
const platformTheme = useAuthUi()?.platformTheme ?? 'light';
const primaryLevel = editState.levels[0] ?? null;
const formalImageSrc = primaryLevel ? resolveLevelFormalImageSrc(primaryLevel) : '';
const formalImageSrc = primaryLevel
? resolveLevelFormalImageSrc(primaryLevel)
: '';
if (typeof document === 'undefined') {
return null;
@@ -1180,7 +1205,9 @@ export function PuzzleResultView({
return syncDraftFromEditState(draft, editState);
}, [draft, editState]);
const primaryLevel = editState?.levels[0] ?? null;
const primaryImageSrc = primaryLevel ? resolveLevelFormalImageSrc(primaryLevel) : '';
const primaryImageSrc = primaryLevel
? resolveLevelFormalImageSrc(primaryLevel)
: '';
const imageRefreshKey = `${session.updatedAt}:${primaryImageSrc}:${editState?.levels.length ?? 0}`;
const activeLevel =
editState?.levels.find((level) => level.levelId === activeLevelId) ?? null;
@@ -1201,7 +1228,8 @@ export function PuzzleResultView({
pictureDescription: level.pictureDescription.trim(),
})),
};
const originalState = savedEditStateRef.current ?? createDraftEditState(draft);
const originalState =
savedEditStateRef.current ?? createDraftEditState(draft);
const changed =
JSON.stringify(normalizedState) !== JSON.stringify(originalState);
@@ -1386,12 +1414,13 @@ export function PuzzleResultView({
isBusy={isBusy}
level={activeLevel}
onClose={() => setActiveLevelId(null)}
onGenerate={(levelId, promptText, referenceImageSrc) => {
onGenerate={(levelId, promptText, referenceImageSrc, imageModel) => {
onExecuteAction({
action: 'generate_puzzle_images',
levelId,
promptText,
referenceImageSrc,
imageModel: imageModel ?? PUZZLE_IMAGE_MODEL_ORIGINAL,
candidateCount: 1,
levelsJson: JSON.stringify(editState.levels),
});