1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-20 09:54:17 +08:00
parent 67c584b4df
commit 50759f3c1e
159 changed files with 16938 additions and 16925 deletions

View File

@@ -19,6 +19,106 @@ function readImageSrc(value: unknown) {
return readString(value) || null;
}
type CustomWorldCoverRenderMode = 'image' | 'scene_with_roles';
function normalizeCoverCharacterRoleIds(
value: unknown,
playableRoles: Record<string, unknown>[],
) {
const availableIds = new Set(
playableRoles.map((role) => readString(role.id)).filter(Boolean),
);
const selectedIds = readArray(value)
.map((entry) => readString(entry))
.filter((entry) => entry && availableIds.has(entry));
if (selectedIds.length > 0) {
return [...new Set(selectedIds)].slice(0, 3);
}
return [...availableIds].slice(0, 3);
}
function resolveOpeningSceneImageSrc(profile: CustomWorldProfileRecord) {
const campImage = isRecord(profile.camp)
? readImageSrc(profile.camp.imageSrc)
: null;
if (campImage) {
return campImage;
}
return (
readArray(profile.landmarks)
.map((landmark) =>
isRecord(landmark) ? readImageSrc(landmark.imageSrc) : null,
)
.find(Boolean) || null
);
}
function resolveLeadPlayableImageSrc(playableRoles: Record<string, unknown>[]) {
return (
playableRoles
.map((role) => readImageSrc(role.imageSrc))
.find(Boolean) || null
);
}
export function resolveCustomWorldCoverPresentation(
profile: CustomWorldProfileRecord,
): {
imageSrc: string | null;
renderMode: CustomWorldCoverRenderMode;
characterImageSrcs: string[];
sourceType: 'default' | 'uploaded' | 'generated';
} {
const playableRoles = readArray(profile.playableNpcs).filter(isRecord);
const cover = isRecord(profile.cover) ? profile.cover : null;
const requestedSourceType = readString(cover?.sourceType);
const sourceType =
requestedSourceType === 'uploaded' ||
requestedSourceType === 'generated'
? requestedSourceType
: 'default';
if (sourceType !== 'default') {
const explicitImageSrc = readImageSrc(cover?.imageSrc);
if (explicitImageSrc) {
return {
imageSrc: explicitImageSrc,
renderMode: 'image',
characterImageSrcs: [],
sourceType,
};
}
}
const openingSceneImageSrc = resolveOpeningSceneImageSrc(profile);
const roleById = new Map(
playableRoles.map((role) => [readString(role.id), role] as const),
);
const characterImageSrcs = normalizeCoverCharacterRoleIds(
cover?.characterRoleIds,
playableRoles,
)
.map((roleId) => readImageSrc(roleById.get(roleId)?.imageSrc))
.filter((imageSrc): imageSrc is string => Boolean(imageSrc));
const leadPlayableImageSrc = resolveLeadPlayableImageSrc(playableRoles);
return {
imageSrc: openingSceneImageSrc || leadPlayableImageSrc,
renderMode:
openingSceneImageSrc && characterImageSrcs.length > 0
? 'scene_with_roles'
: 'image',
characterImageSrcs:
openingSceneImageSrc && characterImageSrcs.length > 0
? characterImageSrcs
: [],
sourceType: 'default',
};
}
function detectThemeMode(
profile: Pick<
CustomWorldProfileRecord,
@@ -64,28 +164,7 @@ function detectThemeMode(
}
export function buildCustomWorldCoverImageSrc(profile: CustomWorldProfileRecord) {
const explicitCampImage = isRecord(profile.camp)
? readImageSrc(profile.camp.imageSrc)
: null;
if (explicitCampImage) {
return explicitCampImage;
}
const landmarkImage = readArray(profile.landmarks)
.map((landmark) => (isRecord(landmark) ? readImageSrc(landmark.imageSrc) : null))
.find(Boolean);
if (landmarkImage) {
return landmarkImage;
}
const playableImage = readArray(profile.playableNpcs)
.map((role) => (isRecord(role) ? readImageSrc(role.imageSrc) : null))
.find(Boolean);
if (playableImage) {
return playableImage;
}
return null;
return resolveCustomWorldCoverPresentation(profile).imageSrc;
}
export function extractCustomWorldLibraryMetadata(profile: CustomWorldProfileRecord) {