/** * Which groups of rows are folded (#290), kept in this browser: a group folded once stays folded * for every selection that shows it, and after a reload. Best effort, like the saves – a browser * that refuses `localStorage` forgets with the page. */ import { Theme } from '../ui/Theme'; const STORAGE_KEY = 'game-maker.folded-sections'; let folded: Set | null = null; function storage(): Pick | null { try { return typeof window === 'undefined' ? null : window.localStorage; } catch { return null; } } function read(): Set { try { const stored: unknown = JSON.parse(storage()?.getItem(STORAGE_KEY) ?? '[]'); return new Set(Array.isArray(stored) ? stored.filter((key) => typeof key === 'string') : []); } catch { return new Set(); } } /** * Names a group: the panel it stands in and its title, numbers left out – "3 OBJECTS SELECTED" and * "KEY AT FRAME 12" are the same group whatever they count. */ export function foldKey(scope: string, title: string): string { return `${scope}:${title.replace(/\d+/g, '#')}`; } export function isFolded(key: string): boolean { folded ??= read(); return folded.has(key); } export function setFolded(key: string, on: boolean): void { folded ??= read(); if (on) folded.add(key); else folded.delete(key); try { storage()?.setItem(STORAGE_KEY, JSON.stringify([...folded].sort())); } catch { // Kept for this page only. } } /** * What a list with a header of its own takes to fold like the groups of the column it stands in: * the band reaches across the column's padding, and the fold is remembered under the list's title. */ export function listFold( scope: string, title: string, ): { bleed: number; collapsed: boolean; onFold: (collapsed: boolean) => void } { const key = foldKey(scope, title.toUpperCase()); return { bleed: Theme.padding, collapsed: isFolded(key), onFold: (on) => setFolded(key, on) }; } /** Forgets what was read, so the next question asks the browser again (tests). */ export function resetFolds(): void { folded = null; }