Files
nodarium/app/src/lib/settings/app-settings.svelte.ts
2026-01-23 02:28:17 +01:00

183 lines
3.7 KiB
TypeScript

import { localState } from '$lib/helpers/localState.svelte';
const themes = [
'dark',
'light',
'catppuccin',
'solarized',
'high-contrast',
'nord',
'dracula'
] as const;
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
},
showNodeIds: {
type: 'boolean',
label: 'Show Node Ids',
value: false
}
},
debug: {
title: 'Debug',
wireframe: {
type: 'boolean',
label: 'Wireframe',
value: false
},
useWorker: {
type: 'boolean',
label: 'Execute in WebWorker',
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
},
showGraphJson: {
type: 'boolean',
label: 'Show Graph Source',
value: false
},
cache: {
title: 'Cache',
useRuntimeCache: {
type: 'boolean',
label: 'Node Results',
value: true
},
useRegistryCache: {
type: 'boolean',
label: 'Node Source',
value: true
}
},
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;
type SettingsToStore<T> = T extends { value: infer V } ? V extends readonly string[] ? V[number]
: V
: T extends any[] ? {}
: T extends object ? {
[K in keyof T as T[K] extends object ? K : never]: SettingsToStore<T[K]>;
}
: never;
export function settingsToStore<T>(settings: T): SettingsToStore<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);
});
});