import React, { useState, useEffect } from "react"; import { Save, RefreshCcw } from "lucide-react"; import { getSettings, updateSettings } from "../lib/saturn"; import { LauncherSettings } from "@/lib/types"; export type SettingSchemaItem = | SettingSchemaLeaf | SettingSchemaGroup; export interface SettingSchemaBase { label: string; description?: string; } export interface SettingSchemaLeaf extends SettingSchemaBase { type: "slider" | "toggle" | "input"; key: string; // leaf key (resolved in context) default: any; min?: number; max?: number; step?: number; format?: (value: any) => string; ticks?: number[]; info?: string; } export interface SettingSchemaGroup extends SettingSchemaBase { type: "group"; children: SettingSchemaItem[]; } export const settingsSchema: SettingSchemaItem[] = [ { type: "group", label: "Performance", children: [ { key: "max_memory", label: "Maximum Memory (RAM)", type: "slider", min: 1024, max: 16384, step: 512, default: 2048, ticks: [1024, 4096, 8192, 12288, 16384], format: (v) => `${(v / 1024).toFixed(1)} GB`, }, ], }, { type: "group", label: "Features", children: [ { key: "features.sync_options", label: "Sync options.txt", type: "toggle", default: true, }, ], }, ]; const getValue = (obj: any, path: string) => path.split(".").reduce((acc, key) => acc?.[key], obj); const setValue = (obj: any, path: string, value: any) => { const keys = path.split("."); const last = keys.pop()!; const clone = structuredClone(obj); let current = clone; for (const key of keys) current = current[key]; current[last] = value; return clone; }; const SettingItem: React.FC<{ item: SettingSchemaItem; settings: LauncherSettings; setSettings: React.Dispatch< React.SetStateAction >; }> = ({ item, settings, setSettings }) => { if (item.type === "group") { return (
{item.label}
{item.children.map((child, i) => ( ))}
); } const value = getValue(settings, item.key); return (
{item.type === "slider" && (
{item.label} {(item.format && item.format(value)) || value}
setSettings((prev) => prev ? setValue(prev, item.key, parseInt(e.target.value)) : prev ) } className="w-full h-2 bg-saturn-border rounded-lg appearance-none cursor-pointer accent-saturn-accent" /> {item.ticks && (
{item.ticks.map((tick) => { const percent = (((tick - item.min!) / (item.max! - item.min!)) * 100 - 50) * 0.97 + 50; return ( {tick / 1024} GB ); })}
)}
)} {item.type === "toggle" && (
setSettings((prev) => prev ? setValue(prev, item.key, e.target.checked) : prev ) } /> {item.label}
)}
); }; const buildDefaultSettings = (schema: SettingSchemaItem[]): any => { const result: any = {}; for (const item of schema) { if (item.type === "group") { Object.assign(result, buildDefaultSettings(item.children)); } else { result[item.key] = item.default; } } return result; }; const SettingsSection: React.FC = () => { const [settings, setSettings] = useState(null); const [isSaving, setIsSaving] = useState(false); const [saveStatus, setSaveStatus] = useState(""); useEffect(() => { const loadSettings = async () => { const s = await getSettings(); setSettings(s); }; loadSettings(); }, []); const handleSave = async () => { if (!settings) return; setIsSaving(true); setSaveStatus("Saving..."); await updateSettings(settings); setTimeout(() => { setIsSaving(false); setSaveStatus("Settings saved"); setTimeout(() => setSaveStatus(""), 2000); }, 1000); }; const handleReset = async () => { setSettings(buildDefaultSettings(settingsSchema) as LauncherSettings); }; if (!settings) return null; return (

Settings

Configure how Saturn Launcher launches your game

{settingsSchema.map((item) => ())}
); }; export default SettingsSection;