- текстура, чёткость, дымка, виньетка и зерно в неразрушающем редакторе - живой WebGL2-предпросмотр эффектов и CPU-фолбэк - секции редактора свёрнуты по умолчанию, состояние в localStorage - вкладка «Редактирование» доступна только из просмотрщика
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
/**
|
|
* Состояние сворачиваемых секций редактора фото.
|
|
*
|
|
* Все секции по умолчанию свёрнуты, а выбор пользователя хранится в браузере
|
|
* (localStorage), поэтому панель открывается в том же виде после перезагрузки
|
|
* и переключения фото. Серверные настройки для этого не нужны.
|
|
*/
|
|
|
|
export const EDITOR_SECTIONS = [
|
|
'light',
|
|
'curve',
|
|
'color',
|
|
'mixer',
|
|
'pointColor',
|
|
'correction',
|
|
'effects'
|
|
] as const;
|
|
|
|
export type EditorSection = (typeof EDITOR_SECTIONS)[number];
|
|
|
|
/** true — секция свёрнута. */
|
|
export type SectionCollapse = Record<EditorSection, boolean>;
|
|
|
|
export const EDITOR_SECTIONS_KEY = 'galo.edit.sections';
|
|
|
|
/** По умолчанию все секции свёрнуты. */
|
|
export function defaultSectionCollapse(): SectionCollapse {
|
|
return {
|
|
light: true,
|
|
curve: true,
|
|
color: true,
|
|
mixer: true,
|
|
pointColor: true,
|
|
correction: true,
|
|
effects: true
|
|
};
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
}
|
|
|
|
/**
|
|
* Приводит сохранённое значение к полному набору секций: неизвестные ключи
|
|
* отбрасываются, небулевы значения и отсутствующие ключи получают дефолт.
|
|
*/
|
|
export function normalizeSectionCollapse(raw: unknown): SectionCollapse {
|
|
const defaults = defaultSectionCollapse();
|
|
if (!isRecord(raw)) return defaults;
|
|
const out = defaults;
|
|
for (const section of EDITOR_SECTIONS) {
|
|
const value = raw[section];
|
|
if (typeof value === 'boolean') out[section] = value;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
type ReadableStorage = Pick<Storage, 'getItem'>;
|
|
type WritableStorage = Pick<Storage, 'setItem'>;
|
|
|
|
/** Читает состояние секций из браузера; без хранилища — дефолт. */
|
|
export function loadSectionCollapse(
|
|
storage?: ReadableStorage | null
|
|
): SectionCollapse {
|
|
const target = storage === undefined ? browserStorage() : storage;
|
|
if (!target) return defaultSectionCollapse();
|
|
try {
|
|
const raw = target.getItem(EDITOR_SECTIONS_KEY);
|
|
if (!raw) return defaultSectionCollapse();
|
|
return normalizeSectionCollapse(JSON.parse(raw));
|
|
} catch {
|
|
return defaultSectionCollapse();
|
|
}
|
|
}
|
|
|
|
/** Сохраняет состояние секций в браузере; ошибки хранилища игнорируются. */
|
|
export function saveSectionCollapse(
|
|
state: SectionCollapse,
|
|
storage?: WritableStorage | null
|
|
): void {
|
|
const target = storage === undefined ? browserStorage() : storage;
|
|
if (!target) return;
|
|
try {
|
|
target.setItem(EDITOR_SECTIONS_KEY, JSON.stringify(state));
|
|
} catch {
|
|
// Приватный режим или переполненное хранилище не должны ломать панель.
|
|
}
|
|
}
|
|
|
|
function browserStorage(): Storage | null {
|
|
return typeof localStorage === 'undefined' ? null : localStorage;
|
|
}
|