Files
Genarrative/apps/ai-game-creator-shell/src/view/project-development/ResourceEditSurface.tsx
T
Git Hooks Test e0319e80f0 补齐AGC资源生成能力并接入视频动画与音频生成
- 资源编辑命令新增create生成模式,支持无源视频、音效和背景音乐生成

- 图片聚焦态新增角色动画生成入口,提交角色动画接口并下载预览视频登记为本地媒体

- 资源页新增生成视频、生成音效、生成背景音乐入口

- 统一资源编辑面板按生成模式区分新建媒体资源与编辑现有资源

- 补充资源生成与角色动画定向测试,并更新AI游戏创作App实施计划文档
2026-08-23 21:37:27 +08:00

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>
);
}