mirror of
https://github.com/zephrynis/nix-flake.git
synced 2026-02-18 20:21:53 +00:00
feat: make AGS colorshell configuration fully declarative
- Add complete colorshell v2.0.3 configuration to home/ags-config/ - Disable runner plugin and NightLight tile (incompatible with NixOS) - Customize SCSS with full opacity (no transparency) - Add dark pale blue color scheme in home/pywal-colors/ - Configure Papirus-Dark icon theme via home-manager - Make ~/.config/ags/ immutable and managed by Nix store - Auto-deploy pywal colors to ~/.cache/wal/colors.json All AGS configuration is now reproducible and version controlled.
This commit is contained in:
226
home/ags-config/modules/wallpaper.ts
Normal file
226
home/ags-config/modules/wallpaper.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import { execAsync } from "ags/process";
|
||||
import { timeout } from "ags/time";
|
||||
import { monitorFile, readFile } from "ags/file";
|
||||
import GObject, { register, getter } from "ags/gobject";
|
||||
|
||||
import AstalIO from "gi://AstalIO";
|
||||
import Gio from "gi://Gio?version=2.0";
|
||||
import GLib from "gi://GLib?version=2.0";
|
||||
import { decoder, encoder } from "./utils";
|
||||
|
||||
|
||||
export { Wallpaper };
|
||||
|
||||
type WalData = {
|
||||
checksum: string;
|
||||
wallpaper: string;
|
||||
alpha: number;
|
||||
special: {
|
||||
background: string;
|
||||
foreground: string;
|
||||
cursor: string;
|
||||
};
|
||||
colors: {
|
||||
color0: string;
|
||||
color1: string;
|
||||
color2: string;
|
||||
color3: string;
|
||||
color4: string;
|
||||
color5: string;
|
||||
color6: string;
|
||||
color7: string;
|
||||
color8: string;
|
||||
color9: string;
|
||||
color10: string;
|
||||
color11: string;
|
||||
color12: string;
|
||||
color13: string;
|
||||
color14: string;
|
||||
color15: string;
|
||||
};
|
||||
};
|
||||
|
||||
@register({ GTypeName: "Wallpaper" })
|
||||
class Wallpaper extends GObject.Object {
|
||||
private static instance: Wallpaper;
|
||||
#wallpaper: (string|undefined);
|
||||
#splash: boolean = true;
|
||||
#monitor: Gio.FileMonitor;
|
||||
#hyprpaperFile: Gio.File;
|
||||
#wallpapersPath: string;
|
||||
#ignoreWatch: boolean = false;
|
||||
|
||||
@getter(Boolean)
|
||||
public get splash() { return this.#splash; }
|
||||
public set splash(showSplash: boolean) {
|
||||
this.#splash = showSplash;
|
||||
this.notify("splash");
|
||||
}
|
||||
|
||||
/** current wallpaper's complete path
|
||||
* can be an empty string if undefined */
|
||||
@getter(String)
|
||||
public get wallpaper() { return this.#wallpaper ?? ""; }
|
||||
public set wallpaper(newValue: string) { this.setWallpaper(newValue); }
|
||||
|
||||
public get wallpapersPath() { return this.#wallpapersPath; }
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.#wallpapersPath = GLib.getenv("WALLPAPERS") ??
|
||||
`${GLib.get_home_dir()}/wallpapers`;
|
||||
|
||||
this.#hyprpaperFile = Gio.File.new_for_path(`${
|
||||
GLib.get_user_config_dir()}/hypr/hyprpaper.conf`);
|
||||
|
||||
this.getWallpaper().then((wall) => {
|
||||
if(wall?.trim()) this.#wallpaper = wall.trim();
|
||||
});
|
||||
|
||||
let tmeout: (AstalIO.Time|undefined) = undefined;
|
||||
|
||||
this.#monitor = monitorFile(this.#hyprpaperFile.get_path()!, (_, event) => {
|
||||
if(event !== Gio.FileMonitorEvent.CHANGED && event !== Gio.FileMonitorEvent.CREATED &&
|
||||
event !== Gio.FileMonitorEvent.MOVED_IN)
|
||||
return;
|
||||
|
||||
if(tmeout) return;
|
||||
else tmeout = timeout(1500, () => tmeout = undefined);
|
||||
|
||||
if(this.#ignoreWatch) {
|
||||
this.#ignoreWatch = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const [ loaded, text ] = this.#hyprpaperFile.load_contents(null);
|
||||
if(!loaded)
|
||||
console.error("Wallpaper: Couldn't read changes inside the hyprpaper file!");
|
||||
|
||||
const content = decoder.decode(text);
|
||||
|
||||
if(content) {
|
||||
let setWall: boolean = true;
|
||||
|
||||
for(const line of content.split('\n')) {
|
||||
if(line.trim().startsWith('#'))
|
||||
continue;
|
||||
|
||||
const lineSplit = line.split('=');
|
||||
const key = lineSplit[0].trim(),
|
||||
value = lineSplit.filter((_, i) => i !== 0).join('=').trim();
|
||||
|
||||
switch(key) {
|
||||
case "splash":
|
||||
this.splash = (/(yes|true|on|enable|enabled|1).*/.test(value)) ? true : false;
|
||||
break;
|
||||
|
||||
case "wallpaper":
|
||||
if(this.#wallpaper !== value && setWall) {
|
||||
this.setWallpaper(value, false);
|
||||
setWall = false; // wallpaper already set
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
vfunc_dispose(): void {
|
||||
this.#monitor.cancel();
|
||||
}
|
||||
|
||||
public static getDefault(): Wallpaper {
|
||||
if(!this.instance)
|
||||
this.instance = new Wallpaper();
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
private writeChanges(): void {
|
||||
this.#ignoreWatch = true; // tell monitor to ignore file replace
|
||||
this.#hyprpaperFile.replace_async(null, false,
|
||||
Gio.FileCreateFlags.REPLACE_DESTINATION,
|
||||
GLib.PRIORITY_DEFAULT, null, (_, result) => {
|
||||
const res = this.#hyprpaperFile.replace_finish(result);
|
||||
if(res) {
|
||||
// success
|
||||
this.#ignoreWatch = true; // tell monitor to ignore this change
|
||||
res.write_bytes_async(encoder.encode(`# This file was automatically generated by color-shell
|
||||
|
||||
preload = ${this.#wallpaper}
|
||||
splash = ${this.#splash}
|
||||
wallpaper = , ${this.#wallpaper}`.split('\n').map(str => str.trimStart()).join('\n')),
|
||||
GLib.PRIORITY_DEFAULT, null, (_, asyncRes) => {
|
||||
if(_!.write_finish(asyncRes)) res.flush(null);
|
||||
res.close(null);
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(`Wallpaper: an error occurred when trying to replace the hyprpaper file`);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public getData(): WalData {
|
||||
const content = readFile(`${GLib.get_user_cache_dir()}/wal/colors.json`);
|
||||
return JSON.parse(content) as WalData;
|
||||
}
|
||||
|
||||
public async getWallpaper(): Promise<string|undefined> {
|
||||
return await execAsync("sh -c \"hyprctl hyprpaper listactive | tail -n 1\"").then(stdout => {
|
||||
const loaded: (string|undefined) = stdout.split('=')[1]?.trim();
|
||||
|
||||
if(!loaded)
|
||||
console.warn(`Wallpaper: Couldn't get wallpaper. There is(are) no loaded wallpaper(s)`);
|
||||
|
||||
return loaded;
|
||||
}).catch((err: Gio.IOErrorEnum) => {
|
||||
console.error(`Wallpaper: Couldn't get wallpaper. Stderr: \n${err.message ? `${err.message} /` : ""} Stack: \n ${err.stack}`);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
public reloadColors(): void {
|
||||
execAsync(`wal -t --cols16 darken -i "${this.#wallpaper}"`).then(() => {
|
||||
console.log("Wallpaper: reloaded shell colors");
|
||||
}).catch(r => {
|
||||
console.error(`Wallpaper: Couldn't update shell colors. Stderr: ${r}`);
|
||||
});
|
||||
}
|
||||
|
||||
public setWallpaper(path: string|Gio.File, write: boolean = true): void {
|
||||
execAsync("hyprctl hyprpaper unload all").then(() =>
|
||||
execAsync(`hyprctl hyprpaper preload ${path}`).then(() =>
|
||||
execAsync(`hyprctl hyprpaper wallpaper ${path}`).then(() => {
|
||||
this.#wallpaper = (typeof path === "string") ? path : path.get_path()!;
|
||||
this.reloadColors();
|
||||
write && this.writeChanges();
|
||||
}).catch(r => {
|
||||
console.error(`Wallpaper: Couldn't set wallpaper. Stderr: ${r}`);
|
||||
})
|
||||
).catch(r => {
|
||||
console.error(`Wallpaper: Couldn't preload image. Stderr: ${r}`);
|
||||
})
|
||||
).catch(r => {
|
||||
console.error(`Wallpaper: Couldn't unload images from memory. Stderr: ${r}`);
|
||||
});
|
||||
}
|
||||
|
||||
public async pickWallpaper(): Promise<string|undefined> {
|
||||
return (await execAsync(`zenity --file-selection`).then(wall => {
|
||||
if(!wall.trim()) return undefined;
|
||||
|
||||
this.setWallpaper(wall);
|
||||
return wall;
|
||||
}).catch(r => {
|
||||
console.error(`Wallpaper: Couldn't pick wallpaper, is \`zenity\` installed? Stderr: ${r}`);
|
||||
return undefined;
|
||||
}));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user