This commit is contained in:
2026-04-24 16:19:40 +08:00
38 changed files with 2505 additions and 7488 deletions

View File

@@ -49,6 +49,7 @@ export type CharacterAssetWorkflowCache = {
characterId: string;
visualPromptText: string;
animationPromptText: string;
animationPromptTextByKey?: Record<string, string>;
visualDrafts: CharacterVisualDraft[];
selectedVisualDraftId: string;
selectedAnimation: string;

View File

@@ -51,6 +51,40 @@ function clampAnimationPlaybackRate(value: number) {
);
}
function buildDefaultAnimationPromptTextByKey(defaultText: string) {
return CORE_ACTIONS.reduce<Partial<Record<AnimationState, string>>>(
(result, action) => ({
...result,
[action.animation]: defaultText,
}),
{},
);
}
function pickCachedAnimationPromptTextByKey(
cache: CharacterAssetWorkflowCache,
fallbackText: string,
) {
const fromCache = cache.animationPromptTextByKey ?? {};
return CORE_ACTIONS.reduce<Partial<Record<AnimationState, string>>>(
(result, action) => {
const cachedText = fromCache[action.animation]?.trim();
const legacyText = cache.animationPromptText?.trim();
return {
...result,
[action.animation]:
cachedText && !isLegacyGeneratedActionDescription(cachedText)
? cachedText
: legacyText && !isLegacyGeneratedActionDescription(legacyText)
? legacyText
: fallbackText,
};
},
{},
);
}
function roundAnimationFps(value: number) {
return Math.round(value * 100) / 100;
}
@@ -548,8 +582,12 @@ export function RpgCreationRoleAssetStudioModal({
const [selectedAnimation, setSelectedAnimation] = useState<AnimationState>(
CORE_ACTIONS[0]?.animation ?? AnimationState.IDLE,
);
const [animationPromptText, setAnimationPromptText] = useState(
initialPromptBundle.animationPromptText,
const [animationPromptTextByKey, setAnimationPromptTextByKey] = useState<
Partial<Record<AnimationState, string>>
>(() =>
buildDefaultAnimationPromptTextByKey(
initialPromptBundle.animationPromptText,
),
);
const [animationStatusByKey, setAnimationStatusByKey] = useState<
Partial<Record<AnimationState, string | null>>
@@ -617,6 +655,9 @@ export function RpgCreationRoleAssetStudioModal({
const selectedActionConfig =
CORE_ACTIONS.find((item) => item.animation === selectedAnimation) ??
CORE_ACTIONS[0]!;
const animationPromptText =
animationPromptTextByKey[selectedAnimation] ??
initialPromptBundle.animationPromptText;
const previewCharacter = useMemo(
() =>
buildAnimationPreviewCharacter({
@@ -689,7 +730,11 @@ export function RpgCreationRoleAssetStudioModal({
let cancelled = false;
setWorkingRole(baseRole);
setVisualPromptText(initialPromptBundle.visualPromptText);
setAnimationPromptText(initialPromptBundle.animationPromptText);
setAnimationPromptTextByKey(
buildDefaultAnimationPromptTextByKey(
initialPromptBundle.animationPromptText,
),
);
setReferenceImageDataUrls([]);
setVisualDrafts([]);
setSelectedVisualDraftId('');
@@ -725,11 +770,11 @@ export function RpgCreationRoleAssetStudioModal({
? cache.visualPromptText
: initialPromptBundle.visualPromptText,
);
setAnimationPromptText(
cache.animationPromptText &&
!isLegacyGeneratedActionDescription(cache.animationPromptText)
? cache.animationPromptText
: initialPromptBundle.animationPromptText,
setAnimationPromptTextByKey(
pickCachedAnimationPromptTextByKey(
cache,
initialPromptBundle.animationPromptText,
),
);
setVisualDrafts(cache.visualDrafts ?? []);
setSelectedVisualDraftId(

View File

@@ -2400,7 +2400,9 @@ function SceneImageGenerationModal({
onClose: () => void;
}) {
const [userPrompt, setUserPrompt] = useDraft(
landmark.name.trim() || landmark.description.trim(),
landmark.visualDescription?.trim() ||
landmark.description.trim() ||
landmark.name.trim(),
);
const [referenceImageSrc, setReferenceImageSrc] = useState('');
const [isGenerating, setIsGenerating] = useState(false);