1
This commit is contained in:
@@ -18,12 +18,14 @@ import {
|
||||
AnimationState,
|
||||
type Character,
|
||||
type CustomWorldProfile,
|
||||
type CustomWorldOpeningCgProfile,
|
||||
type SceneActBlueprint,
|
||||
type SceneChapterBlueprint,
|
||||
} from '../types';
|
||||
import { CharacterAnimator } from './CharacterAnimator';
|
||||
import { CustomWorldNpcPortrait } from './CustomWorldNpcVisualEditor';
|
||||
import { ResolvedAssetImage } from './ResolvedAssetImage';
|
||||
import { ResolvedAssetVideo } from './ResolvedAssetVideo';
|
||||
import type { RpgCreationEditorTarget } from './rpg-creation-editor/RpgCreationEntityEditorModal';
|
||||
|
||||
export type ResultTab = 'world' | 'playable' | 'story' | 'landmarks';
|
||||
@@ -50,6 +52,10 @@ interface CustomWorldEntityCatalogProps {
|
||||
createActionLabel?: string;
|
||||
onCreateAction?: () => void;
|
||||
createActionDisabled?: boolean;
|
||||
openingCgGenerating?: boolean;
|
||||
openingCgPhaseLabel?: string | null;
|
||||
openingCgGenerateDisabled?: boolean;
|
||||
onGenerateOpeningCg?: () => void;
|
||||
pendingGeneratedEntity?: PendingGeneratedEntity | null;
|
||||
recentGeneratedIds?: RecentGeneratedIds;
|
||||
readOnly?: boolean;
|
||||
@@ -240,6 +246,85 @@ function PendingEntityCard({
|
||||
);
|
||||
}
|
||||
|
||||
function OpeningCgPreview({
|
||||
openingCg,
|
||||
isGenerating,
|
||||
phaseLabel,
|
||||
generateDisabled,
|
||||
readOnly,
|
||||
onGenerate,
|
||||
}: {
|
||||
openingCg?: CustomWorldOpeningCgProfile | null;
|
||||
isGenerating: boolean;
|
||||
phaseLabel?: string | null;
|
||||
generateDisabled?: boolean;
|
||||
readOnly: boolean;
|
||||
onGenerate?: () => void;
|
||||
}) {
|
||||
const hasVideo = Boolean(openingCg?.videoSrc?.trim());
|
||||
const buttonLabel = hasVideo ? '重新生成' : '生成';
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="overflow-hidden rounded-2xl border border-[var(--platform-subpanel-border)] bg-black/35 aspect-video">
|
||||
{hasVideo ? (
|
||||
<ResolvedAssetVideo
|
||||
src={openingCg?.videoSrc}
|
||||
className="h-full w-full object-cover"
|
||||
controls
|
||||
playsInline
|
||||
preload="metadata"
|
||||
/>
|
||||
) : openingCg?.storyboardImageSrc ? (
|
||||
<ResolvedAssetImage
|
||||
src={openingCg.storyboardImageSrc}
|
||||
alt="开局 CG 故事板"
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center text-sm font-semibold tracking-[0.18em] text-zinc-500">
|
||||
开局 CG
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="platform-pill platform-pill--neutral px-2.5 py-1 text-[10px]">
|
||||
80 积分
|
||||
</span>
|
||||
<span className="platform-pill platform-pill--neutral px-2.5 py-1 text-[10px]">
|
||||
预计 10 分钟
|
||||
</span>
|
||||
{hasVideo ? (
|
||||
<span className="platform-pill platform-pill--success px-2.5 py-1 text-[10px]">
|
||||
已生成
|
||||
</span>
|
||||
) : null}
|
||||
{!readOnly && onGenerate ? (
|
||||
<div className="ml-auto">
|
||||
<SmallButton
|
||||
onClick={onGenerate}
|
||||
tone="sky"
|
||||
disabled={isGenerating || generateDisabled}
|
||||
>
|
||||
{isGenerating ? (phaseLabel ?? '生成中') : buttonLabel}
|
||||
</SmallButton>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{isGenerating ? (
|
||||
<div className="platform-progress-track h-2 overflow-hidden rounded-full">
|
||||
<div className="h-full w-2/3 animate-pulse bg-[linear-gradient(90deg,#ff4f8b_0%,#ff8a73_52%,#ffd2a6_100%)]" />
|
||||
</div>
|
||||
) : null}
|
||||
{openingCg?.status === 'failed' && openingCg.errorMessage ? (
|
||||
<div className="platform-banner platform-banner--danger rounded-2xl px-3 py-2 text-xs leading-5">
|
||||
{openingCg.errorMessage}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function buildSceneActParticipantText(
|
||||
act: SceneActBlueprint,
|
||||
roleById: Map<
|
||||
@@ -557,6 +642,10 @@ export function CustomWorldEntityCatalog({
|
||||
createActionLabel,
|
||||
onCreateAction,
|
||||
createActionDisabled = false,
|
||||
openingCgGenerating = false,
|
||||
openingCgPhaseLabel = null,
|
||||
openingCgGenerateDisabled = false,
|
||||
onGenerateOpeningCg,
|
||||
pendingGeneratedEntity = null,
|
||||
recentGeneratedIds = {
|
||||
playable: [],
|
||||
@@ -916,6 +1005,17 @@ export function CustomWorldEntityCatalog({
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section title="开局 CG">
|
||||
<OpeningCgPreview
|
||||
openingCg={profile.openingCg}
|
||||
isGenerating={openingCgGenerating}
|
||||
phaseLabel={openingCgPhaseLabel}
|
||||
generateDisabled={openingCgGenerateDisabled}
|
||||
readOnly={readOnly}
|
||||
onGenerate={onGenerateOpeningCg}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="世界概述"
|
||||
actions={
|
||||
|
||||
Reference in New Issue
Block a user