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; } const FRIEND_MODES = new Set(["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 }, ): void { const d = body.defaults; if (!d) return; const out = target as unknown as Record; 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; beatleaderPlayerIdConfigured: boolean; warnings: string[]; } { const warnings: string[] = []; const defaults: Partial = {}; 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 }; }