config: restore to default if not specified
Restore configs to default if not given in the config file instead of not changing them Also sideleft configs
This commit is contained in:
parent
2d97968445
commit
6a043ce106
2 changed files with 109 additions and 76 deletions
167
config.ts
167
config.ts
|
|
@ -2,165 +2,196 @@ import { GLib, monitorFile, readFileAsync, Variable } from "astal";
|
||||||
import { Astal } from "astal/gtk3";
|
import { Astal } from "astal/gtk3";
|
||||||
import { loadStyleAsync } from "./app";
|
import { loadStyleAsync } from "./app";
|
||||||
|
|
||||||
|
type Settings<T> = { [P in keyof T]: T[P] extends object ? Settings<T[P]> : Variable<T[P]> };
|
||||||
|
|
||||||
const CONFIG = `${GLib.get_user_config_dir()}/caelestia/shell.json`;
|
const CONFIG = `${GLib.get_user_config_dir()}/caelestia/shell.json`;
|
||||||
|
|
||||||
const s = <T>(v: T): Variable<T> => Variable(v);
|
const isObject = (o: any) => typeof o === "object" && o !== null && !Array.isArray(o);
|
||||||
|
|
||||||
const warn = (e: Error) => console.warn(`Invalid config: ${e}`);
|
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 updateSection = (from: { [k: string]: any }, to: { [k: string]: any }, path: string) => {
|
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)) {
|
for (const [k, v] of Object.entries(from)) {
|
||||||
|
print(`${path}${k}:`, to.hasOwnProperty(k));
|
||||||
if (to.hasOwnProperty(k)) {
|
if (to.hasOwnProperty(k)) {
|
||||||
if (typeof v === "object" && v !== null && !Array.isArray(v)) updateSection(v, to[k], `${path}.${k}`);
|
if (isObject(v)) updateSection(v, to[k], `${path}${k}.`);
|
||||||
else if (typeof v === typeof to[k].get()) to[k].set(v);
|
else to[k].set(v);
|
||||||
else console.warn(`Invalid type for ${path}.${k}: ${typeof v} != ${typeof to[k].get()}`);
|
} else console.warn(`Unknown config key: ${path}${k}`);
|
||||||
} else console.warn(`Unknown config key: ${path}.${k}`);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateConfig = async () => {
|
export const updateConfig = async () => {
|
||||||
const conf: { [k: string]: any } = JSON.parse(await readFileAsync(CONFIG));
|
updateSection(deepMerge(DEFAULTS, JSON.parse(await readFileAsync(CONFIG))), config);
|
||||||
for (const [k, v] of Object.entries(conf)) {
|
|
||||||
if (config.hasOwnProperty(k)) updateSection(v, config[k as keyof typeof config], k);
|
|
||||||
else console.warn(`Unknown config key: ${k}`);
|
|
||||||
}
|
|
||||||
loadStyleAsync().catch(console.error);
|
loadStyleAsync().catch(console.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initConfig = () => {
|
export const initConfig = () => {
|
||||||
monitorFile(CONFIG, () => updateConfig().catch(warn));
|
monitorFile(CONFIG, () => updateConfig().catch(e => console.warn(`Invalid config: ${e}`)));
|
||||||
updateConfig().catch(warn);
|
updateConfig().catch(e => console.warn(`Invalid config: ${e}`));
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = {
|
const DEFAULTS = {
|
||||||
// Modules
|
// Modules
|
||||||
bar: {
|
bar: {
|
||||||
vertical: s(true),
|
vertical: true,
|
||||||
modules: {
|
modules: {
|
||||||
osIcon: {
|
osIcon: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
activeWindow: {
|
activeWindow: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
mediaPlaying: {
|
mediaPlaying: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
workspaces: {
|
workspaces: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
shown: s(5),
|
shown: 5,
|
||||||
},
|
},
|
||||||
tray: {
|
tray: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
statusIcons: {
|
statusIcons: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
pkgUpdates: {
|
pkgUpdates: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
notifCount: {
|
notifCount: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
battery: {
|
battery: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
dateTime: {
|
dateTime: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
format: s("%d/%m/%y %R"),
|
format: "%d/%m/%y %R",
|
||||||
detailedFormat: s("%c"),
|
detailedFormat: "%c",
|
||||||
},
|
},
|
||||||
power: {
|
power: {
|
||||||
enabled: s(true),
|
enabled: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
launcher: {
|
launcher: {
|
||||||
maxResults: s(15), // Max shown results at one time (i.e. max height of the launcher)
|
maxResults: 15, // Max shown results at one time (i.e. max height of the launcher)
|
||||||
apps: {
|
apps: {
|
||||||
maxResults: s(30), // Actual max results, -1 for infinite
|
maxResults: 30, // Actual max results, -1 for infinite
|
||||||
pins: s([
|
pins: [
|
||||||
["zen", "firefox", "waterfox", "google-chrome", "chromium", "brave-browser"],
|
["zen", "firefox", "waterfox", "google-chrome", "chromium", "brave-browser"],
|
||||||
["foot", "alacritty", "kitty", "wezterm"],
|
["foot", "alacritty", "kitty", "wezterm"],
|
||||||
["thunar", "nemo", "nautilus"],
|
["thunar", "nemo", "nautilus"],
|
||||||
["codium", "code", "clion", "intellij-idea-ultimate-edition"],
|
["codium", "code", "clion", "intellij-idea-ultimate-edition"],
|
||||||
["spotify-adblock", "spotify", "audacious", "elisa"],
|
["spotify-adblock", "spotify", "audacious", "elisa"],
|
||||||
]),
|
],
|
||||||
},
|
},
|
||||||
files: {
|
files: {
|
||||||
maxResults: s(40), // Actual max results, -1 for infinite
|
maxResults: 40, // Actual max results, -1 for infinite
|
||||||
fdOpts: s(["-a", "-t", "f"]), // Options to pass to `fd`
|
fdOpts: ["-a", "-t", "f"], // Options to pass to `fd`
|
||||||
},
|
},
|
||||||
math: {
|
math: {
|
||||||
maxResults: s(40), // Actual max results, -1 for infinite
|
maxResults: 40, // Actual max results, -1 for infinite
|
||||||
},
|
},
|
||||||
windows: {
|
windows: {
|
||||||
maxResults: s(-1), // Actual max results, -1 for infinite
|
maxResults: -1, // Actual max results, -1 for infinite
|
||||||
weights: {
|
weights: {
|
||||||
// Weights for fuzzy sort
|
// Weights for fuzzy sort
|
||||||
title: s(1),
|
title: 1,
|
||||||
class: s(1),
|
class: 1,
|
||||||
initialTitle: s(0.5),
|
initialTitle: 0.5,
|
||||||
initialClass: s(0.5),
|
initialClass: 0.5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
todo: {
|
todo: {
|
||||||
notify: s(true),
|
notify: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notifpopups: {
|
notifpopups: {
|
||||||
maxPopups: s(-1),
|
maxPopups: -1,
|
||||||
expire: s(false),
|
expire: false,
|
||||||
agoTime: s(true), // Whether to show time in ago format, e.g. 10 mins ago, or raw time, e.g. 10:42
|
agoTime: true, // Whether to show time in ago format, e.g. 10 mins ago, or raw time, e.g. 10:42
|
||||||
},
|
},
|
||||||
osds: {
|
osds: {
|
||||||
volume: {
|
volume: {
|
||||||
position: s(Astal.WindowAnchor.RIGHT),
|
position: Astal.WindowAnchor.RIGHT,
|
||||||
margin: s(20),
|
margin: 20,
|
||||||
hideDelay: s(1500),
|
hideDelay: 1500,
|
||||||
showValue: s(true),
|
showValue: true,
|
||||||
},
|
},
|
||||||
brightness: {
|
brightness: {
|
||||||
position: s(Astal.WindowAnchor.LEFT),
|
position: Astal.WindowAnchor.LEFT,
|
||||||
margin: s(20),
|
margin: 20,
|
||||||
hideDelay: s(1500),
|
hideDelay: 1500,
|
||||||
showValue: s(true),
|
showValue: true,
|
||||||
},
|
},
|
||||||
lock: {
|
lock: {
|
||||||
spacing: s(5),
|
spacing: 5,
|
||||||
caps: {
|
caps: {
|
||||||
hideDelay: s(1000),
|
hideDelay: 1000,
|
||||||
},
|
},
|
||||||
num: {
|
num: {
|
||||||
hideDelay: s(1000),
|
hideDelay: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sideleft: {
|
||||||
|
directories: {
|
||||||
|
left: {
|
||||||
|
top: " Downloads",
|
||||||
|
|
||||||
|
middle: " Documents",
|
||||||
|
bottom: " Music",
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
top: " Pictures",
|
||||||
|
middle: " Videos",
|
||||||
|
bottom: " Home",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Services
|
// Services
|
||||||
math: {
|
math: {
|
||||||
maxHistory: s(100),
|
maxHistory: 100,
|
||||||
},
|
},
|
||||||
updates: {
|
updates: {
|
||||||
interval: s(900000),
|
interval: 900000,
|
||||||
},
|
},
|
||||||
weather: {
|
weather: {
|
||||||
interval: s(600000),
|
interval: 600000,
|
||||||
key: s("assets/weather-api-key.txt"), // Path to file containing api key relative to the base directory. To get a key, visit https://weatherapi.com/
|
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: s(""), // Location as a string or empty to autodetect
|
location: "", // Location as a string or empty to autodetect
|
||||||
imperial: s(false),
|
imperial: false,
|
||||||
},
|
},
|
||||||
cpu: {
|
cpu: {
|
||||||
interval: s(2000),
|
interval: 2000,
|
||||||
},
|
},
|
||||||
gpu: {
|
gpu: {
|
||||||
interval: s(2000),
|
interval: 2000,
|
||||||
},
|
},
|
||||||
memory: {
|
memory: {
|
||||||
interval: s(5000),
|
interval: 5000,
|
||||||
},
|
},
|
||||||
storage: {
|
storage: {
|
||||||
interval: s(5000),
|
interval: 5000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const { bar, launcher, notifpopups, osds, math, updates, weather, cpu, gpu, memory, storage } = config;
|
const config = convertSettings(DEFAULTS);
|
||||||
|
|
||||||
|
export const { bar, launcher, notifpopups, osds, sideleft, math, updates, weather, cpu, gpu, memory, storage } = config;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import PopupWindow from "@/widgets/popupwindow";
|
||||||
import { bind, execAsync, GLib, type Binding } from "astal";
|
import { bind, execAsync, GLib, type Binding } from "astal";
|
||||||
import { App, Gtk, type Widget } from "astal/gtk3";
|
import { App, Gtk, type Widget } from "astal/gtk3";
|
||||||
import type cairo from "cairo";
|
import type cairo from "cairo";
|
||||||
|
import { sideleft } from "config";
|
||||||
|
|
||||||
const fmt = (bytes: number, pow: number) => +(bytes / 1024 ** pow).toFixed(2);
|
const fmt = (bytes: number, pow: number) => +(bytes / 1024 ** pow).toFixed(2);
|
||||||
const format = ({ total, used }: { total: number; used: number }) => {
|
const format = ({ total, used }: { total: number; used: number }) => {
|
||||||
|
|
@ -62,13 +63,14 @@ const QuickLaunch = () => (
|
||||||
</box>
|
</box>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Location = ({ home, label, num }: { home?: boolean; label: string; num: number }) => (
|
const Location = ({ label, num }: { label: Binding<string>; num: number }) => (
|
||||||
<button
|
<button
|
||||||
className={"loc" + num}
|
className={"loc" + num}
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
onClicked={self => {
|
onClicked={self => {
|
||||||
self.get_toplevel().hide();
|
self.get_toplevel().hide();
|
||||||
execAsync(`xdg-open ${HOME}/${home ? "" : label.split(" ").at(-1) + "/"}`).catch(console.error);
|
const dir = label.get().split(" ").at(-1);
|
||||||
|
execAsync(`xdg-open ${HOME}/${dir?.toLowerCase() === "home" ? "" : `${dir}/`}`).catch(console.error);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<label xalign={0} label={label} />
|
<label xalign={0} label={label} />
|
||||||
|
|
@ -78,14 +80,14 @@ const Location = ({ home, label, num }: { home?: boolean; label: string; num: nu
|
||||||
const Locations = () => (
|
const Locations = () => (
|
||||||
<box className="locations">
|
<box className="locations">
|
||||||
<box vertical>
|
<box vertical>
|
||||||
<Location label=" Downloads" num={1} />
|
<Location label={bind(sideleft.directories.left.top)} num={1} />
|
||||||
<Location label=" Documents" num={2} />
|
<Location label={bind(sideleft.directories.left.middle)} num={2} />
|
||||||
<Location label=" Music" num={3} />
|
<Location label={bind(sideleft.directories.left.bottom)} num={3} />
|
||||||
</box>
|
</box>
|
||||||
<box vertical>
|
<box vertical>
|
||||||
<Location label=" Pictures" num={4} />
|
<Location label={bind(sideleft.directories.right.top)} num={4} />
|
||||||
<Location label=" Videos" num={5} />
|
<Location label={bind(sideleft.directories.right.middle)} num={5} />
|
||||||
<Location label=" Home" num={6} home />
|
<Location label={bind(sideleft.directories.right.bottom)} num={6} />
|
||||||
</box>
|
</box>
|
||||||
</box>
|
</box>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue