From 19a2b9e77f521a501bfa4db4e10bf8db90ab439f Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Mon, 1 Dec 2025 02:26:08 -0800 Subject: [PATCH] utils: use getBasePath for image URLs --- src/lib/features/database/detail/image.ts | 31 +---- src/lib/utils/element.ts | 4 +- src/lib/utils/gender.ts | 4 +- src/lib/utils/images.ts | 156 +++++++++++++++++++++- src/lib/utils/proficiency.ts | 4 +- src/lib/utils/race.ts | 4 +- 6 files changed, 173 insertions(+), 30 deletions(-) diff --git a/src/lib/features/database/detail/image.ts b/src/lib/features/database/detail/image.ts index 2d443758..16a51c1b 100644 --- a/src/lib/features/database/detail/image.ts +++ b/src/lib/features/database/detail/image.ts @@ -4,13 +4,17 @@ export { getCharacterImage, getWeaponImage, getSummonImage, - getImageUrl as getImageUrlBase + getImageUrl as getImageUrlBase, + getBasePath } from '$lib/utils/images' // Import types for local use and re-export import type { ResourceType, ImageVariant } from '$lib/utils/images' export type { ResourceType as ResourceKind, ImageVariant } +// Import the base function for the legacy wrapper +import { getImageUrl as getImageUrlFromUtils } from '$lib/utils/images' + // Legacy compatibility wrapper interface ImageArgs { type: 'character' | 'weapon' | 'summon' @@ -21,28 +25,5 @@ interface ImageArgs { } export function getImageUrl({ type, id, variant, element, pose }: ImageArgs): string { - // Import the base function to avoid circular dependency - import('$lib/utils/images').then(({ getImageUrl: getImageUrlFromUtils }) => { - return getImageUrlFromUtils(type, id, variant, { pose, element }) - }) - - // Temporary direct implementation for sync compatibility - if (!id) return `/images/placeholders/placeholder-${type}-${variant}.png` - - const directory = `${type}-${variant}` - const extension = ( - (type === 'character' && variant === 'detail') || - (type === 'weapon' && variant === 'base') || - (type === 'summon' && variant === 'detail') - ) ? '.png' : '.jpg' - - if (type === 'character') { - return `/images/${directory}/${id}_${pose || '01'}${extension}` - } - - if (type === 'weapon' && variant === 'grid' && element && element > 0) { - return `/images/${directory}/${id}_${element}${extension}` - } - - return `/images/${directory}/${id}${extension}` + return getImageUrlFromUtils(type, id, variant, { pose, element }) } diff --git a/src/lib/utils/element.ts b/src/lib/utils/element.ts index 3dcec7cf..184212f1 100644 --- a/src/lib/utils/element.ts +++ b/src/lib/utils/element.ts @@ -1,3 +1,5 @@ +import { getBasePath } from '$lib/utils/images' + interface ElementData { en: string ja: string @@ -41,7 +43,7 @@ export function getElementIcon(element?: number): string { if (label === '—' || label === 'Null') return '' // Capitalize first letter for filename const capitalizedLabel = label.charAt(0).toUpperCase() + label.slice(1) - return `/images/labels/element/Label_Element_${capitalizedLabel}.png` + return `${getBasePath()}/labels/element/Label_Element_${capitalizedLabel}.png` } export function getElementOptions() { diff --git a/src/lib/utils/gender.ts b/src/lib/utils/gender.ts index 75aea82e..9263629b 100644 --- a/src/lib/utils/gender.ts +++ b/src/lib/utils/gender.ts @@ -2,6 +2,8 @@ * Gender mapping utilities for Granblue Fantasy */ +import { getBasePath } from '$lib/utils/images' + export const GENDER_LABELS: Record = { 0: 'Unknown', 1: 'Male', @@ -18,7 +20,7 @@ export function getGenderIcon(gender?: number | null): string { const label = getGenderLabel(gender) if (label === '—' || label === 'Unknown') return '' // Gender icons may use different naming convention - return `/images/labels/gender/Label_Gender_${label.replace('/', '_')}.png` + return `${getBasePath()}/labels/gender/Label_Gender_${label.replace('/', '_')}.png` } export function getGenderOptions() { diff --git a/src/lib/utils/images.ts b/src/lib/utils/images.ts index 00759843..1baaa1f5 100644 --- a/src/lib/utils/images.ts +++ b/src/lib/utils/images.ts @@ -1,7 +1,12 @@ /** * Centralized image utility system for all game assets + * + * Supports both local images (development) and remote AWS S3/CDN images (production) + * Configure via PUBLIC_SIERO_IMG_URL environment variable */ +import { getImageBaseUrl } from '$lib/api/adapters/config' + export type ResourceType = 'character' | 'weapon' | 'summon' export type ImageVariant = 'main' | 'grid' | 'square' | 'detail' | 'base' | 'wide' @@ -26,8 +31,18 @@ function getFileExtension(type: ResourceType, variant: ImageVariant): string { return '.jpg' } +/** + * Gets the base path for images + * Returns AWS S3/CDN URL if configured, otherwise local /images path + */ +export function getBasePath(): string { + const remoteUrl = getImageBaseUrl() + return remoteUrl || '/images' +} + /** * Gets the placeholder image for a given type and variant + * Placeholders are always served locally */ export function getPlaceholderImage(type: ResourceType, variant: ImageVariant): string { return `/images/placeholders/placeholder-${type}-${variant}.png` @@ -62,7 +77,7 @@ export function getImageUrl( const directory = getImageDirectory(type, variant) const extension = getFileExtension(type, variant) - const basePath = `/images/${directory}` + const basePath = `${getBasePath()}/${directory}` // Handle character-specific logic if (type === 'character') { @@ -192,3 +207,142 @@ export function getWeaponGridImage( } return getImageUrl('weapon', id, 'grid') } + +// ===== Job-Related Images ===== + +/** + * Get job skill icon URL + */ +export function getJobSkillIcon(slug: string | undefined): string { + if (!slug) return '/images/job-skills/default.png' + return `${getBasePath()}/job-skills/${slug}.png` +} + +/** + * Get accessory square image URL + */ +export function getAccessoryImage(granblueId: string | undefined): string { + if (!granblueId) return '/images/placeholders/placeholder-weapon-grid.png' + return `${getBasePath()}/accessory-square/${granblueId}.jpg` +} + +// ===== Modification Images ===== + +/** + * Get awakening image URL + */ +export function getAwakeningImage(slug: string | undefined, extension: 'png' | 'jpg' = 'jpg'): string { + if (!slug) return '' + return `${getBasePath()}/awakening/${slug}.${extension}` +} + +/** + * Get weapon key image URL + */ +export function getWeaponKeyImage(slug: string, element?: number): string { + const basePath = `${getBasePath()}/weapon-keys` + + // Check if this key type needs element suffix + if (element && isElementalWeaponKey(slug)) { + return `${basePath}/${slug}-${element}.png` + } + return `${basePath}/${slug}.png` +} + +/** + * Check if weapon key slug requires element suffix + */ +function isElementalWeaponKey(slug: string): boolean { + const elementalKeys = [ + 'elemental-teluma', + 'pendulum', + 'chain-of-causality', + 'ultima' + ] + return elementalKeys.some((key) => slug.includes(key)) +} + +/** + * Get AX skill image URL + */ +export function getAxSkillImage(slug: string | undefined): string { + if (!slug) return '' + return `${getBasePath()}/ax/${slug}.png` +} + +/** + * Get mastery image URL + */ +export function getMasteryImage(slug: string | undefined): string { + if (!slug) return '' + return `${getBasePath()}/mastery/${slug}.png` +} + +// ===== Label Images ===== + +/** + * Get element label image URL + */ +export function getElementLabelImage(elementName: string): string { + const capitalizedLabel = elementName.charAt(0).toUpperCase() + elementName.slice(1).toLowerCase() + return `${getBasePath()}/labels/element/Label_Element_${capitalizedLabel}.png` +} + +/** + * Get proficiency label image URL + */ +export function getProficiencyLabelImage(proficiencyName: string): string { + const capitalizedLabel = + proficiencyName.charAt(0).toUpperCase() + proficiencyName.slice(1).toLowerCase() + return `${getBasePath()}/labels/proficiency/Label_Weapon_${capitalizedLabel}.png` +} + +/** + * Get race label image URL + */ +export function getRaceLabelImage(raceName: string): string { + return `${getBasePath()}/labels/race/Label_Race_${raceName}.png` +} + +/** + * Get gender label image URL + */ +export function getGenderLabelImage(genderLabel: string): string { + return `${getBasePath()}/labels/gender/Label_Gender_${genderLabel.replace('/', '_')}.png` +} + +// ===== Element Icons ===== + +/** + * Get element icon image URL (for select dropdowns, etc.) + */ +export function getElementIcon(element: number): string { + const elementNames: Record = { + 1: 'wind', + 2: 'fire', + 3: 'water', + 4: 'earth', + 5: 'dark', + 6: 'light' + } + const name = elementNames[element] || 'none' + return `${getBasePath()}/elements/element-${name}.png` +} + +// ===== Other Game Images ===== + +/** + * Get guidebook image URL + */ +export function getGuidebookImage(granblueId: string | number | undefined): string { + if (!granblueId) return '/images/placeholders/placeholder-weapon-grid.png' + return `${getBasePath()}/guidebooks/book_${granblueId}.png` +} + +/** + * Get raid image URL + */ +export function getRaidImage(slug: string | undefined): string { + if (!slug) return '/images/placeholders/placeholder-weapon-grid.png' + return `${getBasePath()}/raids/${slug}.png` +} diff --git a/src/lib/utils/proficiency.ts b/src/lib/utils/proficiency.ts index 1e83cc91..ef6067f7 100644 --- a/src/lib/utils/proficiency.ts +++ b/src/lib/utils/proficiency.ts @@ -1,3 +1,5 @@ +import { getBasePath } from '$lib/utils/images' + export const PROFICIENCY_LABELS: Record = { 0: 'None', 1: 'Sabre', @@ -21,7 +23,7 @@ export function getProficiencyIcon(proficiency: number): string { if (!label || label === 'None') return '' // Capitalize first letter for filename const capitalizedLabel = label.charAt(0).toUpperCase() + label.slice(1) - return `/images/labels/proficiency/Label_Weapon_${capitalizedLabel}.png` + return `${getBasePath()}/labels/proficiency/Label_Weapon_${capitalizedLabel}.png` } export function getProficiencyOptions() { diff --git a/src/lib/utils/race.ts b/src/lib/utils/race.ts index 0aac4247..1c87903b 100644 --- a/src/lib/utils/race.ts +++ b/src/lib/utils/race.ts @@ -2,6 +2,8 @@ * Race mapping utilities for Granblue Fantasy */ +import { getBasePath } from '$lib/utils/images' + export const RACE_LABELS: Record = { 0: 'Unknown', 1: 'Human', @@ -20,7 +22,7 @@ export function getRaceLabel(race?: number | null): string { export function getRaceIcon(race?: number | null): string { const label = getRaceLabel(race) if (label === '—' || label === 'Unknown') return '' - return `/images/labels/race/Label_Race_${label}.png` + return `${getBasePath()}/labels/race/Label_Race_${label}.png` } export function getRaceOptions() {