diff --git a/src/lib/features/database/detail/DetailScaffold.svelte b/src/lib/features/database/detail/DetailScaffold.svelte index 2190d99c..fc7be0fa 100644 --- a/src/lib/features/database/detail/DetailScaffold.svelte +++ b/src/lib/features/database/detail/DetailScaffold.svelte @@ -1,9 +1,11 @@
@@ -74,6 +113,48 @@ Images Raw Data + + {#if showDownloadDropdown} +
+ + + {#snippet child({ props })} + + {/snippet} + + + + + handleDownloadAll(false)} + > + Download All Images + + handleDownloadAll(true)} + > + Re-download All Images + + {#if availableSizes.length > 0} + + {#each availableSizes as size} + handleDownloadSize(size)} + > + Download All "{size}" Images + + {/each} + {/if} + + + +
+ {/if}
{/if} @@ -109,6 +190,15 @@ .tab-navigation { padding: spacing.$unit-2x; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: spacing.$unit; + } + + .download-dropdown { + margin-left: auto; } .edit-controls { @@ -139,4 +229,36 @@ opacity: 1; } } + + // Import menu styles + :global(.dropdown-menu) { + background: var(--app-bg, white); + border: 1px solid var(--border-color, #ddd); + border-radius: layout.$card-corner; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + padding: spacing.$unit-half; + min-width: calc(spacing.$unit * 22.5); + z-index: 200; + } + + :global(.dropdown-menu-item) { + padding: spacing.$unit spacing.$unit-2x; + border-radius: layout.$item-corner-small; + cursor: pointer; + font-size: typography.$font-regular; + color: var(--text-primary); + display: flex; + align-items: center; + gap: spacing.$unit; + + &:hover { + background: var(--button-contained-bg-hover, #f5f5f5); + } + } + + :global(.dropdown-menu-separator) { + height: 1px; + background: var(--menu-separator, #e5e5e5); + margin: spacing.$unit-half 0; + } diff --git a/src/lib/features/database/detail/tabs/EntityImagesTab.svelte b/src/lib/features/database/detail/tabs/EntityImagesTab.svelte index c568e4a1..967f1494 100644 --- a/src/lib/features/database/detail/tabs/EntityImagesTab.svelte +++ b/src/lib/features/database/detail/tabs/EntityImagesTab.svelte @@ -1,31 +1,205 @@
-
- {#each images as image} -
- - {image.label} - - {image.label} -
- {/each} -
+ {#each sortedPoses as pose} + {@const poseImages = imagesByPose.get(pose) ?? []} + {@const poseLabel = getPoseLabel(pose, poseImages)} + {@const showHeader = poseLabel && sortedPoses.length > 1} + + {#if showHeader} +

{poseLabel}

+ {/if} + +
+ {#each poseImages as image} + {@const imageKey = getImageKey(image)} + {@const isDownloading = downloadingImages.has(imageKey)} + + {#if canEdit && onDownloadImage} + + {#snippet trigger()} + + {/snippet} + + {#snippet menu()} + handleDownload(image, false)} + disabled={isDownloading} + > + Download Image + + handleDownload(image, true)} + disabled={isDownloading} + > + Re-download Image + + {#if onDownloadAllPose} + + handleDownloadAllPose(pose, false)} + > + Download All {poseLabel} Images + + {/if} + + window.open(image.url, '_blank')} + > + Open in New Tab + + {/snippet} + + {:else} +
+ + {image.label} + + {image.variant} +
+ {/if} + {/each} +
+ {/each}
diff --git a/src/lib/utils/images.ts b/src/lib/utils/images.ts index 1baaa1f5..5cedadcd 100644 --- a/src/lib/utils/images.ts +++ b/src/lib/utils/images.ts @@ -119,13 +119,33 @@ export function getCharacterDetailImage( /** * Get weapon image URL + * @param transformation - Optional transformation suffix ('02' for transcendence) */ export function getWeaponImage( id: string | number | null | undefined, variant: ImageVariant = 'main', - element?: number + element?: number, + transformation?: string ): string { - return getImageUrl('weapon', id, variant, { element }) + if (!id) { + return getPlaceholderImage('weapon', variant) + } + + const directory = getImageDirectory('weapon', variant) + const extension = getFileExtension('weapon', variant) + const basePath = `${getBasePath()}/${directory}` + + // Handle element-specific weapon grids + if (variant === 'grid' && element && element > 0) { + return `${basePath}/${id}_${element}${extension}` + } + + // Handle transformation suffix (transcendence) + if (transformation) { + return `${basePath}/${id}_${transformation}${extension}` + } + + return `${basePath}/${id}${extension}` } /** @@ -139,12 +159,27 @@ export function getWeaponBaseImage( /** * Get summon image URL + * @param transformation - Optional transformation suffix ('02' for ULB, '03' for transcendence) */ export function getSummonImage( id: string | number | null | undefined, - variant: ImageVariant = 'main' + variant: ImageVariant = 'main', + transformation?: string ): string { - return getImageUrl('summon', id, variant) + if (!id) { + return getPlaceholderImage('summon', variant) + } + + const directory = getImageDirectory('summon', variant) + const extension = getFileExtension('summon', variant) + const basePath = `${getBasePath()}/${directory}` + + // Handle transformation suffix (ULB, transcendence) + if (transformation) { + return `${basePath}/${id}_${transformation}${extension}` + } + + return `${basePath}/${id}${extension}` } /**