This commit is contained in:
2026-05-08 11:44:42 +08:00
parent b08127031c
commit abf1f1ebea
249 changed files with 39411 additions and 887 deletions

View File

@@ -4,6 +4,7 @@ import {
ImagePlus,
Images,
Loader2,
MessageSquareText,
Play,
Plus,
Sparkles,
@@ -13,6 +14,7 @@ import {
import { type ChangeEvent, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import type { CreativeDraftEditResult } from '../../../packages/shared/src/contracts/creativeAgent';
import type { PuzzleAgentActionRequest } from '../../../packages/shared/src/contracts/puzzleAgentActions';
import type {
PuzzleDraftLevel,
@@ -41,6 +43,14 @@ type PuzzleResultViewProps = {
onBack: () => void;
onExecuteAction: (payload: PuzzleAgentActionRequest) => void;
onStartTestRun?: (draft: PuzzleResultDraft) => void;
creativeDraftEdit?: {
isBusy: boolean;
error: string | null;
onSubmit: (payload: {
instruction: string;
currentDraft: PuzzleResultDraft;
}) => Promise<CreativeDraftEditResult | null> | void;
} | null;
};
type PuzzleAutoSaveState = 'idle' | 'saving' | 'saved' | 'error';
@@ -113,6 +123,7 @@ function normalizeDraftLevels(draft: PuzzleResultDraft) {
levelId: level.levelId?.trim() || `puzzle-level-${index + 1}`,
levelName: level.levelName?.trim() || '',
pictureDescription: level.pictureDescription?.trim() || draft.summary,
pictureReference: level.pictureReference ?? null,
candidates: level.candidates ?? [],
selectedCandidateId: level.selectedCandidateId ?? null,
coverImageSrc: level.coverImageSrc ?? null,
@@ -160,6 +171,7 @@ function createBlankPuzzleLevel(
levelId: `puzzle-level-${Date.now()}-${nextIndex}`,
levelName: '',
pictureDescription: '',
pictureReference: null,
candidates: [],
selectedCandidateId: null,
coverImageSrc: null,
@@ -835,6 +847,20 @@ function PuzzleLevelDetailDialog({
</label>
</div>
<input
value={level.pictureReference ?? ''}
disabled={isBusy}
onChange={(event) =>
onLevelChange({
...level,
pictureReference: event.target.value,
})
}
className="mt-3 w-full rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/90 px-4 py-3 text-sm text-[var(--platform-text-strong)] outline-none"
placeholder="参考图链接或资产ID"
aria-label="图面参考"
/>
{referenceImageSrc ? (
<div className="mt-3 flex items-center gap-3 rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-3">
<div className="h-14 w-14 overflow-hidden rounded-[0.9rem] bg-[var(--platform-subpanel-fill)]">
@@ -1112,6 +1138,78 @@ function PuzzlePublishDialog({
);
}
function PuzzleCreativeDraftEditBar({
currentDraft,
error,
isBusy,
onSubmit,
}: {
currentDraft: PuzzleResultDraft;
error: string | null;
isBusy: boolean;
onSubmit: (payload: {
instruction: string;
currentDraft: PuzzleResultDraft;
}) => Promise<CreativeDraftEditResult | null> | void;
}) {
const [instruction, setInstruction] = useState('');
const trimmedInstruction = instruction.trim();
const canSubmit = Boolean(trimmedInstruction) && !isBusy;
const submit = () => {
if (!canSubmit) {
return;
}
void onSubmit({
instruction: trimmedInstruction,
currentDraft,
});
setInstruction('');
};
return (
<section className="platform-subpanel mb-3 rounded-[1.35rem] p-3 sm:p-4">
<div className="flex items-end gap-2">
<span className="mb-1 hidden h-9 w-9 shrink-0 items-center justify-center rounded-full bg-white/72 text-[var(--platform-text-base)] sm:inline-flex">
{isBusy ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<MessageSquareText className="h-4 w-4" />
)}
</span>
<textarea
value={instruction}
disabled={isBusy}
rows={2}
onChange={(event) => setInstruction(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
submit();
}
}}
className="min-h-11 flex-1 resize-none rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/90 px-4 py-3 text-sm leading-5 text-[var(--platform-text-strong)] outline-none"
placeholder="让 Agent 调整标题、标签或关卡描述"
aria-label="智能修订拼图草稿"
/>
<button
type="button"
disabled={!canSubmit}
onClick={submit}
className="platform-button platform-button--secondary min-h-11 px-4 py-2 text-sm"
>
{isBusy ? '修改中' : '修改'}
</button>
</div>
{error ? (
<div className="platform-banner platform-banner--danger mt-3 rounded-[1rem] text-sm leading-6">
{error}
</div>
) : null}
</section>
);
}
function PuzzleLevelListTab({
editState,
imageRefreshKey,
@@ -1316,6 +1414,7 @@ export function PuzzleResultView({
onBack,
onExecuteAction,
onStartTestRun,
creativeDraftEdit = null,
}: PuzzleResultViewProps) {
const draft = session.draft;
const [activeTab, setActiveTab] = useState<PuzzleResultTab>('levels');
@@ -1491,6 +1590,15 @@ export function PuzzleResultView({
<PuzzleResultTabs activeTab={activeTab} onChange={setActiveTab} />
{creativeDraftEdit ? (
<PuzzleCreativeDraftEditBar
currentDraft={syncedDraft}
error={creativeDraftEdit.error}
isBusy={creativeDraftEdit.isBusy}
onSubmit={creativeDraftEdit.onSubmit}
/>
) : null}
<div className="min-h-0 flex-1 overflow-y-auto pr-1">
{activeTab === 'levels' ? (
<PuzzleLevelListTab