feat: image variants per image

This commit is contained in:
fsylvain committed 2026-10-08 19:57:51 +02:00
1 parent f567866161
commit 951fef18ba
92 files changed
+8695 -413

No files matched your search

+105 -2
View File
@@ -1,7 +1,9 @@
import { vi } from 'vitest'
import { defineComponent, h } from 'vue'
import { mount, flushPromises, type VueWrapper } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import uiPlugin from '@nuxt/ui/vue-plugin'
import UApp from '@nuxt/ui/components/App.vue'
import router from '@/router'
import { ApiError, useApi } from '@/composables/useApi'
import type { MediaItem } from '@/composables/useMedia'
@@ -21,6 +23,8 @@ type Row = [
string,
boolean,
string,
number,
number,
]
const ROWS: Row[] = [
@@ -36,6 +40,8 @@ const ROWS: Row[] = [
'2026-09-12T12:00:00Z',
false,
'0a',
4,
266240,
],
[
2,
@@ -49,6 +55,8 @@ const ROWS: Row[] = [
'2026-09-08T12:00:00Z',
false,
'1b',
5,
419840,
],
[
3,
@@ -62,6 +70,8 @@ const ROWS: Row[] = [
'2026-09-08T11:00:00Z',
false,
'2c',
5,
404480,
],
[
4,
@@ -75,6 +85,8 @@ const ROWS: Row[] = [
'2026-09-05T12:00:00Z',
false,
'3d',
0,
0,
],
[
5,
@@ -88,6 +100,8 @@ const ROWS: Row[] = [
'2026-09-02T12:00:00Z',
true,
'0e',
3,
245760,
],
[
6,
@@ -101,6 +115,8 @@ const ROWS: Row[] = [
'2026-08-30T12:00:00Z',
true,
'0e',
0,
0,
],
[
7,
@@ -114,6 +130,8 @@ const ROWS: Row[] = [
'2026-08-29T12:00:00Z',
true,
'2f',
2,
153600,
],
[
8,
@@ -127,6 +145,8 @@ const ROWS: Row[] = [
'2026-08-28T12:00:00Z',
false,
'3a',
0,
0,
],
[
9,
@@ -140,6 +160,8 @@ const ROWS: Row[] = [
'2026-08-21T12:00:00Z',
true,
'2f',
1,
61440,
],
[
10,
@@ -153,6 +175,8 @@ const ROWS: Row[] = [
'2026-08-04T12:00:00Z',
false,
'1b',
3,
194560,
],
[
11,
@@ -166,6 +190,8 @@ const ROWS: Row[] = [
'2026-07-19T12:00:00Z',
false,
'1c',
4,
286720,
],
[
12,
@@ -179,6 +205,8 @@ const ROWS: Row[] = [
'2026-06-14T12:00:00Z',
true,
'0e',
5,
440320,
],
]
@@ -199,6 +227,8 @@ export function fixture(ids?: number[]): MediaItem[] {
created_at,
duplicate,
sha256,
variant_count,
variant_size,
]) => ({
id,
filename,
@@ -212,6 +242,8 @@ export function fixture(ids?: number[]): MediaItem[] {
created_at,
used_in,
duplicate,
variant_count,
variant_size,
}),
)
}
@@ -238,6 +270,10 @@ export interface Handlers {
merge?: (body: { keep_id: number; merge_ids: number[] }) => unknown
preview?: () => unknown
purge?: (body: { fingerprint: string }) => unknown
variants?: (id: number) => unknown
generateAll?: (id: number) => unknown
generate?: (id: number, name: string) => unknown
purgeVariant?: (id: number, name: string) => unknown
}
export function libraryHandlers(
@@ -282,6 +318,28 @@ function dispatcher(handlers: Handlers) {
if (method === 'POST' && path === '/v1/media/purge') {
return (handlers.purge ?? (() => undefined))(JSON.parse(String(options?.body)))
}
const variantsList = /^\/v1\/media\/(\d+)\/variants$/.exec(path)
if (variantsList && method === 'GET') {
return (handlers.variants ?? (() => ({ unsupported: null, items: [] })))(
Number(variantsList[1]),
)
}
if (variantsList && method === 'POST') {
return (handlers.generateAll ?? (() => undefined))(Number(variantsList[1]))
}
const variantNamed = /^\/v1\/media\/(\d+)\/variants\/([\w-]+)$/.exec(path)
if (variantNamed && method === 'POST') {
return (handlers.generate ?? (() => undefined))(
Number(variantNamed[1]),
variantNamed[2]!,
)
}
if (variantNamed && method === 'DELETE') {
return (handlers.purgeVariant ?? (() => undefined))(
Number(variantNamed[1]),
variantNamed[2]!,
)
}
const usage = /^\/v1\/media\/(\d+)\/usage$/.exec(path)
if (method === 'GET' && usage) {
return (handlers.usage ?? (() => []))(Number(usage[1]))
@@ -324,7 +382,7 @@ export async function mountPage(
handlers: Handlers = {},
url = '/media',
width = DESKTOP_WIDTH,
options: { realApi?: boolean; role?: string } = {},
options: { realApi?: boolean; role?: string; app?: boolean } = {},
) {
setViewportWidth(width)
setActivePinia(createPinia())
@@ -349,7 +407,10 @@ export async function mountPage(
})
}
await router.push(url)
kit.wrapper = mount(MediaView, {
const root = options.app
? defineComponent({ render: () => h(UApp, null, { default: () => h(MediaView) }) })
: MediaView
kit.wrapper = mount(root, {
attachTo: document.body,
global: { plugins: [router, uiPlugin] },
})
@@ -599,3 +660,45 @@ export function uploadRows() {
export function rowTexts() {
return uploadRows().map(textOf)
}
export const STORED_NAME = '1.jpg'
export const NORTH_WALL_BYTES = 4089446
const VARIANT_SPECS: [
string,
number | null,
number | null,
string,
string,
number,
number,
number,
][] = [
['card', 800, null, 'inside', 'webp', 800, 600, 65536],
['hero', 1200, 630, 'cover', 'webp', 1200, 630, 98304],
['og', 1200, 630, 'cover', 'jpeg', 1200, 630, 145408],
['square', 600, 600, 'cover', 'jpeg', 600, 600, 83968],
['thumb', 400, 300, 'cover', 'webp', 400, 300, 18432],
]
export function variantItems(generated: string[] = VARIANT_SPECS.map(([name]) => name)) {
return VARIANT_SPECS.map(
([name, width, height, fit, format, outputWidth, outputHeight, size]) => ({
name,
width,
height,
fit,
format,
output_width: outputWidth,
output_height: outputHeight,
output_format: format,
generated: generated.includes(name),
size: generated.includes(name) ? size : null,
url: `/static/uploadedImages/${STORED_NAME}?v=${name}`,
}),
)
}
export function variantList(generated?: string[]) {
return { unsupported: null, items: variantItems(generated) }
}