Files
nodarium/app/src/lib/settings/app-settings.svelte.ts
Max Richter cfcb447784
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m48s
feat: update some more components to svelte 5
2025-11-24 21:11:16 +01:00

182 lines
3.8 KiB
TypeScript

import { localState } from "$lib/helpers/localState.svelte";
import type { NodeInput } from "@nodes/types";
import type { SettingsType } from ".";
const themes = [
"dark",
"light",
"catppuccin",
"solarized",
"high-contrast",
"nord",
"dracula",
];
export const AppSettingTypes = {
theme: {
type: "select",
options: themes,
label: "Theme",
value: themes[0],
},
showGrid: {
type: "boolean",
label: "Show Grid",
value: true,
},
centerCamera: {
type: "boolean",
label: "Center Camera",
value: true,
},
nodeInterface: {
title: "Node Interface",
showNodeGrid: {
type: "boolean",
label: "Show Grid",
value: true,
},
snapToGrid: {
type: "boolean",
label: "Snap to Grid",
value: true,
},
showHelp: {
type: "boolean",
label: "Show Help",
value: false,
},
},
debug: {
title: "Debug",
amount: {
type: "number",
label: "Amount",
value: 4,
},
wireframe: {
type: "boolean",
label: "Wireframe",
value: false,
},
useWorker: {
type: "boolean",
label: "Execute runtime in worker",
value: true,
},
showIndices: {
type: "boolean",
label: "Show Indices",
value: false,
},
showPerformancePanel: {
type: "boolean",
label: "Show Performance Panel",
value: false,
},
showBenchmarkPanel: {
type: "boolean",
label: "Show Benchmark Panel",
value: false,
},
showVertices: {
type: "boolean",
label: "Show Vertices",
value: false,
},
showStemLines: {
type: "boolean",
label: "Show Stem Lines",
value: false,
},
stressTest: {
title: "Stress Test",
amount: {
type: "integer",
min: 2,
max: 15,
value: 4,
},
loadGrid: {
type: "button",
label: "Load Grid",
},
loadTree: {
type: "button",
label: "Load Tree",
},
lottaFaces: {
type: "button",
label: "Load 'lots of faces'",
},
lottaNodes: {
type: "button",
label: "Load 'lots of nodes'",
},
lottaNodesAndFaces: {
type: "button",
label: "Load 'lots of nodes and faces'",
},
},
},
} as const satisfies SettingsType;
type IsInputDefinition<T> = T extends NodeInput ? T : never;
type HasTitle = { title: string };
type Widen<T> = T extends boolean
? boolean
: T extends number
? number
: T extends string
? string
: T;
type ExtractSettingsValues<T> = {
-readonly [K in keyof T]: T[K] extends HasTitle
? ExtractSettingsValues<Omit<T[K], "title">>
: T[K] extends IsInputDefinition<T[K]>
? T[K] extends { value: infer V }
? Widen<V>
: never
: T[K] extends Record<string, any>
? ExtractSettingsValues<T[K]>
: never;
};
export function settingsToStore<T>(settings: T): ExtractSettingsValues<T> {
const result = {} as any;
for (const key in settings) {
const value = settings[key];
if (value && typeof value === "object") {
if ("value" in value) {
result[key] = value.value;
} else {
result[key] = settingsToStore(value);
}
}
}
return result;
}
export let appSettings = localState(
"app-settings",
settingsToStore(AppSettingTypes),
);
$effect.root(() => {
$effect(() => {
const theme = appSettings.value.theme;
const classes = document.documentElement.classList;
const newClassName = `theme-${theme}`;
if (classes) {
for (const className of classes) {
if (className.startsWith("theme-") && className !== newClassName) {
classes.remove(className);
}
}
}
document.documentElement.classList.add(newClassName);
});
});