Persist config changes in local storage

This commit is contained in:
Jared Miller 2026-03-09 18:12:33 -04:00
parent 14208e17fb
commit 8ad5130466
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
3 changed files with 88 additions and 8 deletions

View file

@ -45,6 +45,24 @@
width: 65px; width: 65px;
} }
.reset-btn {
width: calc(100% - 16px);
margin: 12px 8px;
padding: 6px;
background: #333;
color: #aaa;
border: 1px solid #555;
border-radius: 3px;
font-family: monospace;
font-size: 12px;
cursor: pointer;
}
.reset-btn:hover {
background: #444;
color: #ddd;
}
#gui-container { #gui-container {
flex: 1; flex: 1;
} }

View file

@ -1,4 +1,6 @@
export default { const STORAGE_KEY = "ants-simulation-config";
const defaults = {
worldSize: 1024, worldSize: 1024,
antsCount: 12, antsCount: 12,
simulationStepsPerSecond: 60, simulationStepsPerSecond: 60,
@ -17,3 +19,41 @@ export default {
repellentMaxPerCell: 10, repellentMaxPerCell: 10,
repellentThreshold: 0.01, repellentThreshold: 0.01,
}; };
type ConfigType = typeof defaults;
function loadSaved(): Partial<ConfigType> {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) return JSON.parse(raw);
} catch {
// corrupted data, ignore
}
return {};
}
const saved = loadSaved();
const Config: ConfigType = { ...defaults, ...saved };
export function saveConfig(): void {
const toSave: Partial<ConfigType> = {};
for (const key of Object.keys(defaults) as (keyof ConfigType)[]) {
if (Config[key] !== defaults[key]) {
// biome-ignore lint/suspicious/noExplicitAny: generic key/value copy
(toSave as any)[key] = Config[key];
}
}
if (Object.keys(toSave).length > 0) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
} else {
localStorage.removeItem(STORAGE_KEY);
}
}
export function resetConfig(): void {
Object.assign(Config, defaults);
localStorage.clear();
}
export { defaults };
export default Config;

View file

@ -1,5 +1,5 @@
import GUI from "lil-gui"; import GUI from "lil-gui";
import Config from "./Config"; import Config, { resetConfig, saveConfig } from "./Config";
type EventHandler = () => void; type EventHandler = () => void;
@ -16,33 +16,50 @@ class GUIController {
.add(Config, "worldSize", 256, 4096) .add(Config, "worldSize", 256, 4096)
.name("World size") .name("World size")
.step(1) .step(1)
.onChange(() => this.emit("reset")); .onChange(() => this.saveAndEmit("reset"));
simFolder simFolder
.add(Config, "antsCount", 0, 22) .add(Config, "antsCount", 0, 22)
.name("Ants count 2^") .name("Ants count 2^")
.step(1) .step(1)
.onChange(() => this.emit("reset")); .onChange(() => this.saveAndEmit("reset"));
simFolder simFolder
.add(Config, "scentFadeOutFactor", 0, 0.01) .add(Config, "scentFadeOutFactor", 0, 0.01)
.name("Pheromone evaporation factor") .name("Pheromone evaporation factor")
.step(0.0001) .step(0.0001)
.onChange(() => this.emit("reset")); .onChange(() => this.saveAndEmit("reset"));
simFolder simFolder
.add(Config, "scentBlurRadius", 0, 0.5) .add(Config, "scentBlurRadius", 0, 0.5)
.name("Pheromone diffusion factor") .name("Pheromone diffusion factor")
.step(0.01) .step(0.01)
.onChange(() => this.emit("reset")); .onChange(() => this.saveAndEmit("reset"));
simFolder simFolder
.add(Config, "simulationStepsPerSecond", 1, 500) .add(Config, "simulationStepsPerSecond", 1, 500)
.name("Simulation steps per second") .name("Simulation steps per second")
.step(1); .step(1)
.onChange(() => saveConfig());
const controlsFolder = this.gui.addFolder("Controls"); const controlsFolder = this.gui.addFolder("Controls");
controlsFolder.add(Config, "brushRadius", 1, 100).name("Brush radius"); controlsFolder
.add(Config, "brushRadius", 1, 100)
.name("Brush radius")
.onChange(() => saveConfig());
simFolder.open(); simFolder.open();
controlsFolder.open(); controlsFolder.open();
const resetBtn = document.createElement("button");
resetBtn.textContent = "Reset to defaults";
resetBtn.className = "reset-btn";
resetBtn.addEventListener("click", () => {
resetConfig();
for (const c of this.gui.controllersRecursive()) {
c.updateDisplay();
}
this.emit("reset");
});
// biome-ignore lint/style/noNonNullAssertion: gui-container exists in index.html
document.getElementById("gui-container")!.appendChild(resetBtn);
} }
on(event: string, handler: EventHandler): void { on(event: string, handler: EventHandler): void {
@ -53,6 +70,11 @@ class GUIController {
this.listeners.get(event)!.push(handler); this.listeners.get(event)!.push(handler);
} }
private saveAndEmit(event: string): void {
saveConfig();
this.emit(event);
}
private emit(event: string): void { private emit(event: string): void {
const handlers = this.listeners.get(event); const handlers = this.listeners.get(event);
if (handlers) { if (handlers) {