67 lines
2.8 KiB
TypeScript
67 lines
2.8 KiB
TypeScript
import { getLocalStorage, setLocalStorage } from "@/lib/local-storage";
|
|
import { STORAGE_KEYS } from "@/lib/settings/constants";
|
|
|
|
/**
|
|
* How the settings menu renders — a per-device preference.
|
|
*
|
|
* Deliberately *not* part of user settings: the menu belongs to the surface
|
|
* you are looking at, not to the account. A phone reaching the same install
|
|
* can choose a different mode from the desktop beside it, and the
|
|
* value has to be readable before the store hydrates (the sidebar paints the
|
|
* menu on the first frame — a mode arriving one render later is a visible
|
|
* flip). Both rule out `updateUserSettings`, which is async and account-wide.
|
|
*
|
|
* `accordion` is the default and follows the current route through the
|
|
* workspace, agent, and executor branches.
|
|
*/
|
|
export const SETTINGS_MENU_MODES = ["flat", "accordion", "persistent"] as const;
|
|
|
|
export type SettingsMenuMode = (typeof SETTINGS_MENU_MODES)[number];
|
|
|
|
export const DEFAULT_SETTINGS_MENU_MODE: SettingsMenuMode = "accordion";
|
|
|
|
/** True when `mode` renders branches at all — i.e. anything but `flat`. */
|
|
export function isTreeSettingsMenuMode(mode: SettingsMenuMode): boolean {
|
|
return mode !== "flat";
|
|
}
|
|
|
|
function isSettingsMenuMode(value: unknown): value is SettingsMenuMode {
|
|
return SETTINGS_MENU_MODES.includes(value as SettingsMenuMode);
|
|
}
|
|
|
|
/**
|
|
* The stored value, or the default. Re-validated rather than trusted: a mode
|
|
* removed in an upgrade would otherwise leave the menu rendering nothing.
|
|
*/
|
|
export function readSettingsMenuMode(): SettingsMenuMode {
|
|
const stored = getLocalStorage<string>(
|
|
STORAGE_KEYS.SETTINGS_MENU_MODE,
|
|
DEFAULT_SETTINGS_MENU_MODE,
|
|
);
|
|
return isSettingsMenuMode(stored) ? stored : DEFAULT_SETTINGS_MENU_MODE;
|
|
}
|
|
|
|
export function writeSettingsMenuMode(mode: SettingsMenuMode): void {
|
|
setLocalStorage(STORAGE_KEYS.SETTINGS_MENU_MODE, mode);
|
|
}
|
|
|
|
/**
|
|
* Branch keys the persistent tree should reopen on this device.
|
|
*
|
|
* Keys are node identities, not routes, so a key naming a workspace, agent or
|
|
* executor that has since been deleted simply never matches a rendered branch:
|
|
* stale entries are inert rather than wrong. They are also never pruned — the
|
|
* expansion set is only ever added to or narrowed by an explicit collapse, and
|
|
* nothing here knows which records still exist — so the stored list grows by
|
|
* one string per deleted record and is cleared only by "Collapse all".
|
|
*/
|
|
export function readSettingsMenuExpandedKeys(): string[] {
|
|
const stored = getLocalStorage<string[]>(STORAGE_KEYS.SETTINGS_MENU_EXPANDED, []) as unknown;
|
|
if (!Array.isArray(stored)) return [];
|
|
return stored.filter((key): key is string => typeof key === "string");
|
|
}
|
|
|
|
export function writeSettingsMenuExpandedKeys(keys: readonly string[]): void {
|
|
setLocalStorage(STORAGE_KEYS.SETTINGS_MENU_EXPANDED, [...keys]);
|
|
}
|