auto-save 2026-05-19 10:40 (+1, ~3)
This commit is contained in:
@@ -13,7 +13,10 @@ import type {
|
||||
GenerateResponse,
|
||||
LockCharacterResponse,
|
||||
PackKind,
|
||||
ProjectFromUploadResponse,
|
||||
RegenerateAssetResponse,
|
||||
UploadImageResponse,
|
||||
UploadedImageRole,
|
||||
VideoGenerationResponse,
|
||||
} from '@/lib/types';
|
||||
import type { VIDEO_TEMPLATES } from '@/lib/templates';
|
||||
@@ -26,6 +29,7 @@ export default function Home() {
|
||||
const [allLoading, setAllLoading] = useState(false);
|
||||
const [characterLoading, setCharacterLoading] = useState(false);
|
||||
const [videoLoading, setVideoLoading] = useState(false);
|
||||
const [uploadLoading, setUploadLoading] = useState(false);
|
||||
const [provider, setProvider] = useState<string>('?');
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
|
||||
@@ -60,6 +64,55 @@ export default function Home() {
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadImage(file: File, role: UploadedImageRole) {
|
||||
const form = new FormData();
|
||||
form.set('image', file);
|
||||
form.set('role', role);
|
||||
const r = await fetch('/api/uploads', { method: 'POST', body: form });
|
||||
if (!r.ok) throw new Error(await r.text());
|
||||
const d: UploadImageResponse = await r.json();
|
||||
return d.uploadedImage;
|
||||
}
|
||||
|
||||
async function handleUploadProject(opts: {
|
||||
mode: 'remix' | 'replicate';
|
||||
files: Array<{ file: File; role: 'reference' | 'subject' }>;
|
||||
prompt?: string;
|
||||
styleId?: string;
|
||||
count?: number;
|
||||
}) {
|
||||
if (uploadLoading) return;
|
||||
setUploadLoading(true);
|
||||
try {
|
||||
const uploadedImages = await Promise.all(opts.files.map(item => uploadImage(item.file, item.role)));
|
||||
const r = await fetch('/api/projects/from-upload', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
uploadedImages,
|
||||
mode: opts.mode,
|
||||
remixPrompt: opts.mode === 'remix' ? opts.prompt : undefined,
|
||||
userHint: opts.mode === 'replicate' ? opts.prompt : undefined,
|
||||
styleId: opts.styleId,
|
||||
count: opts.count,
|
||||
}),
|
||||
});
|
||||
if (!r.ok) {
|
||||
alert('上传项目创建失败:' + (await r.text()));
|
||||
return;
|
||||
}
|
||||
const d: ProjectFromUploadResponse = await r.json();
|
||||
setProvider(d.provider);
|
||||
const all = await refreshSessions();
|
||||
const s = all.find(x => x.id === d.sessionId) ?? null;
|
||||
setCurrent(s);
|
||||
} catch (error) {
|
||||
alert('上传失败:' + String(error));
|
||||
} finally {
|
||||
setUploadLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAction(imageId: string, action: 'select' | 'reject' | 'reset') {
|
||||
if (!current) return;
|
||||
const r = await fetch('/api/select', {
|
||||
@@ -243,7 +296,12 @@ export default function Home() {
|
||||
</header>
|
||||
|
||||
<div className="space-y-8">
|
||||
<PromptPanel onGenerate={handleGenerate} loading={loading} />
|
||||
<PromptPanel
|
||||
onGenerate={handleGenerate}
|
||||
onUploadProject={handleUploadProject}
|
||||
loading={loading}
|
||||
uploadLoading={uploadLoading}
|
||||
/>
|
||||
{current && (
|
||||
<section className="space-y-5">
|
||||
<div className="flex items-end justify-between">
|
||||
|
||||
Reference in New Issue
Block a user