feat(web): вкладка «Редактирование» с инструментом «Свет»

В правой панели фото появилась вкладка «Редактирование» с инструментом «Свет»:
ползунки экспозиции, контраста, светов, теней, белых и чёрных и точечная кривая
с выбором канала. Правки сохраняются автоматически, переключатель «Сброс»
удаляет параметры правок и отредактированную версию фото.

Редактирование неразрушающее: параметры хранятся в метаданных фото, а
отредактированная версия рендерится в фоне отдельным объектом (edit, edit_thumb,
edit_medium, edit_preview) рядом с исходными вариантами. Оригинал не меняется,
скачивание отдаёт исходный файл. Сетка, просмотр и обложки альбомов показывают
отредактированную версию, когда она отрендерена.
This commit is contained in:
iu
2026-10-20 23:06:24 +03:00
parent afaaaab5f0
commit 0548b1ab5e
32 changed files with 1954 additions and 24 deletions
@@ -145,6 +145,11 @@
// insert an unseen photo here, or the grid would reflow.
const photo = data as unknown as Photo;
photos = photos.map((p) => (p.id === photo.id ? photo : p));
// Обновлённое фото (например, отрендеренные правки) должно
// попасть и в правую панель.
if (selection.albumId === albumId && selection.has(photo.id)) {
selection.replace(photo);
}
break;
}
case 'like.added': {
@@ -9,6 +9,8 @@
import { photoUpdates } from '$lib/stores/photo-updates.svelte';
import { trash } from '$lib/stores/trash.svelte';
import { viewerReturn } from '$lib/stores/viewer-return';
import { auth } from '$lib/stores/auth.svelte';
import { SSEClient } from '$lib/sse';
import type { Album, Photo } from '$lib/types';
const albumId = $derived($page.params.id ?? '');
@@ -55,6 +57,27 @@
}
});
// Обновления альбома: отрендеренные правки приходят отдельным событием.
let client: SSEClient | null = null;
$effect(() => {
const id = albumId;
if (!auth.access) return;
client = new SSEClient({
path: `/api/sse/albums/${id}`,
onEvent: (ev) => {
if (ev.type !== 'photo.updated') return;
const updated = ev.data as unknown as Photo;
photos = photos.map((p) => (p.id === updated.id ? updated : p));
}
});
client.connect(auth.access);
return () => {
client?.close();
client = null;
};
});
function close() {
goto(`/albums/${albumId}`);
}