e0319e80f0
- 资源编辑命令新增create生成模式,支持无源视频、音效和背景音乐生成 - 图片聚焦态新增角色动画生成入口,提交角色动画接口并下载预览视频登记为本地媒体 - 资源页新增生成视频、生成音效、生成背景音乐入口 - 统一资源编辑面板按生成模式区分新建媒体资源与编辑现有资源 - 补充资源生成与角色动画定向测试,并更新AI游戏创作App实施计划文档
152 lines
4.5 KiB
TypeScript
152 lines
4.5 KiB
TypeScript
import { ChevronLeft, LoaderCircle, Sparkles } from 'lucide-react';
|
|
import { type FormEvent, useState } from 'react';
|
|
|
|
import {
|
|
type LocalProjectResourceEditKind,
|
|
resourceEditPromptMaxLength,
|
|
} from './resourceEditModel';
|
|
|
|
export type ResourceEditSubmitInput = {
|
|
prompt: string;
|
|
assetName: string;
|
|
};
|
|
|
|
export function ResourceEditSurface({
|
|
resourceLabel,
|
|
editKind,
|
|
generationMode = 'derive',
|
|
initialAssetName,
|
|
semanticNotice,
|
|
onCancel,
|
|
onSubmit,
|
|
}: {
|
|
resourceLabel: string;
|
|
editKind: LocalProjectResourceEditKind;
|
|
generationMode?: 'create' | 'derive';
|
|
initialAssetName: string;
|
|
semanticNotice: string | null;
|
|
onCancel: () => void;
|
|
onSubmit: (input: ResourceEditSubmitInput) => Promise<void>;
|
|
}) {
|
|
const [prompt, setPrompt] = useState('');
|
|
const [assetName, setAssetName] = useState(initialAssetName);
|
|
const [attempted, setAttempted] = useState(false);
|
|
const [submitting, setSubmitting] = useState(false);
|
|
const [error, setError] = useState('');
|
|
const inputLocked = attempted || submitting;
|
|
const promptMaxLength = resourceEditPromptMaxLength(editKind);
|
|
const isCreate = generationMode === 'create';
|
|
|
|
async function submit(event: FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
const normalizedPrompt = prompt.trim();
|
|
const normalizedName = assetName.trim();
|
|
if (!normalizedPrompt || !normalizedName || submitting) {
|
|
return;
|
|
}
|
|
setAttempted(true);
|
|
setSubmitting(true);
|
|
setError('');
|
|
try {
|
|
await onSubmit({ prompt: normalizedPrompt, assetName: normalizedName });
|
|
} catch (submitError) {
|
|
setError(
|
|
submitError instanceof Error
|
|
? submitError.message
|
|
: String(submitError),
|
|
);
|
|
setSubmitting(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<section
|
|
className="game-resource-editor"
|
|
aria-labelledby="game-resource-editor-title"
|
|
>
|
|
<header className="game-resource-editor-titlebar">
|
|
<button
|
|
type="button"
|
|
className="game-resource-editor-back"
|
|
onClick={onCancel}
|
|
disabled={submitting}
|
|
>
|
|
<ChevronLeft size={16} aria-hidden="true" />
|
|
返回资源
|
|
</button>
|
|
<span>
|
|
<small>{isCreate ? '新建媒体资源' : '编辑现有资源'}</small>
|
|
<strong id="game-resource-editor-title">{resourceLabel}</strong>
|
|
</span>
|
|
</header>
|
|
<form className="game-resource-editor-form" onSubmit={submit}>
|
|
{editKind !== 'version' ? (
|
|
<label>
|
|
<span>派生资源名称</span>
|
|
<input
|
|
value={assetName}
|
|
maxLength={120}
|
|
disabled={inputLocked}
|
|
onChange={(event) => setAssetName(event.currentTarget.value)}
|
|
/>
|
|
</label>
|
|
) : null}
|
|
<label>
|
|
<span>编辑提示词</span>
|
|
<textarea
|
|
value={prompt}
|
|
maxLength={promptMaxLength}
|
|
rows={7}
|
|
autoFocus
|
|
disabled={inputLocked}
|
|
placeholder={
|
|
isCreate
|
|
? '描述希望生成的视频、音效或背景音乐'
|
|
: '描述希望在现有资源基础上发生的变化'
|
|
}
|
|
onChange={(event) => setPrompt(event.currentTarget.value)}
|
|
/>
|
|
</label>
|
|
{semanticNotice ? (
|
|
<p className="game-resource-editor-semantic-note">{semanticNotice}</p>
|
|
) : null}
|
|
{error ? (
|
|
<p className="game-resource-editor-error" role="alert">
|
|
{error}
|
|
</p>
|
|
) : null}
|
|
<div className="game-resource-editor-actions">
|
|
{error ? (
|
|
<button type="submit" disabled={submitting}>
|
|
<Sparkles size={15} aria-hidden="true" />
|
|
使用原请求重试
|
|
</button>
|
|
) : (
|
|
<button
|
|
type="submit"
|
|
disabled={submitting || !prompt.trim() || !assetName.trim()}
|
|
>
|
|
{submitting ? (
|
|
<LoaderCircle
|
|
className="game-resource-editor-spinner"
|
|
size={15}
|
|
aria-hidden="true"
|
|
/>
|
|
) : (
|
|
<Sparkles size={15} aria-hidden="true" />
|
|
)}
|
|
{submitting
|
|
? '正在派生…'
|
|
: editKind === 'version'
|
|
? '创建子版本'
|
|
: isCreate
|
|
? '生成资源'
|
|
: '生成派生资源'}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</form>
|
|
</section>
|
|
);
|
|
}
|