auto-save 2026-05-18 23:55 (+5, ~9)

This commit is contained in:
2026-05-18 23:55:42 +08:00
parent a1b783cedb
commit 4eda85ed66
16 changed files with 958 additions and 57 deletions

View File

@@ -5,13 +5,25 @@ import PromptPanel from '@/components/PromptPanel';
import ResultGrid from '@/components/ResultGrid';
import Sidebar from '@/components/Sidebar';
import PackPanel from '@/components/PackPanel';
import type { GenImage, GenSession, GeneratePackResponse, GenerateResponse, PackKind } from '@/lib/types';
import type {
GenImage,
GenSession,
GenerateAllPacksResponse,
GeneratePackResponse,
GenerateResponse,
LockCharacterResponse,
PackKind,
VideoGenerationResponse,
} from '@/lib/types';
export default function Home() {
const [sessions, setSessions] = useState<GenSession[]>([]);
const [current, setCurrent] = useState<GenSession | null>(null);
const [loading, setLoading] = useState(false);
const [loadingKind, setLoadingKind] = useState<PackKind | null>(null);
const [allLoading, setAllLoading] = useState(false);
const [characterLoading, setCharacterLoading] = useState(false);
const [videoLoading, setVideoLoading] = useState(false);
const [provider, setProvider] = useState<string>('?');
const [sidebarOpen, setSidebarOpen] = useState(true);
@@ -85,6 +97,84 @@ export default function Home() {
}
}
async function reloadCurrent(sessionId: string) {
const all = await refreshSessions();
const updated = all.find(x => x.id === sessionId) ?? null;
setCurrent(updated);
return updated;
}
async function handleLockCharacter(image: GenImage) {
if (!current || characterLoading) return;
setCharacterLoading(true);
try {
const r = await fetch('/api/character/lock', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: current.id, imageId: image.id, force: true }),
});
if (!r.ok) {
alert('角色锁定失败:' + (await r.text()));
return;
}
const d: LockCharacterResponse = await r.json();
setProvider(d.provider);
await reloadCurrent(current.id);
} finally {
setCharacterLoading(false);
}
}
async function handleGenerateAll(image: GenImage) {
if (!current || allLoading) return;
setAllLoading(true);
try {
const r = await fetch('/api/packs/generate-all', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: current.id, imageId: image.id }),
});
if (!r.ok) {
alert('完整三包生成失败:' + (await r.text()));
return;
}
const d: GenerateAllPacksResponse = await r.json();
setProvider(d.provider);
await reloadCurrent(current.id);
} finally {
setAllLoading(false);
}
}
async function handleGenerateVideo(image: GenImage, promptTemplate: string) {
if (!current || videoLoading) return;
setVideoLoading(true);
try {
const character = current.characterSpec
? `${current.characterSpec.name}${current.characterSpec.oneLiner}`
: current.prompt;
const r = await fetch('/api/video/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
prompt: promptTemplate.replace('{character}', character),
imageUrl: image.url,
duration: 6,
ratio: '16:9',
resolution: '1080p',
}),
});
if (!r.ok) {
alert('Seedance 视频提交失败:' + (await r.text()));
return;
}
const d: VideoGenerationResponse = await r.json();
alert(`Seedance 任务已提交:${d.taskId ?? d.status}`);
} finally {
setVideoLoading(false);
}
}
return (
<div className="flex h-screen bg-[#FAFAFA]">
<Sidebar
@@ -107,9 +197,9 @@ export default function Home() {
</p>
</div>
<div className="flex items-center gap-2 pt-1">
<span className={provider === 'poe' ? 'chip chip-live' : 'chip chip-mock'}>
<span className={`w-1.5 h-1.5 rounded-full ${provider === 'poe' ? 'bg-emerald-500' : 'bg-amber-500'}`} />
{provider === 'poe' ? 'Poe · 实时生图' : provider === 'mock' ? 'Mock · 占位图' : provider}
<span className={provider === 'gpt' ? 'chip chip-live' : 'chip chip-mock'}>
<span className={`w-1.5 h-1.5 rounded-full ${provider === 'gpt' ? 'bg-emerald-500' : 'bg-amber-500'}`} />
{provider === 'gpt' ? 'GPT · 最高规格' : provider === 'mock' ? 'Mock · 占位图' : provider}
</span>
</div>
</header>
@@ -128,7 +218,17 @@ export default function Home() {
<code className="text-[11px] text-zinc-400 font-mono">{current.id}</code>
</div>
<ResultGrid images={current.images} onAction={handleAction} />
<PackPanel session={current} loadingKind={loadingKind} onGenerate={handleGeneratePack} />
<PackPanel
session={current}
loadingKind={loadingKind}
allLoading={allLoading}
characterLoading={characterLoading}
videoLoading={videoLoading}
onGenerate={handleGeneratePack}
onGenerateAll={handleGenerateAll}
onLockCharacter={handleLockCharacter}
onGenerateVideo={handleGenerateVideo}
/>
</section>
)}
</div>