1
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user