From 1ffd7e1d54dd33311440cf4f6c0b2debca042c4d Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sun, 23 Mar 2025 16:16:21 +1100 Subject: config: refactor --- app.tsx | 3 +- config.ts | 210 -------------------------------------------- src/config/defaults.ts | 146 ++++++++++++++++++++++++++++++ src/config/funcs.ts | 47 ++++++++++ src/config/index.ts | 22 +++++ src/modules/notifpopups.tsx | 2 +- tsconfig.json | 3 +- 7 files changed, 220 insertions(+), 213 deletions(-) delete mode 100644 config.ts create mode 100644 src/config/defaults.ts create mode 100644 src/config/funcs.ts create mode 100644 src/config/index.ts diff --git a/app.tsx b/app.tsx index 9f8a0a5..820d322 100644 --- a/app.tsx +++ b/app.tsx @@ -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" || diff --git a/config.ts b/config.ts deleted file mode 100644 index 308202e..0000000 --- a/config.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { GLib, monitorFile, readFileAsync, Variable } from "astal"; -import { Astal } from "astal/gtk3"; -import { loadStyleAsync } from "./app"; - -type Settings = { [P in keyof T]: T[P] extends object & { length?: never } ? Settings : Variable }; - -const CONFIG = `${GLib.get_user_config_dir()}/caelestia/shell.json`; - -const isObject = (o: any) => typeof o === "object" && o !== null && !Array.isArray(o); - -const deepMerge = (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 = (obj: T): Settings => - 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 = { - style: { - transparency: "normal", // One of "off", "normal", "high" - vibrant: false, // Extra saturation - }, - // Modules - bar: { - vertical: true, - modules: { - osIcon: { - enabled: true, - }, - activeWindow: { - enabled: true, - }, - mediaPlaying: { - enabled: true, - }, - workspaces: { - enabled: true, - shown: 5, - }, - tray: { - enabled: true, - }, - statusIcons: { - enabled: true, - }, - pkgUpdates: { - enabled: true, - }, - notifCount: { - enabled: true, - }, - battery: { - enabled: true, - }, - dateTime: { - enabled: true, - format: "%d/%m/%y %R", - detailedFormat: "%c", - }, - power: { - enabled: true, - }, - }, - }, - launcher: { - actionPrefix: ">", // Prefix for launcher actions - apps: { - maxResults: 30, // Actual max results, -1 for infinite - }, - files: { - maxResults: 40, // Actual max results, -1 for infinite - fdOpts: ["-a", "-t", "f"], // Options to pass to `fd` - shortenThreshold: 30, // Threshold to shorten paths in characters - }, - math: { - maxResults: 40, // Actual max results, -1 for infinite - }, - todo: { - notify: true, - }, - wallpaper: { - style: "medium", // One of "compact", "medium", "large" - }, - disabledActions: ["logout", "shutdown", "reboot", "hibernate"], // Actions to hide, see launcher/actions.tsx for available actions - }, - notifpopups: { - maxPopups: -1, - expire: false, - agoTime: true, // Whether to show time in ago format, e.g. 10 mins ago, or raw time, e.g. 10:42 - }, - osds: { - volume: { - position: Astal.WindowAnchor.RIGHT, - margin: 20, - hideDelay: 1500, - showValue: true, - }, - brightness: { - position: Astal.WindowAnchor.LEFT, - margin: 20, - hideDelay: 1500, - showValue: true, - }, - lock: { - spacing: 5, - caps: { - hideDelay: 1000, - }, - num: { - hideDelay: 1000, - }, - }, - }, - sideleft: { - directories: { - left: { - top: "󰉍 Downloads", - middle: "󱧶 Documents", - bottom: "󱍙 Music", - }, - right: { - top: "󰉏 Pictures", - middle: "󰉏 Videos", - bottom: "󱂵 Home", - }, - }, - }, - // Services - math: { - maxHistory: 100, - }, - updates: { - interval: 900000, - }, - weather: { - interval: 600000, - key: "assets/weather-api-key.txt", // Path to file containing api key relative to the base directory. To get a key, visit https://weatherapi.com/ - location: "", // Location as a string or empty to autodetect - imperial: false, - }, - cpu: { - interval: 2000, - }, - gpu: { - interval: 2000, - }, - memory: { - interval: 5000, - }, - storage: { - interval: 5000, - }, - wallpapers: { - paths: [ - { - recursive: true, // Whether to search recursively - path: "~/Pictures/Wallpapers", // Path to search - }, - ], - }, -}; - -const config = convertSettings(DEFAULTS); - -export const { - style, - bar, - launcher, - notifpopups, - osds, - sideleft, - math, - updates, - weather, - cpu, - gpu, - memory, - storage, - wallpapers, -} = config; diff --git a/src/config/defaults.ts b/src/config/defaults.ts new file mode 100644 index 0000000..d699a45 --- /dev/null +++ b/src/config/defaults.ts @@ -0,0 +1,146 @@ +import { Astal } from "astal/gtk3"; + +export default { + style: { + transparency: "normal", // One of "off", "normal", "high" + vibrant: false, // Extra saturation + }, + // Modules + bar: { + vertical: true, + modules: { + osIcon: { + enabled: true, + }, + activeWindow: { + enabled: true, + }, + mediaPlaying: { + enabled: true, + }, + workspaces: { + enabled: true, + shown: 5, + }, + tray: { + enabled: true, + }, + statusIcons: { + enabled: true, + }, + pkgUpdates: { + enabled: true, + }, + notifCount: { + enabled: true, + }, + battery: { + enabled: true, + }, + dateTime: { + enabled: true, + format: "%d/%m/%y %R", + detailedFormat: "%c", + }, + power: { + enabled: true, + }, + }, + }, + launcher: { + actionPrefix: ">", // Prefix for launcher actions + apps: { + maxResults: 30, // Actual max results, -1 for infinite + }, + files: { + maxResults: 40, // Actual max results, -1 for infinite + fdOpts: ["-a", "-t", "f"], // Options to pass to `fd` + shortenThreshold: 30, // Threshold to shorten paths in characters + }, + math: { + maxResults: 40, // Actual max results, -1 for infinite + }, + todo: { + notify: true, + }, + wallpaper: { + style: "medium", // One of "compact", "medium", "large" + }, + disabledActions: ["logout", "shutdown", "reboot", "hibernate"], // Actions to hide, see launcher/actions.tsx for available actions + }, + notifpopups: { + maxPopups: -1, + expire: false, + agoTime: true, // Whether to show time in ago format, e.g. 10 mins ago, or raw time, e.g. 10:42 + }, + osds: { + volume: { + position: Astal.WindowAnchor.RIGHT, // Top = 2, Right = 4, Left = 8, Bottom = 16 + margin: 20, + hideDelay: 1500, + showValue: true, + }, + brightness: { + position: Astal.WindowAnchor.LEFT, // Top = 2, Right = 4, Left = 8, Bottom = 16 + margin: 20, + hideDelay: 1500, + showValue: true, + }, + lock: { + spacing: 5, + caps: { + hideDelay: 1000, + }, + num: { + hideDelay: 1000, + }, + }, + }, + sideleft: { + directories: { + left: { + top: "󰉍 Downloads", + middle: "󱧶 Documents", + bottom: "󱍙 Music", + }, + right: { + top: "󰉏 Pictures", + middle: "󰉏 Videos", + bottom: "󱂵 Home", + }, + }, + }, + // Services + math: { + maxHistory: 100, + }, + updates: { + interval: 900000, + }, + weather: { + interval: 600000, + key: "assets/weather-api-key.txt", // Path to file containing api key relative to the base directory. To get a key, visit https://weatherapi.com/ + location: "", // Location as a string or empty to autodetect + imperial: false, + }, + cpu: { + interval: 2000, + }, + gpu: { + interval: 2000, + }, + memory: { + interval: 5000, + }, + storage: { + interval: 5000, + }, + wallpapers: { + paths: [ + { + recursive: true, // Whether to search recursively + path: "~/Pictures/Wallpapers", // Path to search + }, + ], + }, +}; diff --git a/src/config/funcs.ts b/src/config/funcs.ts new file mode 100644 index 0000000..bc0a7d1 --- /dev/null +++ b/src/config/funcs.ts @@ -0,0 +1,47 @@ +import { GLib, monitorFile, readFileAsync, Variable } from "astal"; +import config from "."; +import { loadStyleAsync } from "../../app"; +import defaults from "./defaults"; + +type Settings = { [P in keyof T]: T[P] extends object & { length?: never } ? Settings : Variable }; + +const CONFIG = `${GLib.get_user_config_dir()}/caelestia/shell.json`; + +const isObject = (o: any) => typeof o === "object" && o !== null && !Array.isArray(o); + +const deepMerge = (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 = (obj: T): Settings => + 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}`)); +}; diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..d09a668 --- /dev/null +++ b/src/config/index.ts @@ -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; diff --git a/src/modules/notifpopups.tsx b/src/modules/notifpopups.tsx index d3cbb90..9d461b1 100644 --- a/src/modules/notifpopups.tsx +++ b/src/modules/notifpopups.tsx @@ -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"; diff --git a/tsconfig.json b/tsconfig.json index b487066..944ac5a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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/*"] } } -- cgit v1.2.3-freya