Persist custom world asset configs in runtime snapshots
This commit is contained in:
@@ -3,8 +3,8 @@ import {
|
||||
RefreshCcw,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
type CSSProperties,
|
||||
type ChangeEvent,
|
||||
type CSSProperties,
|
||||
type ReactNode,
|
||||
useEffect,
|
||||
useMemo,
|
||||
@@ -15,8 +15,8 @@ import { createPortal } from 'react-dom';
|
||||
import { ROLE_TEMPLATE_CHARACTERS } from '../data/characterPresets';
|
||||
import {
|
||||
AnimationState,
|
||||
type CharacterAnimationConfig,
|
||||
type Character,
|
||||
type CharacterAnimationConfig,
|
||||
} from '../types';
|
||||
import {
|
||||
buildAnimationClipFromVideoSource,
|
||||
@@ -358,6 +358,37 @@ function buildRoleCharacterBrief(
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function isLegacyGeneratedVisualDescription(value: string) {
|
||||
const normalized = value.trim();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'2D 横版 RPG',
|
||||
'纯绿色绿幕',
|
||||
'2 到 2.5 头身',
|
||||
'深色粗轮廓',
|
||||
'身体整体朝右',
|
||||
'脚底完整可见',
|
||||
].some((marker) => normalized.includes(marker));
|
||||
}
|
||||
|
||||
function isLegacyGeneratedActionDescription(value: string) {
|
||||
const normalized = value.trim();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return [
|
||||
'动作气质参考:',
|
||||
'发力起手明确',
|
||||
'收招利落',
|
||||
'动作表现偏向',
|
||||
'起手克制',
|
||||
].some((marker) => normalized.includes(marker));
|
||||
}
|
||||
|
||||
function mergeRole<T extends EditableCustomWorldRole>(
|
||||
role: T,
|
||||
patch: Partial<T>,
|
||||
@@ -712,10 +743,16 @@ export function CustomWorldRoleAssetStudioModal({
|
||||
});
|
||||
setWorkingRole(nextRole);
|
||||
setVisualPromptText(
|
||||
cache.visualPromptText || initialPromptBundle.visualPromptText,
|
||||
cache.visualPromptText &&
|
||||
!isLegacyGeneratedVisualDescription(cache.visualPromptText)
|
||||
? cache.visualPromptText
|
||||
: initialPromptBundle.visualPromptText,
|
||||
);
|
||||
setAnimationPromptText(
|
||||
cache.animationPromptText || initialPromptBundle.animationPromptText,
|
||||
cache.animationPromptText &&
|
||||
!isLegacyGeneratedActionDescription(cache.animationPromptText)
|
||||
? cache.animationPromptText
|
||||
: initialPromptBundle.animationPromptText,
|
||||
);
|
||||
setVisualDrafts(cache.visualDrafts ?? []);
|
||||
setSelectedVisualDraftId(
|
||||
@@ -904,6 +941,8 @@ export function CustomWorldRoleAssetStudioModal({
|
||||
}
|
||||
|
||||
const isLoopAction = config.loop;
|
||||
const shouldUseLastFrameReference =
|
||||
!isLoopAction && config.animation !== AnimationState.DIE;
|
||||
|
||||
const result = await generateCharacterAnimationDraft({
|
||||
characterId: workingRole.id,
|
||||
@@ -915,7 +954,9 @@ export function CustomWorldRoleAssetStudioModal({
|
||||
visualSource: workingRole.imageSrc,
|
||||
referenceImageDataUrls: [],
|
||||
referenceVideoDataUrls: [],
|
||||
lastFrameImageDataUrl: isLoopAction ? undefined : workingRole.imageSrc,
|
||||
lastFrameImageDataUrl: shouldUseLastFrameReference
|
||||
? workingRole.imageSrc
|
||||
: undefined,
|
||||
frameCount: config.frameCount,
|
||||
fps: config.fps,
|
||||
durationSeconds: config.durationSeconds,
|
||||
@@ -1108,12 +1149,12 @@ export function CustomWorldRoleAssetStudioModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Field label="形象提示词">
|
||||
<Field label="形象描述">
|
||||
<TextArea
|
||||
value={visualPromptText}
|
||||
onChange={setVisualPromptText}
|
||||
rows={6}
|
||||
placeholder="角色形象提示词会先按设定自动生成,也可以继续手动细化。"
|
||||
placeholder="这里默认展示角色形象描述,也可以继续手动细化。"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
@@ -1296,12 +1337,12 @@ export function CustomWorldRoleAssetStudioModal({
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Field label="动作提示词">
|
||||
<Field label="动作描述">
|
||||
<TextArea
|
||||
value={animationPromptText}
|
||||
onChange={setAnimationPromptText}
|
||||
rows={5}
|
||||
placeholder="角色动作提示词会先按设定自动生成,也可以继续手动细化。"
|
||||
placeholder="这里默认展示角色动作描述,也可以继续手动细化。"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user