config: refactor

This commit is contained in:
2 * r + 2 * t 2025-03-23 16:16:21 +11:00
parent 9fb35c544f
commit 1ffd7e1d54
6 changed files with 77 additions and 70 deletions

View file

@ -12,7 +12,8 @@ import Wallpapers from "@/services/wallpapers";
import type PopupWindow from "@/widgets/popupwindow";
import { execAsync, idle, timeout, writeFileAsync } from "astal";
import { App } from "astal/gtk3";
import { initConfig, style, updateConfig } from "config";
import { style } from "config";
import { initConfig, updateConfig } from "config/funcs";
const shouldBeTransparent = (name: string) =>
name === "base" ||

View file

@ -1,51 +1,6 @@
import { GLib, monitorFile, readFileAsync, Variable } from "astal";
import { Astal } from "astal/gtk3";
import { loadStyleAsync } from "./app";
type Settings<T> = { [P in keyof T]: T[P] extends object & { length?: never } ? Settings<T[P]> : Variable<T[P]> };
const CONFIG = `${GLib.get_user_config_dir()}/caelestia/shell.json`;
const isObject = (o: any) => typeof o === "object" && o !== null && !Array.isArray(o);
const deepMerge = <T extends object, U extends object>(a: T, b: U, path = ""): T & U => {
const merged: { [k: string]: any } = { ...b };
for (const [k, v] of Object.entries(a)) {
if (b.hasOwnProperty(k)) {
const bv = b[k as keyof U];
if (isObject(v) && isObject(bv)) merged[k] = deepMerge(v, bv as object, `${path}${k}.`);
else if (typeof v !== typeof bv) {
console.warn(`Invalid type for ${path}${k}: ${typeof v} != ${typeof bv}`);
merged[k] = v;
}
} else merged[k] = v;
}
return merged as any;
};
const convertSettings = <T extends object>(obj: T): Settings<T> =>
Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, isObject(v) ? convertSettings(v) : Variable(v)])) as any;
const updateSection = (from: { [k: string]: any }, to: { [k: string]: any }, path = "") => {
for (const [k, v] of Object.entries(from)) {
if (to.hasOwnProperty(k)) {
if (isObject(v)) updateSection(v, to[k], `${path}${k}.`);
else to[k].set(v);
} else console.warn(`Unknown config key: ${path}${k}`);
}
};
export const updateConfig = async () => {
updateSection(deepMerge(DEFAULTS, JSON.parse(await readFileAsync(CONFIG))), config);
loadStyleAsync().catch(console.error);
};
export const initConfig = () => {
monitorFile(CONFIG, () => updateConfig().catch(e => console.warn(`Invalid config: ${e}`)));
updateConfig().catch(e => console.warn(`Invalid config: ${e}`));
};
const DEFAULTS = {
export default {
style: {
transparency: "normal", // One of "off", "normal", "high"
vibrant: false, // Extra saturation
@ -120,13 +75,13 @@ const DEFAULTS = {
},
osds: {
volume: {
position: Astal.WindowAnchor.RIGHT,
position: Astal.WindowAnchor.RIGHT, // Top = 2, Right = 4, Left = 8, Bottom = 16
margin: 20,
hideDelay: 1500,
showValue: true,
},
brightness: {
position: Astal.WindowAnchor.LEFT,
position: Astal.WindowAnchor.LEFT, // Top = 2, Right = 4, Left = 8, Bottom = 16
margin: 20,
hideDelay: 1500,
showValue: true,
@ -189,22 +144,3 @@ const DEFAULTS = {
],
},
};
const config = convertSettings(DEFAULTS);
export const {
style,
bar,
launcher,
notifpopups,
osds,
sideleft,
math,
updates,
weather,
cpu,
gpu,
memory,
storage,
wallpapers,
} = config;

47
src/config/funcs.ts Normal file
View file

@ -0,0 +1,47 @@
import { GLib, monitorFile, readFileAsync, Variable } from "astal";
import config from ".";
import { loadStyleAsync } from "../../app";
import defaults from "./defaults";
type Settings<T> = { [P in keyof T]: T[P] extends object & { length?: never } ? Settings<T[P]> : Variable<T[P]> };
const CONFIG = `${GLib.get_user_config_dir()}/caelestia/shell.json`;
const isObject = (o: any) => typeof o === "object" && o !== null && !Array.isArray(o);
const deepMerge = <T extends object, U extends object>(a: T, b: U, path = ""): T & U => {
const merged: { [k: string]: any } = { ...b };
for (const [k, v] of Object.entries(a)) {
if (b.hasOwnProperty(k)) {
const bv = b[k as keyof U];
if (isObject(v) && isObject(bv)) merged[k] = deepMerge(v, bv as object, `${path}${k}.`);
else if (typeof v !== typeof bv) {
console.warn(`Invalid type for ${path}${k}: ${typeof v} != ${typeof bv}`);
merged[k] = v;
}
} else merged[k] = v;
}
return merged as any;
};
export const convertSettings = <T extends object>(obj: T): Settings<T> =>
Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, isObject(v) ? convertSettings(v) : Variable(v)])) as any;
const updateSection = (from: { [k: string]: any }, to: { [k: string]: any }, path = "") => {
for (const [k, v] of Object.entries(from)) {
if (to.hasOwnProperty(k)) {
if (isObject(v)) updateSection(v, to[k], `${path}${k}.`);
else to[k].set(v);
} else console.warn(`Unknown config key: ${path}${k}`);
}
};
export const updateConfig = async () => {
updateSection(deepMerge(defaults, JSON.parse(await readFileAsync(CONFIG))), config);
loadStyleAsync().catch(console.error);
};
export const initConfig = () => {
monitorFile(CONFIG, () => updateConfig().catch(e => console.warn(`Invalid config: ${e}`)));
updateConfig().catch(e => console.warn(`Invalid config: ${e}`));
};

22
src/config/index.ts Normal file
View file

@ -0,0 +1,22 @@
import defaults from "./defaults";
import { convertSettings } from "./funcs";
const config = convertSettings(defaults);
export const {
style,
bar,
launcher,
notifpopups,
osds,
sideleft,
math,
updates,
weather,
cpu,
gpu,
memory,
storage,
wallpapers,
} = config;
export default config;

View file

@ -1,6 +1,6 @@
import { App, Astal, Gtk } from "astal/gtk3";
import { notifpopups as config } from "config";
import AstalNotifd from "gi://AstalNotifd";
import { notifpopups as config } from "../../config";
import { setupChildClickthrough } from "../utils/widgets";
import Notification from "../widgets/notification";

View file

@ -13,7 +13,8 @@
"paths": {
"astal": ["/usr/share/astal/gjs"],
"astal/*": ["/usr/share/astal/gjs/*"],
"config": ["./config"],
"config": ["./src/config/index"],
"config/*": ["./src/config/*"],
"@/*": ["./src/*"]
}
}