beatsaber-overlay/src/client/overlay-config.ts

109 lines
3.3 KiB
TypeScript

import type { FriendMode, OverlaySettings } from "./types.ts";
/** Keys accepted in overlay.toml (snake_case). */
export interface OverlayToml {
chat_request_database?: string;
cover?: boolean;
map_info?: boolean;
time?: boolean;
score?: boolean;
friends?: boolean;
friend_mode?: string;
bsr?: boolean;
beatleader_player_id?: string;
right?: boolean;
bottom?: boolean;
scale?: number;
fade?: number;
}
export interface OverlayConfigApiBody {
defaults: Partial<OverlaySettings>;
}
const FRIEND_MODES = new Set<FriendMode>(["mutual", "following", "followers"]);
/** Merge `/api/overlay-config` into the object used as `defaults` before applying the URL hash. */
export function mergeOverlayConfigResponse(
target: OverlaySettings,
body: { defaults?: Partial<OverlaySettings> },
): void {
const d = body.defaults;
if (!d) return;
const out = target as unknown as Record<string, unknown>;
for (const key of Object.keys(d) as (keyof OverlaySettings)[]) {
const v = d[key];
if (v !== undefined) out[key as string] = v;
}
}
function asBool(v: unknown): boolean | undefined {
if (typeof v === "boolean") return v;
return undefined;
}
function asNum(v: unknown): number | undefined {
if (typeof v === "number" && Number.isFinite(v)) return v;
return undefined;
}
/**
* Maps parsed TOML (snake_case) to overlay defaults. Collects warnings for invalid values.
*/
export function overlayTomlToDefaults(toml: OverlayToml): {
chatRequestDatabase: string | undefined;
defaults: Partial<OverlaySettings>;
beatleaderPlayerIdConfigured: boolean;
warnings: string[];
} {
const warnings: string[] = [];
const defaults: Partial<OverlaySettings> = {};
const db = typeof toml.chat_request_database === "string" ? toml.chat_request_database.trim() : "";
const chatRequestDatabase = db || undefined;
const bCover = asBool(toml.cover);
if (bCover !== undefined) defaults.cover = bCover;
const bMap = asBool(toml.map_info);
if (bMap !== undefined) defaults.mapInfo = bMap;
const bTime = asBool(toml.time);
if (bTime !== undefined) defaults.time = bTime;
const bScore = asBool(toml.score);
if (bScore !== undefined) defaults.score = bScore;
const bFriends = asBool(toml.friends);
if (bFriends !== undefined) defaults.friends = bFriends;
const bBsr = asBool(toml.bsr);
if (bBsr !== undefined) defaults.bsr = bBsr;
const bRight = asBool(toml.right);
if (bRight !== undefined) defaults.right = bRight;
const bBottom = asBool(toml.bottom);
if (bBottom !== undefined) defaults.bottom = bBottom;
if (toml.friend_mode !== undefined) {
const raw = String(toml.friend_mode).trim().toLowerCase();
if (FRIEND_MODES.has(raw as FriendMode)) defaults.friendMode = raw as FriendMode;
else {
warnings.push(
`overlay.toml: invalid friend_mode "${toml.friend_mode}" (expected mutual, following, or followers)`,
);
}
}
const scale = asNum(toml.scale);
if (scale !== undefined) defaults.scale = scale;
const fade = asNum(toml.fade);
if (fade !== undefined) defaults.fade = fade;
let beatleaderPlayerIdConfigured = false;
if (typeof toml.beatleader_player_id === "string") {
const id = toml.beatleader_player_id.trim();
if (id) {
defaults.beatLeaderId = id;
beatleaderPlayerIdConfigured = true;
}
}
return { chatRequestDatabase, defaults, beatleaderPlayerIdConfigured, warnings };
}