diff --git a/src/routes/(app)/database/characters/[id]/+page.svelte b/src/routes/(app)/database/characters/[id]/+page.svelte
index 77daaaef..abed119c 100644
--- a/src/routes/(app)/database/characters/[id]/+page.svelte
+++ b/src/routes/(app)/database/characters/[id]/+page.svelte
@@ -17,10 +17,10 @@
import CharacterUncapSection from '$lib/features/database/characters/sections/CharacterUncapSection.svelte'
import CharacterTaxonomySection from '$lib/features/database/characters/sections/CharacterTaxonomySection.svelte'
import CharacterStatsSection from '$lib/features/database/characters/sections/CharacterStatsSection.svelte'
- import CharacterImagesSection from '$lib/features/database/characters/sections/CharacterImagesSection.svelte'
import EntityImagesTab from '$lib/features/database/detail/tabs/EntityImagesTab.svelte'
import EntityRawDataTab from '$lib/features/database/detail/tabs/EntityRawDataTab.svelte'
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
+ import DetailItem from '$lib/components/ui/DetailItem.svelte'
import { getCharacterImage } from '$lib/utils/images'
// Types
@@ -81,6 +81,9 @@
return getCharacterImage(character?.granblueId, 'grid', '01')
}
+ // Available image sizes for characters
+ const characterSizes = ['detail', 'grid', 'main', 'square']
+
// Generate image items for character (variants and poses based on uncap level)
const characterImages = $derived.by((): ImageItem[] => {
if (!character?.granblueId) return []
@@ -88,8 +91,7 @@
const variants = ['detail', 'grid', 'main', 'square'] as const
const images: ImageItem[] = []
- // Determine available poses based on uncap level
- // _01 = Base, _02 = MLB (3*), _03 = FLB (5*), _04 = Transcendence
+ // Only include poses that are available - _01 = Base, _02 = MLB (3*), _03 = FLB (5*), _04 = Transcendence
const poses: { id: string; label: string }[] = [
{ id: '01', label: 'Base' },
{ id: '02', label: 'MLB' }
@@ -103,19 +105,51 @@
poses.push({ id: '04', label: 'Transcendence' })
}
- for (const variant of variants) {
- for (const pose of poses) {
+ for (const pose of poses) {
+ for (const variant of variants) {
images.push({
url: getCharacterImage(character.granblueId, variant, pose.id),
label: `${variant} (${pose.label})`,
variant,
- pose: pose.id
+ pose: pose.id,
+ poseLabel: pose.label
})
}
}
return images
})
+
+ // Image download handlers
+ async function handleDownloadImage(size: string, transformation: string | undefined, force: boolean) {
+ if (!character?.id) return
+ await entityAdapter.downloadCharacterImage(character.id, size, transformation, force)
+ }
+
+ async function handleDownloadAllPose(pose: string, force: boolean) {
+ if (!character?.id) return
+ // Download all sizes for this pose
+ for (const size of characterSizes) {
+ await entityAdapter.downloadCharacterImage(character.id, size, pose, force)
+ }
+ }
+
+ async function handleDownloadAllImages(force: boolean) {
+ if (!character?.id) return
+ await entityAdapter.downloadCharacterImages(character.id, { force })
+ }
+
+ async function handleDownloadSize(size: string) {
+ if (!character?.id) return
+ // Download this size for all available poses
+ const poses = ['01', '02']
+ if (character.uncap?.flb) poses.push('03')
+ if (character.uncap?.transcendence) poses.push('04')
+
+ for (const pose of poses) {
+ await entityAdapter.downloadCharacterImage(character.id, size, pose, false)
+ }
+ }
@@ -128,6 +162,9 @@
editUrl={canEdit ? editUrl : undefined}
{currentTab}
onTabChange={handleTabChange}
+ onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
+ onDownloadSize={canEdit ? handleDownloadSize : undefined}
+ availableSizes={characterSizes}
>
{#if currentTab === 'info'}
@@ -136,12 +173,18 @@
- {#if character?.id && character?.granblueId}
-
+ {#if character.releaseDate || character.flbDate || character.ulbDate}
+
+ {#if character.releaseDate}
+
+ {/if}
+ {#if character.flbDate}
+
+ {/if}
+ {#if character.ulbDate}
+
+ {/if}
+
{/if}
{#if relatedQuery.data?.length}
@@ -162,13 +205,26 @@
{/if}
{:else if currentTab === 'images'}
-
+
{:else if currentTab === 'raw'}
{
+ const result = await entityAdapter.fetchCharacterWiki(character.id)
+ rawDataQuery.refetch()
+ return result
+ }
+ : undefined}
/>
{/if}
diff --git a/src/routes/(app)/database/summons/[id]/+page.svelte b/src/routes/(app)/database/summons/[id]/+page.svelte
index ae1b9167..e1ab1c57 100644
--- a/src/routes/(app)/database/summons/[id]/+page.svelte
+++ b/src/routes/(app)/database/summons/[id]/+page.svelte
@@ -19,6 +19,8 @@
import SummonStatsSection from '$lib/features/database/summons/sections/SummonStatsSection.svelte'
import EntityImagesTab from '$lib/features/database/detail/tabs/EntityImagesTab.svelte'
import EntityRawDataTab from '$lib/features/database/detail/tabs/EntityRawDataTab.svelte'
+ import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
+ import DetailItem from '$lib/components/ui/DetailItem.svelte'
import { getSummonImage } from '$lib/utils/images'
// Types
@@ -69,23 +71,85 @@
return getSummonImage(summon?.granblueId, 'grid')
}
+ // Available image sizes for summons
+ const summonSizes = ['detail', 'grid', 'main', 'square', 'wide']
+
// Generate image items for summon (detail, grid, main, square, wide variants)
+ // Summons have transformations: Base (no suffix), ULB (_02), Transcendence Stage 1 (_03), Transcendence Stage 5 (_04)
const summonImages = $derived.by((): ImageItem[] => {
if (!summon?.granblueId) return []
const variants = ['detail', 'grid', 'main', 'square', 'wide'] as const
const images: ImageItem[] = []
- for (const variant of variants) {
- images.push({
- url: getSummonImage(summon.granblueId, variant),
- label: variant,
- variant
- })
+ // Only include transformations that are available
+ const transformations: { id: string; label: string; suffix?: string }[] = [
+ { id: '01', label: 'Base', suffix: undefined }
+ ]
+
+ if (summon.uncap?.ulb) {
+ transformations.push({ id: '02', label: 'ULB', suffix: '02' })
+ }
+
+ if (summon.uncap?.transcendence) {
+ transformations.push(
+ { id: '03', label: 'Transcendence (1)', suffix: '03' },
+ { id: '04', label: 'Transcendence (5)', suffix: '04' }
+ )
+ }
+
+ for (const transformation of transformations) {
+ for (const variant of variants) {
+ images.push({
+ url: getSummonImage(summon.granblueId, variant, transformation.suffix),
+ label: `${variant} (${transformation.label})`,
+ variant,
+ pose: transformation.id,
+ poseLabel: transformation.label
+ })
+ }
}
return images
})
+
+ // Image download handlers
+ async function handleDownloadImage(size: string, transformation: string | undefined, force: boolean) {
+ if (!summon?.id) return
+ // For summons, '01' means base (no transformation suffix)
+ const trans = transformation === '01' ? undefined : transformation
+ await entityAdapter.downloadSummonImage(summon.id, size, trans, force)
+ }
+
+ async function handleDownloadAllPose(pose: string, force: boolean) {
+ if (!summon?.id) return
+ const trans = pose === '01' ? undefined : pose
+ // Download all sizes for this pose
+ for (const size of summonSizes) {
+ await entityAdapter.downloadSummonImage(summon.id, size, trans, force)
+ }
+ }
+
+ async function handleDownloadAllImages(force: boolean) {
+ if (!summon?.id) return
+ await entityAdapter.downloadSummonImages(summon.id, { force })
+ }
+
+ async function handleDownloadSize(size: string) {
+ if (!summon?.id) return
+ // Download this size for all available transformations
+ const transformations: (string | undefined)[] = [undefined]
+ if (summon.uncap?.ulb) {
+ transformations.push('02')
+ }
+ if (summon.uncap?.transcendence) {
+ transformations.push('03', '04')
+ }
+
+ for (const trans of transformations) {
+ await entityAdapter.downloadSummonImage(summon.id, size, trans, false)
+ }
+ }
@@ -98,6 +162,9 @@
editUrl={canEdit ? editUrl : undefined}
{currentTab}
onTabChange={handleTabChange}
+ onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
+ onDownloadSize={canEdit ? handleDownloadSize : undefined}
+ availableSizes={summonSizes}
>
{#if currentTab === 'info'}
{/if}
+
+ {#if summon.releaseDate || summon.flbDate || summon.ulbDate || summon.transcendenceDate}
+
+ {#if summon.releaseDate}
+
+ {/if}
+ {#if summon.flbDate}
+
+ {/if}
+ {#if summon.ulbDate}
+
+ {/if}
+ {#if summon.transcendenceDate}
+
+ {/if}
+
+ {/if}
{:else if currentTab === 'images'}
-
+
{:else if currentTab === 'raw'}
{
+ const result = await entityAdapter.fetchSummonWiki(summon.id)
+ rawDataQuery.refetch()
+ return result
+ }
+ : undefined}
/>
{/if}
diff --git a/src/routes/(app)/database/weapons/[id]/+page.svelte b/src/routes/(app)/database/weapons/[id]/+page.svelte
index a94737fc..311537b2 100644
--- a/src/routes/(app)/database/weapons/[id]/+page.svelte
+++ b/src/routes/(app)/database/weapons/[id]/+page.svelte
@@ -19,6 +19,8 @@
import WeaponStatsSection from '$lib/features/database/weapons/sections/WeaponStatsSection.svelte'
import EntityImagesTab from '$lib/features/database/detail/tabs/EntityImagesTab.svelte'
import EntityRawDataTab from '$lib/features/database/detail/tabs/EntityRawDataTab.svelte'
+ import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
+ import DetailItem from '$lib/components/ui/DetailItem.svelte'
import { getWeaponGridImage, getWeaponImage as getWeaponImageUrl } from '$lib/utils/images'
// Types
@@ -69,23 +71,78 @@
return getWeaponGridImage(weapon?.granblueId, weapon?.element, weapon?.instanceElement)
}
+ // Available image sizes for weapons
+ const weaponSizes = ['base', 'grid', 'main', 'square']
+
// Generate image items for weapon (base, grid, main, square variants)
+ // Weapons have transformations: Base (no suffix), Transcendence Stage 1 (_02), Transcendence Stage 5 (_03)
const weaponImages = $derived.by((): ImageItem[] => {
if (!weapon?.granblueId) return []
const variants = ['base', 'grid', 'main', 'square'] as const
const images: ImageItem[] = []
- for (const variant of variants) {
- images.push({
- url: getWeaponImageUrl(weapon.granblueId, variant),
- label: variant,
- variant
- })
+ // Only include transformations that are available
+ const transformations: { id: string; label: string; suffix?: string }[] = [
+ { id: '01', label: 'Base', suffix: undefined }
+ ]
+
+ if (weapon.uncap?.transcendence) {
+ transformations.push(
+ { id: '02', label: 'Transcendence (1)', suffix: '02' },
+ { id: '03', label: 'Transcendence (5)', suffix: '03' }
+ )
+ }
+
+ for (const transformation of transformations) {
+ for (const variant of variants) {
+ images.push({
+ url: getWeaponImageUrl(weapon.granblueId, variant, undefined, transformation.suffix),
+ label: `${variant} (${transformation.label})`,
+ variant,
+ pose: transformation.id,
+ poseLabel: transformation.label
+ })
+ }
}
return images
})
+
+ // Image download handlers
+ async function handleDownloadImage(size: string, transformation: string | undefined, force: boolean) {
+ if (!weapon?.id) return
+ // For weapons, '01' means base (no transformation suffix)
+ const trans = transformation === '01' ? undefined : transformation
+ await entityAdapter.downloadWeaponImage(weapon.id, size, trans, force)
+ }
+
+ async function handleDownloadAllPose(pose: string, force: boolean) {
+ if (!weapon?.id) return
+ const trans = pose === '01' ? undefined : pose
+ // Download all sizes for this pose
+ for (const size of weaponSizes) {
+ await entityAdapter.downloadWeaponImage(weapon.id, size, trans, force)
+ }
+ }
+
+ async function handleDownloadAllImages(force: boolean) {
+ if (!weapon?.id) return
+ await entityAdapter.downloadWeaponImages(weapon.id, { force })
+ }
+
+ async function handleDownloadSize(size: string) {
+ if (!weapon?.id) return
+ // Download this size for all available transformations
+ const transformations: (string | undefined)[] = [undefined]
+ if (weapon.uncap?.transcendence) {
+ transformations.push('02', '03')
+ }
+
+ for (const trans of transformations) {
+ await entityAdapter.downloadWeaponImage(weapon.id, size, trans, false)
+ }
+ }
@@ -98,6 +155,9 @@
editUrl={canEdit ? editUrl : undefined}
{currentTab}
onTabChange={handleTabChange}
+ onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
+ onDownloadSize={canEdit ? handleDownloadSize : undefined}
+ availableSizes={weaponSizes}
>
{#if currentTab === 'info'}
@@ -123,15 +183,45 @@
{/if}
+
+ {#if weapon.releaseDate || weapon.flbDate || weapon.ulbDate || weapon.transcendenceDate}
+
+ {#if weapon.releaseDate}
+
+ {/if}
+ {#if weapon.flbDate}
+
+ {/if}
+ {#if weapon.ulbDate}
+
+ {/if}
+ {#if weapon.transcendenceDate}
+
+ {/if}
+
+ {/if}
{:else if currentTab === 'images'}
-
+
{:else if currentTab === 'raw'}
{
+ const result = await entityAdapter.fetchWeaponWiki(weapon.id)
+ rawDataQuery.refetch()
+ return result
+ }
+ : undefined}
/>
{/if}