Added ags
TODO copy ags config
This commit is contained in:
16
roles/ags/files/lib/battery.ts
Normal file
16
roles/ags/files/lib/battery.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import icons from "./icons"
|
||||
|
||||
export default async function init() {
|
||||
const bat = await Service.import("battery")
|
||||
bat.connect("notify::percent", ({ percent, charging }) => {
|
||||
const low = 30
|
||||
if (percent !== low || percent !== low / 2 || !charging)
|
||||
return
|
||||
|
||||
Utils.notify({
|
||||
summary: `${percent}% Battery Percentage`,
|
||||
iconName: icons.battery.warning,
|
||||
urgency: "critical",
|
||||
})
|
||||
})
|
||||
}
|
||||
16
roles/ags/files/lib/gtk.ts
Normal file
16
roles/ags/files/lib/gtk.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import Gio from "gi://Gio"
|
||||
import options from "options"
|
||||
|
||||
const settings = new Gio.Settings({
|
||||
schema: "org.gnome.desktop.interface",
|
||||
})
|
||||
|
||||
function gtk() {
|
||||
const scheme = options.theme.scheme.value
|
||||
settings.set_string("color-scheme", `prefer-${scheme}`)
|
||||
}
|
||||
|
||||
export default function init() {
|
||||
options.theme.scheme.connect("changed", gtk)
|
||||
gtk()
|
||||
}
|
||||
80
roles/ags/files/lib/hyprland.ts
Normal file
80
roles/ags/files/lib/hyprland.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import options from "options"
|
||||
const { messageAsync } = await Service.import("hyprland")
|
||||
|
||||
const {
|
||||
hyprland,
|
||||
theme: {
|
||||
spacing,
|
||||
radius,
|
||||
border: { width },
|
||||
blur,
|
||||
shadows,
|
||||
dark: {
|
||||
primary: { bg: darkActive },
|
||||
},
|
||||
light: {
|
||||
primary: { bg: lightActive },
|
||||
},
|
||||
scheme,
|
||||
},
|
||||
} = options
|
||||
|
||||
const deps = [
|
||||
"hyprland",
|
||||
spacing.id,
|
||||
radius.id,
|
||||
blur.id,
|
||||
width.id,
|
||||
shadows.id,
|
||||
darkActive.id,
|
||||
lightActive.id,
|
||||
scheme.id,
|
||||
]
|
||||
|
||||
function activeBorder() {
|
||||
const color = scheme.value === "dark"
|
||||
? darkActive.value
|
||||
: lightActive.value
|
||||
|
||||
return color.replace("#", "")
|
||||
}
|
||||
|
||||
function sendBatch(batch: string[]) {
|
||||
const cmd = batch
|
||||
.filter(x => !!x)
|
||||
.map(x => `keyword ${x}`)
|
||||
.join("; ")
|
||||
|
||||
return messageAsync(`[[BATCH]]/${cmd}`)
|
||||
}
|
||||
|
||||
async function setupHyprland() {
|
||||
const wm_gaps = Math.floor(hyprland.gaps.value * spacing.value)
|
||||
|
||||
sendBatch([
|
||||
`general:border_size ${width}`,
|
||||
`general:gaps_out ${wm_gaps}`,
|
||||
`general:gaps_in ${Math.floor(wm_gaps / 2)}`,
|
||||
`general:col.active_border rgba(${activeBorder()}ff)`,
|
||||
`general:col.inactive_border rgba(${hyprland.inactiveBorder.value})`,
|
||||
`decoration:rounding ${radius}`,
|
||||
`decoration:drop_shadow ${shadows.value ? "yes" : "no"}`,
|
||||
`dwindle:no_gaps_when_only ${hyprland.gapsWhenOnly.value ? 0 : 1}`,
|
||||
`master:no_gaps_when_only ${hyprland.gapsWhenOnly.value ? 0 : 1}`,
|
||||
])
|
||||
|
||||
await sendBatch(App.windows.map(({ name }) => `layerrule unset, ${name}`))
|
||||
|
||||
if (blur.value > 0) {
|
||||
sendBatch(App.windows.flatMap(({ name }) => [
|
||||
`layerrule unset, ${name}`,
|
||||
`layerrule blur, ${name}`,
|
||||
`layerrule ignorealpha ${/* based on shadow color */.29}, ${name}`,
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
export default function init() {
|
||||
options.handler(deps, setupHyprland)
|
||||
setupHyprland()
|
||||
}
|
||||
145
roles/ags/files/lib/icons.ts
Normal file
145
roles/ags/files/lib/icons.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
export const substitutes = {
|
||||
"transmission-gtk": "transmission",
|
||||
"blueberry.py": "blueberry",
|
||||
"Caprine": "facebook-messenger",
|
||||
"com.raggesilver.BlackBox-symbolic": "terminal-symbolic",
|
||||
"org.wezfurlong.wezterm-symbolic": "terminal-symbolic",
|
||||
"audio-headset-bluetooth": "audio-headphones-symbolic",
|
||||
"audio-card-analog-usb": "audio-speakers-symbolic",
|
||||
"audio-card-analog-pci": "audio-card-symbolic",
|
||||
"preferences-system": "emblem-system-symbolic",
|
||||
"com.github.Aylur.ags-symbolic": "controls-symbolic",
|
||||
"com.github.Aylur.ags": "controls-symbolic",
|
||||
}
|
||||
|
||||
export default {
|
||||
missing: "image-missing-symbolic",
|
||||
nix: {
|
||||
nix: "nix-snowflake-symbolic",
|
||||
},
|
||||
app: {
|
||||
terminal: "terminal-symbolic",
|
||||
},
|
||||
fallback: {
|
||||
executable: "application-x-executable",
|
||||
notification: "dialog-information-symbolic",
|
||||
video: "video-x-generic-symbolic",
|
||||
audio: "audio-x-generic-symbolic",
|
||||
},
|
||||
ui: {
|
||||
close: "window-close-symbolic",
|
||||
colorpicker: "color-select-symbolic",
|
||||
info: "info-symbolic",
|
||||
link: "external-link-symbolic",
|
||||
lock: "system-lock-screen-symbolic",
|
||||
menu: "open-menu-symbolic",
|
||||
refresh: "view-refresh-symbolic",
|
||||
search: "system-search-symbolic",
|
||||
settings: "emblem-system-symbolic",
|
||||
themes: "preferences-desktop-theme-symbolic",
|
||||
tick: "object-select-symbolic",
|
||||
time: "hourglass-symbolic",
|
||||
toolbars: "toolbars-symbolic",
|
||||
warning: "dialog-warning-symbolic",
|
||||
avatar: "avatar-default-symbolic",
|
||||
arrow: {
|
||||
right: "pan-end-symbolic",
|
||||
left: "pan-start-symbolic",
|
||||
down: "pan-down-symbolic",
|
||||
up: "pan-up-symbolic",
|
||||
},
|
||||
},
|
||||
audio: {
|
||||
mic: {
|
||||
muted: "microphone-disabled-symbolic",
|
||||
low: "microphone-sensitivity-low-symbolic",
|
||||
medium: "microphone-sensitivity-medium-symbolic",
|
||||
high: "microphone-sensitivity-high-symbolic",
|
||||
},
|
||||
volume: {
|
||||
muted: "audio-volume-muted-symbolic",
|
||||
low: "audio-volume-low-symbolic",
|
||||
medium: "audio-volume-medium-symbolic",
|
||||
high: "audio-volume-high-symbolic",
|
||||
overamplified: "audio-volume-overamplified-symbolic",
|
||||
},
|
||||
type: {
|
||||
headset: "audio-headphones-symbolic",
|
||||
speaker: "audio-speakers-symbolic",
|
||||
card: "audio-card-symbolic",
|
||||
},
|
||||
mixer: "mixer-symbolic",
|
||||
},
|
||||
powerprofile: {
|
||||
balanced: "power-profile-balanced-symbolic",
|
||||
"power-saver": "power-profile-power-saver-symbolic",
|
||||
performance: "power-profile-performance-symbolic",
|
||||
},
|
||||
asusctl: {
|
||||
profile: {
|
||||
Balanced: "power-profile-balanced-symbolic",
|
||||
Quiet: "power-profile-power-saver-symbolic",
|
||||
Performance: "power-profile-performance-symbolic",
|
||||
},
|
||||
mode: {
|
||||
Integrated: "processor-symbolic",
|
||||
Hybrid: "controller-symbolic",
|
||||
},
|
||||
},
|
||||
battery: {
|
||||
charging: "battery-flash-symbolic",
|
||||
warning: "battery-empty-symbolic",
|
||||
},
|
||||
bluetooth: {
|
||||
enabled: "bluetooth-active-symbolic",
|
||||
disabled: "bluetooth-disabled-symbolic",
|
||||
},
|
||||
brightness: {
|
||||
indicator: "display-brightness-symbolic",
|
||||
keyboard: "keyboard-brightness-symbolic",
|
||||
screen: "display-brightness-symbolic",
|
||||
},
|
||||
powermenu: {
|
||||
sleep: "weather-clear-night-symbolic",
|
||||
reboot: "system-reboot-symbolic",
|
||||
logout: "system-log-out-symbolic",
|
||||
shutdown: "system-shutdown-symbolic",
|
||||
},
|
||||
recorder: {
|
||||
recording: "media-record-symbolic",
|
||||
},
|
||||
notifications: {
|
||||
noisy: "org.gnome.Settings-notifications-symbolic",
|
||||
silent: "notifications-disabled-symbolic",
|
||||
message: "chat-bubbles-symbolic",
|
||||
},
|
||||
trash: {
|
||||
full: "user-trash-full-symbolic",
|
||||
empty: "user-trash-symbolic",
|
||||
},
|
||||
mpris: {
|
||||
shuffle: {
|
||||
enabled: "media-playlist-shuffle-symbolic",
|
||||
disabled: "media-playlist-consecutive-symbolic",
|
||||
},
|
||||
loop: {
|
||||
none: "media-playlist-repeat-symbolic",
|
||||
track: "media-playlist-repeat-song-symbolic",
|
||||
playlist: "media-playlist-repeat-symbolic",
|
||||
},
|
||||
playing: "media-playback-pause-symbolic",
|
||||
paused: "media-playback-start-symbolic",
|
||||
stopped: "media-playback-start-symbolic",
|
||||
prev: "media-skip-backward-symbolic",
|
||||
next: "media-skip-forward-symbolic",
|
||||
},
|
||||
system: {
|
||||
cpu: "org.gnome.SystemMonitor-symbolic",
|
||||
ram: "drive-harddisk-solidstate-symbolic",
|
||||
temp: "temperature-symbolic",
|
||||
},
|
||||
color: {
|
||||
dark: "dark-mode-symbolic",
|
||||
light: "light-mode-symbolic",
|
||||
},
|
||||
}
|
||||
19
roles/ags/files/lib/init.ts
Normal file
19
roles/ags/files/lib/init.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import matugen from "./matugen"
|
||||
import hyprland from "./hyprland"
|
||||
import tmux from "./tmux"
|
||||
import gtk from "./gtk"
|
||||
import lowBattery from "./battery"
|
||||
import notifications from "./notifications"
|
||||
|
||||
export default function init() {
|
||||
try {
|
||||
gtk()
|
||||
tmux()
|
||||
matugen()
|
||||
lowBattery()
|
||||
notifications()
|
||||
hyprland()
|
||||
} catch (error) {
|
||||
logError(error)
|
||||
}
|
||||
}
|
||||
113
roles/ags/files/lib/matugen.ts
Normal file
113
roles/ags/files/lib/matugen.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import wallpaper from "service/wallpaper"
|
||||
import options from "options"
|
||||
import { sh, dependencies } from "./utils"
|
||||
|
||||
export default function init() {
|
||||
wallpaper.connect("changed", () => matugen())
|
||||
options.autotheme.connect("changed", () => matugen())
|
||||
}
|
||||
|
||||
function animate(...setters: Array<() => void>) {
|
||||
const delay = options.transition.value / 2
|
||||
setters.forEach((fn, i) => Utils.timeout(delay * i, fn))
|
||||
}
|
||||
|
||||
export async function matugen(
|
||||
type: "image" | "color" = "image",
|
||||
arg = wallpaper.wallpaper,
|
||||
) {
|
||||
if (!options.autotheme.value || !dependencies("matugen"))
|
||||
return
|
||||
|
||||
const colors = await sh(`matugen --dry-run -j hex ${type} ${arg}`)
|
||||
const c = JSON.parse(colors).colors as { light: Colors, dark: Colors }
|
||||
const { dark, light } = options.theme
|
||||
|
||||
animate(
|
||||
() => {
|
||||
dark.widget.value = c.dark.on_surface
|
||||
light.widget.value = c.light.on_surface
|
||||
},
|
||||
() => {
|
||||
dark.border.value = c.dark.outline
|
||||
light.border.value = c.light.outline
|
||||
},
|
||||
() => {
|
||||
dark.bg.value = c.dark.surface
|
||||
light.bg.value = c.light.surface
|
||||
},
|
||||
() => {
|
||||
dark.fg.value = c.dark.on_surface
|
||||
light.fg.value = c.light.on_surface
|
||||
},
|
||||
() => {
|
||||
dark.primary.bg.value = c.dark.primary
|
||||
light.primary.bg.value = c.light.primary
|
||||
options.bar.battery.charging.value = options.theme.scheme.value === "dark"
|
||||
? c.dark.primary : c.light.primary
|
||||
},
|
||||
() => {
|
||||
dark.primary.fg.value = c.dark.on_primary
|
||||
light.primary.fg.value = c.light.on_primary
|
||||
},
|
||||
() => {
|
||||
dark.error.bg.value = c.dark.error
|
||||
light.error.bg.value = c.light.error
|
||||
},
|
||||
() => {
|
||||
dark.error.fg.value = c.dark.on_error
|
||||
light.error.fg.value = c.light.on_error
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type Colors = {
|
||||
background: string
|
||||
error: string
|
||||
error_container: string
|
||||
inverse_on_surface: string
|
||||
inverse_primary: string
|
||||
inverse_surface: string
|
||||
on_background: string
|
||||
on_error: string
|
||||
on_error_container: string
|
||||
on_primary: string
|
||||
on_primary_container: string
|
||||
on_primary_fixed: string
|
||||
on_primary_fixed_variant: string
|
||||
on_secondary: string
|
||||
on_secondary_container: string
|
||||
on_secondary_fixed: string
|
||||
on_secondary_fixed_variant: string
|
||||
on_surface: string
|
||||
on_surface_variant: string
|
||||
on_tertiary: string
|
||||
on_tertiary_container: string
|
||||
on_tertiary_fixed: string
|
||||
on_tertiary_fixed_variant: string
|
||||
outline: string
|
||||
outline_variant: string
|
||||
primary: string
|
||||
primary_container: string
|
||||
primary_fixed: string
|
||||
primary_fixed_dim: string
|
||||
scrim: string
|
||||
secondary: string
|
||||
secondary_container: string
|
||||
secondary_fixed: string
|
||||
secondary_fixed_dim: string
|
||||
shadow: string
|
||||
surface: string
|
||||
surface_bright: string
|
||||
surface_container: string
|
||||
surface_container_high: string
|
||||
surface_container_highest: string
|
||||
surface_container_low: string
|
||||
surface_container_lowest: string
|
||||
surface_dim: string
|
||||
surface_variant: string
|
||||
tertiary: string
|
||||
tertiary_container: string
|
||||
tertiary_fixed: string
|
||||
tertiary_fixed_dim: string
|
||||
}
|
||||
16
roles/ags/files/lib/notifications.ts
Normal file
16
roles/ags/files/lib/notifications.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import options from "options"
|
||||
const notifs = await Service.import("notifications")
|
||||
|
||||
// TODO: consider adding this to upstream
|
||||
|
||||
const { blacklist } = options.notifications
|
||||
|
||||
export default function init() {
|
||||
const notify = notifs.constructor.prototype.Notify.bind(notifs)
|
||||
notifs.constructor.prototype.Notify = function(appName: string, ...rest: unknown[]) {
|
||||
if (blacklist.value.includes(appName))
|
||||
return Number.MAX_SAFE_INTEGER
|
||||
|
||||
return notify(appName, ...rest)
|
||||
}
|
||||
}
|
||||
116
roles/ags/files/lib/option.ts
Normal file
116
roles/ags/files/lib/option.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Variable } from "resource:///com/github/Aylur/ags/variable.js"
|
||||
|
||||
type OptProps = {
|
||||
persistent?: boolean
|
||||
}
|
||||
|
||||
export class Opt<T = unknown> extends Variable<T> {
|
||||
static { Service.register(this) }
|
||||
|
||||
constructor(initial: T, { persistent = false }: OptProps = {}) {
|
||||
super(initial)
|
||||
this.initial = initial
|
||||
this.persistent = persistent
|
||||
}
|
||||
|
||||
initial: T
|
||||
id = ""
|
||||
persistent: boolean
|
||||
toString() { return `${this.value}` }
|
||||
toJSON() { return `opt:${this.value}` }
|
||||
|
||||
getValue = (): T => {
|
||||
return super.getValue()
|
||||
}
|
||||
|
||||
init(cacheFile: string) {
|
||||
const cacheV = JSON.parse(Utils.readFile(cacheFile) || "{}")[this.id]
|
||||
if (cacheV !== undefined)
|
||||
this.value = cacheV
|
||||
|
||||
this.connect("changed", () => {
|
||||
const cache = JSON.parse(Utils.readFile(cacheFile) || "{}")
|
||||
cache[this.id] = this.value
|
||||
Utils.writeFileSync(JSON.stringify(cache, null, 2), cacheFile)
|
||||
})
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (this.persistent)
|
||||
return
|
||||
|
||||
if (JSON.stringify(this.value) !== JSON.stringify(this.initial)) {
|
||||
this.value = this.initial
|
||||
return this.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const opt = <T>(initial: T, opts?: OptProps) => new Opt(initial, opts)
|
||||
|
||||
function getOptions(object: object, path = ""): Opt[] {
|
||||
return Object.keys(object).flatMap(key => {
|
||||
const obj: Opt = object[key]
|
||||
const id = path ? path + "." + key : key
|
||||
|
||||
if (obj instanceof Variable) {
|
||||
obj.id = id
|
||||
return obj
|
||||
}
|
||||
|
||||
if (typeof obj === "object")
|
||||
return getOptions(obj, id)
|
||||
|
||||
return []
|
||||
})
|
||||
}
|
||||
|
||||
export function mkOptions<T extends object>(cacheFile: string, object: T) {
|
||||
for (const opt of getOptions(object))
|
||||
opt.init(cacheFile)
|
||||
|
||||
Utils.ensureDirectory(cacheFile.split("/").slice(0, -1).join("/"))
|
||||
|
||||
const configFile = `${TMP}/config.json`
|
||||
console.log(configFile)
|
||||
const values = getOptions(object).reduce((obj, { id, value }) => ({ [id]: value, ...obj }), {})
|
||||
Utils.writeFileSync(JSON.stringify(values, null, 2), configFile)
|
||||
Utils.monitorFile(configFile, () => {
|
||||
const cache = JSON.parse(Utils.readFile(configFile) || "{}")
|
||||
for (const opt of getOptions(object)) {
|
||||
if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value))
|
||||
opt.value = cache[opt.id]
|
||||
}
|
||||
})
|
||||
|
||||
function sleep(ms = 0) {
|
||||
return new Promise(r => setTimeout(r, ms))
|
||||
}
|
||||
|
||||
async function reset(
|
||||
[opt, ...list] = getOptions(object),
|
||||
id = opt?.reset(),
|
||||
): Promise<Array<string>> {
|
||||
if (!opt)
|
||||
return sleep().then(() => [])
|
||||
|
||||
return id
|
||||
? [id, ...(await sleep(50).then(() => reset(list)))]
|
||||
: await sleep().then(() => reset(list))
|
||||
}
|
||||
|
||||
return Object.assign(object, {
|
||||
configFile,
|
||||
array: () => getOptions(object),
|
||||
async reset() {
|
||||
return (await reset()).join("\n")
|
||||
},
|
||||
handler(deps: string[], callback: () => void) {
|
||||
for (const opt of getOptions(object)) {
|
||||
if (deps.some(i => opt.id.startsWith(i)))
|
||||
opt.connect("changed", callback)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
16
roles/ags/files/lib/session.ts
Normal file
16
roles/ags/files/lib/session.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import GLib from "gi://GLib?version=2.0"
|
||||
|
||||
declare global {
|
||||
const OPTIONS: string
|
||||
const TMP: string
|
||||
const USER: string
|
||||
}
|
||||
|
||||
Object.assign(globalThis, {
|
||||
OPTIONS: `${GLib.get_user_cache_dir()}/ags/options.json`,
|
||||
TMP: `${GLib.get_tmp_dir()}/asztal`,
|
||||
USER: GLib.get_user_name(),
|
||||
})
|
||||
|
||||
Utils.ensureDirectory(TMP)
|
||||
App.addIcons(`${App.configDir}/assets`)
|
||||
14
roles/ags/files/lib/tmux.ts
Normal file
14
roles/ags/files/lib/tmux.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import options from "options"
|
||||
import { sh } from "./utils"
|
||||
|
||||
export async function tmux() {
|
||||
const { scheme, dark, light } = options.theme
|
||||
const hex = scheme.value === "dark" ? dark.primary.bg.value : light.primary.bg.value
|
||||
if (await sh("which tmux"))
|
||||
sh(`tmux set @main_accent "${hex}"`)
|
||||
}
|
||||
|
||||
export default function init() {
|
||||
options.theme.dark.primary.bg.connect("changed", tmux)
|
||||
options.theme.light.primary.bg.connect("changed", tmux)
|
||||
}
|
||||
111
roles/ags/files/lib/utils.ts
Normal file
111
roles/ags/files/lib/utils.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { type Application } from "types/service/applications"
|
||||
import icons, { substitutes } from "./icons"
|
||||
import Gtk from "gi://Gtk?version=3.0"
|
||||
import Gdk from "gi://Gdk"
|
||||
import GLib from "gi://GLib?version=2.0"
|
||||
|
||||
export type Binding<T> = import("types/service").Binding<any, any, T>
|
||||
|
||||
/**
|
||||
* @returns substitute icon || name || fallback icon
|
||||
*/
|
||||
export function icon(name: string | null, fallback = icons.missing) {
|
||||
if (!name)
|
||||
return fallback || ""
|
||||
|
||||
if (GLib.file_test(name, GLib.FileTest.EXISTS))
|
||||
return name
|
||||
|
||||
const icon = (substitutes[name] || name)
|
||||
if (Utils.lookUpIcon(icon))
|
||||
return icon
|
||||
|
||||
print(`no icon substitute "${icon}" for "${name}", fallback: "${fallback}"`)
|
||||
return fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns execAsync(["bash", "-c", cmd])
|
||||
*/
|
||||
export async function bash(strings: TemplateStringsArray | string, ...values: unknown[]) {
|
||||
const cmd = typeof strings === "string" ? strings : strings
|
||||
.flatMap((str, i) => str + `${values[i] ?? ""}`)
|
||||
.join("")
|
||||
|
||||
return Utils.execAsync(["bash", "-c", cmd]).catch(err => {
|
||||
console.error(cmd, err)
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns execAsync(cmd)
|
||||
*/
|
||||
export async function sh(cmd: string | string[]) {
|
||||
return Utils.execAsync(cmd).catch(err => {
|
||||
console.error(typeof cmd === "string" ? cmd : cmd.join(" "), err)
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
export function forMonitors(widget: (monitor: number) => Gtk.Window) {
|
||||
const n = Gdk.Display.get_default()?.get_n_monitors() || 1
|
||||
return range(n, 0).map(widget).flat(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns [start...length]
|
||||
*/
|
||||
export function range(length: number, start = 1) {
|
||||
return Array.from({ length }, (_, i) => i + start)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if all of the `bins` are found
|
||||
*/
|
||||
export function dependencies(...bins: string[]) {
|
||||
const missing = bins.filter(bin => {
|
||||
return !Utils.exec(`which ${bin}`)
|
||||
})
|
||||
|
||||
if (missing.length > 0) {
|
||||
console.warn("missing dependencies:", missing.join(", "))
|
||||
Utils.notify(`missing dependencies: ${missing.join(", ")}`)
|
||||
}
|
||||
|
||||
return missing.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* run app detached
|
||||
*/
|
||||
export function launchApp(app: Application) {
|
||||
const exe = app.executable
|
||||
.split(/\s+/)
|
||||
.filter(str => !str.startsWith("%") && !str.startsWith("@"))
|
||||
.join(" ")
|
||||
|
||||
bash(`${exe} &`)
|
||||
app.frequency += 1
|
||||
}
|
||||
|
||||
/**
|
||||
* to use with drag and drop
|
||||
*/
|
||||
export function createSurfaceFromWidget(widget: Gtk.Widget) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const cairo = imports.gi.cairo as any
|
||||
const alloc = widget.get_allocation()
|
||||
const surface = new cairo.ImageSurface(
|
||||
cairo.Format.ARGB32,
|
||||
alloc.width,
|
||||
alloc.height,
|
||||
)
|
||||
const cr = new cairo.Context(surface)
|
||||
cr.setSourceRGBA(255, 255, 255, 0)
|
||||
cr.rectangle(0, 0, alloc.width, alloc.height)
|
||||
cr.fill()
|
||||
widget.draw(cr)
|
||||
return surface
|
||||
}
|
||||
42
roles/ags/files/lib/variables.ts
Normal file
42
roles/ags/files/lib/variables.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import GLib from "gi://GLib"
|
||||
// import options from "options"
|
||||
//
|
||||
// const intval = options.system.fetchInterval.value
|
||||
// const tempPath = options.system.temperature.value
|
||||
|
||||
export const clock = Variable(GLib.DateTime.new_now_local(), {
|
||||
poll: [1000, () => GLib.DateTime.new_now_local()],
|
||||
})
|
||||
|
||||
export const uptime = Variable(0, {
|
||||
poll: [60_000, "cat /proc/uptime", line =>
|
||||
Number.parseInt(line.split(".")[0]) / 60,
|
||||
],
|
||||
})
|
||||
|
||||
export const distro = {
|
||||
id: GLib.get_os_info("ID"),
|
||||
logo: GLib.get_os_info("LOGO"),
|
||||
}
|
||||
|
||||
// const divide = ([total, free]: string[]) => Number.parseInt(free) / Number.parseInt(total)
|
||||
//
|
||||
// export const cpu = Variable(0, {
|
||||
// poll: [intval, "top -b -n 1", out => divide(["100", out.split("\n")
|
||||
// .find(line => line.includes("Cpu(s)"))
|
||||
// ?.split(/\s+/)[1]
|
||||
// .replace(",", ".") || "0"])],
|
||||
// })
|
||||
//
|
||||
// export const ram = Variable(0, {
|
||||
// poll: [intval, "free", out => divide(out.split("\n")
|
||||
// .find(line => line.includes("Mem:"))
|
||||
// ?.split(/\s+/)
|
||||
// .splice(1, 2) || ["1", "1"])],
|
||||
// })
|
||||
//
|
||||
// export const temperature = Variable(0, {
|
||||
// poll: [intval, `cat ${tempPath}`, n => {
|
||||
// return Number.parseInt(n) / 100_000
|
||||
// }],
|
||||
// })
|
||||
Reference in New Issue
Block a user