auto-save 2026-05-18 10:44 (+6, ~2)

This commit is contained in:
2026-05-18 10:46:21 +08:00
parent 0accb73400
commit 494779dc13
20 changed files with 772 additions and 0 deletions

97
src/app/page.tsx Normal file
View File

@@ -0,0 +1,97 @@
'use client';
import { useCallback, useEffect, useState } from 'react';
import PromptPanel from '@/components/PromptPanel';
import ResultGrid from '@/components/ResultGrid';
import Sidebar from '@/components/Sidebar';
import type { GenImage, GenSession, GenerateResponse } 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 [provider, setProvider] = useState<string>('?');
const refreshSessions = useCallback(async () => {
const r = await fetch('/api/sessions');
const d = await r.json();
setSessions(d.sessions);
return d.sessions as GenSession[];
}, []);
useEffect(() => { refreshSessions(); }, [refreshSessions]);
async function handleGenerate(opts: { prompt: string; refImages: string[]; count: number; style?: string }) {
setLoading(true);
try {
const r = await fetch('/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(opts),
});
if (!r.ok) {
alert('生成失败:' + (await r.text()));
return;
}
const d: GenerateResponse = await r.json();
setProvider(d.provider);
const all = await refreshSessions();
const s = all.find(x => x.id === d.sessionId) ?? null;
setCurrent(s);
} finally {
setLoading(false);
}
}
async function handleAction(imageId: string, action: 'select' | 'reject' | 'reset') {
if (!current) return;
const r = await fetch('/api/select', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: current.id, imageId, action }),
});
if (!r.ok) return;
const d: { image: GenImage } = await r.json();
setCurrent({
...current,
images: current.images.map(i => i.id === imageId ? d.image : i),
});
refreshSessions();
}
return (
<div className="flex h-screen">
<Sidebar
sessions={sessions}
currentId={current?.id ?? null}
onPick={id => setCurrent(sessions.find(s => s.id === id) ?? null)}
onNew={() => setCurrent(null)}
/>
<main className="flex-1 overflow-y-auto">
<header className="px-8 py-6 border-b border-white/10 flex items-center justify-between">
<div>
<h1 className="text-xl font-bold">AI </h1>
<p className="text-xs text-white/40 mt-1"> </p>
</div>
<div className="text-xs text-white/40">
provider: <span className={provider === 'poe' ? 'text-accent' : 'text-yellow-400'}>{provider}</span>
{provider === 'mock' && <span className="ml-2 text-yellow-400/80">( POE_API_KEY)</span>}
</div>
</header>
<div className="p-8 space-y-6 max-w-6xl">
<PromptPanel onGenerate={handleGenerate} loading={loading} />
{current && (
<section className="card p-6 space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-sm text-white/60"> · {new Date(current.createdAt).toLocaleString('zh-CN')}</h2>
<code className="text-xs text-white/30">{current.id}</code>
</div>
<ResultGrid images={current.images} onAction={handleAction} />
</section>
)}
</div>
</main>
</div>
);
}