feat: image variants settings screens

This commit is contained in:
fsylvain committed 2026-10-08 13:57:41 +02:00
1 parent f1d56ef50e
commit f567866161
42 files changed
+7192 -11

No files matched your search

@@ -0,0 +1,111 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { computed, ref } from 'vue'
import ImageCompare from '@/components/variants/ImageCompare.vue'
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
import { SAMPLE_SOURCE, estimateLabel, estimateVariant } from '@/lib/variants/variantEstimate'
import { parseDimension, sliderValue, type VariantFormValues } from '@/lib/variants/variantForm'
import { formatVariantSize } from '@/lib/variants/variantFormat'
const URL_PREFIX = '/static/uploadedImages/'
const NAME_PLACEHOLDER = '<name>'
const props = defineProps<{
values: VariantFormValues
disabled: boolean
}>()
const position = ref(50)
const { copied, copy } = useCopyToClipboard()
const dimensionsAreValid = computed(
() =>
(props.values.width.trim() === '' || parseDimension(props.values.width) !== null) &&
(props.values.height.trim() === '' || parseDimension(props.values.height) !== null),
)
const estimate = computed(() =>
dimensionsAreValid.value
? estimateVariant({
width: parseDimension(props.values.width),
height: parseDimension(props.values.height),
fit: props.values.fit,
format: props.values.format,
quality: sliderValue(props.values.quality),
})
: null,
)
const shownName = computed(() => props.values.name || t('name'))
const outputLine = computed(() =>
estimate.value ? estimateLabel(estimate.value) : t('No output yet'),
)
const originalLine = computed(
() =>
`${SAMPLE_SOURCE.width} × ${SAMPLE_SOURCE.height} · ${formatVariantSize(SAMPLE_SOURCE.bytes)}`,
)
const hasName = computed(() => props.values.name !== '')
const url = computed(
() => `${URL_PREFIX}${SAMPLE_SOURCE.filename}?v=${props.values.name || NAME_PLACEHOLDER}`,
)
async function onCopy() {
try {
await copy(url.value)
} catch {
return
}
}
</script>
<template>
<div class="flex min-w-0 flex-col gap-3">
<div class="flex items-baseline gap-2">
<h3 class="flex-1 font-semibold text-highlighted">{{ t('Preview') }}</h3>
<span class="text-xs/5 text-muted">{{
t('Sample: {0}', [SAMPLE_SOURCE.filename])
}}</span>
</div>
<ImageCompare v-model="position" :name="values.name" :comparable="estimate !== null" />
<div class="grid grid-cols-2 gap-3">
<div class="flex flex-col">
<span class="font-medium text-highlighted">{{ t('Original') }}</span>
<span class="text-xs/5 text-muted">{{ originalLine }}</span>
</div>
<div class="flex flex-col">
<span class="font-mono font-medium text-highlighted">{{ shownName }}</span>
<span class="text-xs/5 text-muted">{{ outputLine }}</span>
</div>
</div>
<p class="text-xs/5 text-muted">
{{
t(
'Drag the divider, or focus it and use the arrow keys. The estimate comes from a sample; output is never larger than the source.',
)
}}
</p>
<div class="flex flex-col gap-1.5">
<span class="font-medium text-highlighted">{{ t('URL') }}</span>
<div class="flex gap-2">
<div
data-testid="variant-url"
class="flex min-h-11 min-w-0 flex-1 items-center truncate border border-default bg-elevated px-3 font-mono text-xs sm:min-h-8"
:class="hasName ? 'text-highlighted' : 'text-muted'"
>
{{ url }}
</div>
<UButton
color="neutral"
variant="outline"
:icon="copied ? 'i-lucide-check' : 'i-lucide-copy'"
:disabled="!hasName || disabled"
class="h-11 shrink-0 gap-1.5 border border-accented bg-default px-3 text-sm text-highlighted ring-0 sm:h-8"
@click="onCopy"
>
{{ copied ? t('Copied') : t('Copy') }}
</UButton>
</div>
</div>
</div>
</template>