diff --git a/roles/ags/files/agsv2/.eslintrc.yml b/roles/ags/files/agsv2/.eslintrc.yml deleted file mode 100644 index ff96a83..0000000 --- a/roles/ags/files/agsv2/.eslintrc.yml +++ /dev/null @@ -1,130 +0,0 @@ -env: - es2022: true -extends: - - "eslint:recommended" - - "plugin:@typescript-eslint/recommended" -parser: "@typescript-eslint/parser" -parserOptions: - ecmaVersion: 2022 - sourceType: "module" - project: "./tsconfig.json" - warnOnUnsupportedTypeScriptVersion: false -root: true -ignorePatterns: - - types/ -plugins: - - "@typescript-eslint" -rules: - "@typescript-eslint/ban-ts-comment": - - "off" - "@typescript-eslint/no-non-null-assertion": - - "off" - # "@typescript-eslint/no-explicit-any": - # - "off" - "@typescript-eslint/no-unused-vars": - - error - - varsIgnorePattern: (^unused|_$) - argsIgnorePattern: ^(unused|_) - "@typescript-eslint/no-empty-interface": - - "off" - - arrow-parens: - - error - - as-needed - comma-dangle: - - error - - always-multiline - comma-spacing: - - error - - before: false - after: true - comma-style: - - error - - last - curly: - - error - - multi-or-nest - - consistent - dot-location: - - error - - property - eol-last: - - error - eqeqeq: - - error - - always - indent: - - error - - 4 - - SwitchCase: 1 - keyword-spacing: - - error - - before: true - lines-between-class-members: - - error - - always - - exceptAfterSingleLine: true - padded-blocks: - - error - - never - - allowSingleLineBlocks: false - prefer-const: - - error - quotes: - - error - - double - - avoidEscape: true - semi: - - error - - never - nonblock-statement-body-position: - - error - - below - no-trailing-spaces: - - error - no-useless-escape: - - off - max-len: - - error - - code: 100 - func-call-spacing: - - error - array-bracket-spacing: - - error - space-before-function-paren: - - error - - anonymous: never - named: never - asyncArrow: ignore - space-before-blocks: - - error - key-spacing: - - error - object-curly-spacing: - - error - - always -globals: - Widget: readonly - Utils: readonly - App: readonly - Variable: readonly - Service: readonly - pkg: readonly - ARGV: readonly - Debugger: readonly - GIRepositoryGType: readonly - globalThis: readonly - imports: readonly - Intl: readonly - log: readonly - logError: readonly - print: readonly - printerr: readonly - window: readonly - TextEncoder: readonly - TextDecoder: readonly - console: readonly - setTimeout: readonly - setInterval: readonly - clearTimeout: readonly - clearInterval: readonly diff --git a/roles/ags/files/agsv2/.gitignore b/roles/ags/files/agsv2/.gitignore deleted file mode 100644 index f56dbd1..0000000 --- a/roles/ags/files/agsv2/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -types -package-lock.json -bun.lockb -flake.lock -.weather diff --git a/roles/ags/files/agsv2/assets/airdrop-symbolic.svg b/roles/ags/files/agsv2/assets/airdrop-symbolic.svg deleted file mode 100644 index 90a2cba..0000000 --- a/roles/ags/files/agsv2/assets/airdrop-symbolic.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/roles/ags/files/agsv2/assets/battery-flash-symbolic.svg b/roles/ags/files/agsv2/assets/battery-flash-symbolic.svg deleted file mode 100644 index 21b5e33..0000000 --- a/roles/ags/files/agsv2/assets/battery-flash-symbolic.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/roles/ags/files/agsv2/assets/chat-bubbles-symbolic.svg b/roles/ags/files/agsv2/assets/chat-bubbles-symbolic.svg deleted file mode 100644 index fdee0b3..0000000 --- a/roles/ags/files/agsv2/assets/chat-bubbles-symbolic.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/roles/ags/files/agsv2/assets/controller-symbolic.svg b/roles/ags/files/agsv2/assets/controller-symbolic.svg deleted file mode 100644 index 98bf5d6..0000000 --- a/roles/ags/files/agsv2/assets/controller-symbolic.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/roles/ags/files/agsv2/assets/controls-symbolic.svg b/roles/ags/files/agsv2/assets/controls-symbolic.svg deleted file mode 100644 index 7df5663..0000000 --- a/roles/ags/files/agsv2/assets/controls-symbolic.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/roles/ags/files/agsv2/assets/dark-mode-symbolic.svg b/roles/ags/files/agsv2/assets/dark-mode-symbolic.svg deleted file mode 100644 index 9f2e6b4..0000000 --- a/roles/ags/files/agsv2/assets/dark-mode-symbolic.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/roles/ags/files/agsv2/assets/hourglass-symbolic.svg b/roles/ags/files/agsv2/assets/hourglass-symbolic.svg deleted file mode 100644 index aa4f97c..0000000 --- a/roles/ags/files/agsv2/assets/hourglass-symbolic.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/roles/ags/files/agsv2/assets/light-mode-symbolic.svg b/roles/ags/files/agsv2/assets/light-mode-symbolic.svg deleted file mode 100644 index d5fb271..0000000 --- a/roles/ags/files/agsv2/assets/light-mode-symbolic.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/roles/ags/files/agsv2/assets/mixer-symbolic.svg b/roles/ags/files/agsv2/assets/mixer-symbolic.svg deleted file mode 100644 index ad6cfa8..0000000 --- a/roles/ags/files/agsv2/assets/mixer-symbolic.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/roles/ags/files/agsv2/assets/nix-snowflake-symbolic.svg b/roles/ags/files/agsv2/assets/nix-snowflake-symbolic.svg deleted file mode 100644 index 7bb42ed..0000000 --- a/roles/ags/files/agsv2/assets/nix-snowflake-symbolic.svg +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roles/ags/files/agsv2/assets/preferences-desktop-theme-symbolic.svg b/roles/ags/files/agsv2/assets/preferences-desktop-theme-symbolic.svg deleted file mode 100644 index 4461454..0000000 --- a/roles/ags/files/agsv2/assets/preferences-desktop-theme-symbolic.svg +++ /dev/null @@ -1,321 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roles/ags/files/agsv2/assets/processor-symbolic.svg b/roles/ags/files/agsv2/assets/processor-symbolic.svg deleted file mode 100644 index 832dbaf..0000000 --- a/roles/ags/files/agsv2/assets/processor-symbolic.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/roles/ags/files/agsv2/assets/terminal-symbolic.svg b/roles/ags/files/agsv2/assets/terminal-symbolic.svg deleted file mode 100644 index 9f82bcf..0000000 --- a/roles/ags/files/agsv2/assets/terminal-symbolic.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/roles/ags/files/agsv2/assets/toolbars-symbolic.svg b/roles/ags/files/agsv2/assets/toolbars-symbolic.svg deleted file mode 100644 index 9f4c564..0000000 --- a/roles/ags/files/agsv2/assets/toolbars-symbolic.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/roles/ags/files/agsv2/config.js b/roles/ags/files/agsv2/config.js deleted file mode 100644 index 6475b9e..0000000 --- a/roles/ags/files/agsv2/config.js +++ /dev/null @@ -1,51 +0,0 @@ -import GLib from "gi://GLib" - -const main = "/tmp/asztal/main.js" -const entry = `${App.configDir}/main.ts` -const bundler = GLib.getenv("AGS_BUNDLER") || "bun" - -const v = { - ags: pkg.version?.split(".").map(Number) || [], - expect: [1, 8, 1], -} - -try { - switch (bundler) { - case "bun": await Utils.execAsync([ - "bun", "build", entry, - "--outfile", main, - "--external", "resource://*", - "--external", "gi://*", - "--external", "file://*", - ]); break - - case "esbuild": await Utils.execAsync([ - "esbuild", "--bundle", entry, - "--format=esm", - `--outfile=${main}`, - "--external:resource://*", - "--external:gi://*", - "--external:file://*", - ]); break - - default: - throw `"${bundler}" is not a valid bundler` - } - - if (v.ags[0] < v.expect[0] && v.ags[1] < v.expect[1] && v.ags[2] < v.expect[2]) { - print(`my config needs at least v${v.expect.join(".")}, yours is v${v.ags.join(".")}`) - App.quit() - } - - if (v.ags[0] > v.expect[0]) { - print(`my config doesn't work on ags versions higher than v${v.expect[0]}, yours is v${v.ags[0]}`) - App.quit() - } - - await import(`file://${main}`) -} catch (error) { - console.error(error) - App.quit() -} - -export { } diff --git a/roles/ags/files/agsv2/default.nix b/roles/ags/files/agsv2/default.nix deleted file mode 100644 index ad2b77f..0000000 --- a/roles/ags/files/agsv2/default.nix +++ /dev/null @@ -1,103 +0,0 @@ -{ - inputs, - writeShellScript, - system, - stdenv, - cage, - swww, - esbuild, - dart-sass, - fd, - fzf, - brightnessctl, - accountsservice, - slurp, - wf-recorder, - wl-clipboard, - wayshot, - swappy, - hyprpicker, - pavucontrol, - networkmanager, - gtk3, - which, -}: let - name = "asztal"; - - ags = inputs.ags.packages.${system}.default.override { - extraPackages = [accountsservice]; - }; - - dependencies = [ - which - dart-sass - fd - fzf - brightnessctl - swww - inputs.matugen.packages.${system}.default - slurp - wf-recorder - wl-clipboard - wayshot - swappy - hyprpicker - pavucontrol - networkmanager - gtk3 - ]; - - addBins = list: builtins.concatStringsSep ":" (builtins.map (p: "${p}/bin") list); - - greeter = writeShellScript "greeter" '' - export PATH=$PATH:${addBins dependencies} - ${cage}/bin/cage -ds -m last ${ags}/bin/ags -- -c ${config}/greeter.js - ''; - - desktop = writeShellScript name '' - export PATH=$PATH:${addBins dependencies} - ${ags}/bin/ags -b ${name} -c ${config}/config.js $@ - ''; - - config = stdenv.mkDerivation { - inherit name; - src = ./.; - - buildPhase = '' - ${esbuild}/bin/esbuild \ - --bundle ./main.ts \ - --outfile=main.js \ - --format=esm \ - --external:resource://\* \ - --external:gi://\* \ - - ${esbuild}/bin/esbuild \ - --bundle ./greeter/greeter.ts \ - --outfile=greeter.js \ - --format=esm \ - --external:resource://\* \ - --external:gi://\* \ - ''; - - installPhase = '' - mkdir -p $out - cp -r assets $out - cp -r style $out - cp -r greeter $out - cp -r widget $out - cp -f main.js $out/config.js - cp -f greeter.js $out/greeter.js - ''; - }; -in - stdenv.mkDerivation { - inherit name; - src = config; - - installPhase = '' - mkdir -p $out/bin - cp -r . $out - cp ${desktop} $out/bin/${name} - cp ${greeter} $out/bin/greeter - ''; - } diff --git a/roles/ags/files/agsv2/greeter.js b/roles/ags/files/agsv2/greeter.js deleted file mode 100644 index 5c8e369..0000000 --- a/roles/ags/files/agsv2/greeter.js +++ /dev/null @@ -1,18 +0,0 @@ -const main = "/tmp/ags/greeter.js" -const entry = `${App.configDir}/greeter/greeter.ts` - -try { - await Utils.execAsync([ - "bun", "build", entry, - "--outfile", main, - "--external", "resource://*", - "--external", "gi://*", - "--external", "file://*", - ]) - await import(`file://${main}`) -} catch (error) { - console.error(error) - App.quit() -} - -export { } diff --git a/roles/ags/files/agsv2/greeter/auth.ts b/roles/ags/files/agsv2/greeter/auth.ts deleted file mode 100644 index eb5d825..0000000 --- a/roles/ags/files/agsv2/greeter/auth.ts +++ /dev/null @@ -1,114 +0,0 @@ -import GLib from "gi://GLib?version=2.0" -import icons from "lib/icons" -import { bash } from "lib/utils" - -const userName = await bash("find /home -maxdepth 1 -printf '%f\n' | tail -n 1") -const iconFile = `/var/lib/AccountsService/icons/${userName}` - -// FIXME: AccountsService crashes? -// import AccountsService from "gi://AccountsService?version=1.0" -// const { iconFile, realName, userName } = AccountsService.UserManager -// .get_default().list_users()[0] - -const loggingin = Variable(false) - -const CMD = GLib.getenv("ASZTAL_DM_CMD") - || "Hyprland &> $HOME/.cache/current_session.txt" - -const ENV = GLib.getenv("ASZTAL_DM_ENV") - || "WLR_NO_HARDWARE_CURSORS=1 _JAVA_AWT_WM_NONREPARENTING=1" - -async function login(pw: string) { - loggingin.value = true - const greetd = await Service.import("greetd") - return greetd.login(userName, pw, CMD, ENV.split(/\s+/)) - .catch(res => { - loggingin.value = false - response.label = res?.description || JSON.stringify(res) - password.text = "" - revealer.reveal_child = true - }) -} - -const avatar = Widget.Box({ - class_name: "avatar", - hpack: "center", - css: `background-image: url('${iconFile}')`, -}) - -const password = Widget.Entry({ - placeholder_text: "Password", - hexpand: true, - visibility: false, - on_accept: ({ text }) => { login(text || "") }, -}) - -const response = Widget.Label({ - class_name: "response", - wrap: true, - max_width_chars: 35, - hpack: "center", - hexpand: true, - xalign: .5, -}) - -const revealer = Widget.Revealer({ - transition: "slide_down", - child: response, -}) - -export default Widget.Box({ - class_name: "auth", - attribute: { password }, - vertical: true, - children: [ - Widget.Overlay({ - child: Widget.Box( - { - css: "min-width: 200px; min-height: 200px;", - vertical: true, - }, - Widget.Box({ - class_name: "wallpaper", - css: `background-image: url('${WALLPAPER}')`, - }), - Widget.Box({ - class_name: "wallpaper-contrast", - vexpand: true, - }), - ), - overlay: Widget.Box( - { - vpack: "end", - vertical: true, - }, - avatar, - Widget.Box({ - hpack: "center", - children: [ - Widget.Icon(icons.ui.avatar), - Widget.Label(userName), - ], - }), - Widget.Box( - { - class_name: "password", - }, - Widget.Spinner({ - visible: loggingin.bind(), - active: true, - }), - Widget.Icon({ - visible: loggingin.bind().as(b => !b), - icon: icons.ui.lock, - }), - password, - ), - ), - }), - Widget.Box( - { class_name: "response-box" }, - revealer, - ), - ], -}) diff --git a/roles/ags/files/agsv2/greeter/greeter.ts b/roles/ags/files/agsv2/greeter/greeter.ts deleted file mode 100644 index eb1493f..0000000 --- a/roles/ags/files/agsv2/greeter/greeter.ts +++ /dev/null @@ -1,37 +0,0 @@ -import "./session" -import "style/style" -import GLib from "gi://GLib?version=2.0" -import RegularWindow from "widget/RegularWindow" -import statusbar from "./statusbar" -import auth from "./auth" - -const win = RegularWindow({ - name: "greeter", - setup: self => { - self.set_default_size(500, 500) - self.show_all() - auth.attribute.password.grab_focus() - }, - child: Widget.Overlay({ - child: Widget.Box({ expand: true }), - overlays: [ - Widget.Box({ - vpack: "start", - hpack: "fill", - hexpand: true, - child: statusbar, - }), - Widget.Box({ - vpack: "center", - hpack: "center", - child: auth, - }), - ], - }), -}) - -App.config({ - icons: "./assets", - windows: [win], - cursorTheme: GLib.getenv("XCURSOR_THEME")!, -}) diff --git a/roles/ags/files/agsv2/greeter/session.ts b/roles/ags/files/agsv2/greeter/session.ts deleted file mode 100644 index b2e6f80..0000000 --- a/roles/ags/files/agsv2/greeter/session.ts +++ /dev/null @@ -1,23 +0,0 @@ -import GLib from "gi://GLib?version=2.0" -import { bash } from "lib/utils" - -// import AccountsService from "gi://AccountsService?version=1.0" -// const { userName } = AccountsService.UserManager.get_default().list_users()[0] - -const userName = await bash("find /home -maxdepth 1 -printf '%f\n' | tail -n 1") - -declare global { - const WALLPAPER: string -} - -Object.assign(globalThis, { - TMP: `${GLib.get_tmp_dir()}/greeter`, - OPTIONS: "/var/cache/greeter/options.json", - WALLPAPER: "/var/cache/greeter/background", - // TMP: "/tmp/ags", - // OPTIONS: Utils.CACHE_DIR + "/options.json", - // WALLPAPER: Utils.HOME + "/.config/background", - USER: userName, -}) - -Utils.ensureDirectory(TMP) diff --git a/roles/ags/files/agsv2/greeter/statusbar.ts b/roles/ags/files/agsv2/greeter/statusbar.ts deleted file mode 100644 index 8076011..0000000 --- a/roles/ags/files/agsv2/greeter/statusbar.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { clock } from "lib/variables" -import options from "options" -import icons from "lib/icons" -import BatteryBar from "widget/bar/buttons/BatteryBar" -import PanelButton from "widget/bar/PanelButton" - -const { scheme } = options.theme -const { monochrome } = options.bar.powermenu -const { format } = options.bar.date - -const poweroff = PanelButton({ - class_name: "powermenu", - child: Widget.Icon(icons.powermenu.shutdown), - on_clicked: () => Utils.exec("shutdown now"), - setup: self => self.hook(monochrome, () => { - self.toggleClassName("colored", !monochrome.value) - self.toggleClassName("box") - }), -}) - -const date = PanelButton({ - class_name: "date", - child: Widget.Label({ - label: clock.bind().as(c => c.format(`${format}`)!), - }), -}) - -const darkmode = PanelButton({ - class_name: "darkmode", - child: Widget.Icon({ icon: scheme.bind().as(s => icons.color[s]) }), - on_clicked: () => scheme.value = scheme.value === "dark" ? "light" : "dark", -}) - -export default Widget.CenterBox({ - class_name: "bar", - hexpand: true, - center_widget: date, - end_widget: Widget.Box({ - hpack: "end", - children: [ - darkmode, - BatteryBar(), - poweroff, - ], - }), -}) diff --git a/roles/ags/files/agsv2/lib/battery.ts b/roles/ags/files/agsv2/lib/battery.ts deleted file mode 100644 index 3817260..0000000 --- a/roles/ags/files/agsv2/lib/battery.ts +++ /dev/null @@ -1,16 +0,0 @@ -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", - }) - }) -} diff --git a/roles/ags/files/agsv2/lib/gtk.ts b/roles/ags/files/agsv2/lib/gtk.ts deleted file mode 100644 index 8cd60a3..0000000 --- a/roles/ags/files/agsv2/lib/gtk.ts +++ /dev/null @@ -1,16 +0,0 @@ -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() -} diff --git a/roles/ags/files/agsv2/lib/hyprland.ts b/roles/ags/files/agsv2/lib/hyprland.ts deleted file mode 100644 index 95a6d14..0000000 --- a/roles/ags/files/agsv2/lib/hyprland.ts +++ /dev/null @@ -1,82 +0,0 @@ -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 primary() { - return scheme.value === "dark" - ? darkActive.value - : lightActive.value -} - -function rgba(color: string) { - return `rgba(${color}ff)`.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(primary())}`, - `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() -} diff --git a/roles/ags/files/agsv2/lib/icons.ts b/roles/ags/files/agsv2/lib/icons.ts deleted file mode 100644 index d77cf42..0000000 --- a/roles/ags/files/agsv2/lib/icons.ts +++ /dev/null @@ -1,148 +0,0 @@ -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", - "Alacritty-symbolic": "terminal-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: { - airdrop: "airdrop-symbolic", - 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", - lock: "system-lock-screen-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", - }, -} diff --git a/roles/ags/files/agsv2/lib/init.ts b/roles/ags/files/agsv2/lib/init.ts deleted file mode 100644 index aa03300..0000000 --- a/roles/ags/files/agsv2/lib/init.ts +++ /dev/null @@ -1,19 +0,0 @@ -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) - } -} diff --git a/roles/ags/files/agsv2/lib/matugen.ts b/roles/ags/files/agsv2/lib/matugen.ts deleted file mode 100644 index dfccccf..0000000 --- a/roles/ags/files/agsv2/lib/matugen.ts +++ /dev/null @@ -1,113 +0,0 @@ -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 -} diff --git a/roles/ags/files/agsv2/lib/notifications.ts b/roles/ags/files/agsv2/lib/notifications.ts deleted file mode 100644 index 0000831..0000000 --- a/roles/ags/files/agsv2/lib/notifications.ts +++ /dev/null @@ -1,16 +0,0 @@ -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) - } -} diff --git a/roles/ags/files/agsv2/lib/option.ts b/roles/ags/files/agsv2/lib/option.ts deleted file mode 100644 index 2d73978..0000000 --- a/roles/ags/files/agsv2/lib/option.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Variable } from "resource:///com/github/Aylur/ags/variable.js" - -type OptProps = { - persistent?: boolean -} - -export class Opt extends Variable { - 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 = (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(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` - 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> { - 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) - } - }, - }) -} - diff --git a/roles/ags/files/agsv2/lib/session.ts b/roles/ags/files/agsv2/lib/session.ts deleted file mode 100644 index 0e3e0cf..0000000 --- a/roles/ags/files/agsv2/lib/session.ts +++ /dev/null @@ -1,16 +0,0 @@ -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`) diff --git a/roles/ags/files/agsv2/lib/tmux.ts b/roles/ags/files/agsv2/lib/tmux.ts deleted file mode 100644 index 210bd21..0000000 --- a/roles/ags/files/agsv2/lib/tmux.ts +++ /dev/null @@ -1,14 +0,0 @@ -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").catch(() => false)) - 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) -} diff --git a/roles/ags/files/agsv2/lib/utils.ts b/roles/ags/files/agsv2/lib/utils.ts deleted file mode 100644 index 425f455..0000000 --- a/roles/ags/files/agsv2/lib/utils.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* 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 = import("types/service").Binding - -/** - * @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).flatMap(widget) -} - -/** - * @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 => Utils.exec({ - cmd: `which ${bin}`, - out: () => false, - err: () => true, - })) - - if (missing.length > 0) { - console.warn(Error(`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 -} diff --git a/roles/ags/files/agsv2/lib/variables.ts b/roles/ags/files/agsv2/lib/variables.ts deleted file mode 100644 index 19308cd..0000000 --- a/roles/ags/files/agsv2/lib/variables.ts +++ /dev/null @@ -1,42 +0,0 @@ -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 -// }], -// }) diff --git a/roles/ags/files/agsv2/main.ts b/roles/ags/files/agsv2/main.ts deleted file mode 100644 index 56ab63b..0000000 --- a/roles/ags/files/agsv2/main.ts +++ /dev/null @@ -1,44 +0,0 @@ -import "lib/session" -import "style/style" -import init from "lib/init" -import options from "options" -import Bar from "widget/bar/Bar" -import Launcher from "widget/launcher/Launcher" -import NotificationPopups from "widget/notifications/NotificationPopups" -import OSD from "widget/osd/OSD" -import Overview from "widget/overview/Overview" -import PowerMenu from "widget/powermenu/PowerMenu" -import ScreenCorners from "widget/bar/ScreenCorners" -import SettingsDialog from "widget/settings/SettingsDialog" -import Verification from "widget/powermenu/Verification" -import Shortcuts from "widget/keybinds/index" -import { forMonitors } from "lib/utils" -import { setupQuickSettings } from "widget/quicksettings/QuickSettings" -import { setupDateMenu } from "widget/datemenu/DateMenu" - -App.config({ - onConfigParsed: () => { - setupQuickSettings() - setupDateMenu() - init() - }, - closeWindowDelay: { - "launcher": options.transition.value, - "overview": options.transition.value, - "quicksettings": options.transition.value, - "datemenu": options.transition.value, - "shortcuts": options.transition.value, - }, - windows: () => [ - ...forMonitors(Bar), - ...forMonitors(NotificationPopups), - ...forMonitors(ScreenCorners), - ...forMonitors(OSD), - Launcher(), - Overview(), - PowerMenu(), - SettingsDialog(), - Verification(), - Shortcuts(), - ], -}) diff --git a/roles/ags/files/agsv2/options.ts b/roles/ags/files/agsv2/options.ts deleted file mode 100644 index 1be8c42..0000000 --- a/roles/ags/files/agsv2/options.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { opt, mkOptions } from "lib/option" -import { distro } from "lib/variables" -import { icon } from "lib/utils" -import icons from "lib/icons" - -const options = mkOptions(OPTIONS, { - autotheme: opt(true), - - wallpaper: { - resolution: opt(1920), - market: opt("random"), - }, - - theme: { - dark: { - primary: { - bg: opt("#51a4e7"), - fg: opt("#141414"), - }, - error: { - bg: opt("#e55f86"), - fg: opt("#141414"), - }, - bg: opt("#171717"), - fg: opt("#eeeeee"), - widget: opt("#eeeeee"), - border: opt("#eeeeee"), - }, - light: { - primary: { - bg: opt("#426ede"), - fg: opt("#eeeeee"), - }, - error: { - bg: opt("#b13558"), - fg: opt("#eeeeee"), - }, - bg: opt("#fffffa"), - fg: opt("#080808"), - widget: opt("#080808"), - border: opt("#080808"), - }, - - blur: opt(0), - scheme: opt<"dark" | "light">("dark"), - widget: { opacity: opt(94) }, - border: { - width: opt(1), - opacity: opt(96), - }, - - shadows: opt(true), - padding: opt(7), - spacing: opt(12), - radius: opt(11), - }, - - transition: opt(200), - - font: { - size: opt(13), - name: opt("Ubuntu Nerd Font"), - }, - - bar: { - flatButtons: opt(true), - position: opt<"top" | "bottom">("top"), - corners: opt(50), - transparent: opt(true), - layout: { - start: opt>([ - "launcher", - "workspaces", - "taskbar", - "expander", - "messages", - ]), - center: opt>([ - "date", - ]), - end: opt>([ - "media", - "expander", - "systray", - "colorpicker", - "screenrecord", - "system", - "battery", - "powermenu", - ]), - }, - launcher: { - icon: { - colored: opt(true), - icon: opt(icon(distro.logo, icons.ui.search)), - }, - label: { - colored: opt(false), - label: opt(" Apps"), - }, - action: opt(() => App.toggleWindow("launcher")), - }, - date: { - format: opt("%H:%M - %A %e."), - action: opt(() => App.toggleWindow("datemenu")), - }, - battery: { - bar: opt<"hidden" | "regular" | "whole">("regular"), - charging: opt("#00D787"), - percentage: opt(true), - blocks: opt(7), - width: opt(50), - low: opt(30), - }, - workspaces: { - workspaces: opt(7), - }, - taskbar: { - iconSize: opt(0), - monochrome: opt(true), - exclusive: opt(false), - }, - messages: { - action: opt(() => App.toggleWindow("datemenu")), - }, - systray: { - ignore: opt([ - "KDE Connect Indicator", - "spotify-client", - ]), - }, - media: { - monochrome: opt(true), - preferred: opt("spotify"), - direction: opt<"left" | "right">("right"), - format: opt("{artists} - {title}"), - length: opt(40), - }, - powermenu: { - monochrome: opt(false), - action: opt(() => App.toggleWindow("powermenu")), - }, - }, - - launcher: { - width: opt(0), - margin: opt(80), - nix: { - pkgs: opt("nixpkgs/nixos-unstable"), - max: opt(8), - }, - sh: { - max: opt(16), - }, - apps: { - iconSize: opt(62), - max: opt(6), - favorites: opt([ - [ - "zen-browser", - "wezterm", - "org.gnome.Nautilus", - "org.gnome.Calendar", - "spotify", - ], - ]), - }, - }, - - overview: { - scale: opt(9), - workspaces: opt(7), - monochromeIcon: opt(true), - }, - - powermenu: { - sleep: opt("systemctl suspend"), - reboot: opt("systemctl reboot"), - logout: opt("pkill Hyprland"), - shutdown: opt("shutdown now"), - lock: opt("sh -c 'pidof hyprlock || hyprlock'"), - layout: opt<"line" | "box">("line"), - labels: opt(true), - }, - - quicksettings: { - avatar: { - image: opt(`/var/lib/AccountsService/icons/${Utils.USER}`), - size: opt(70), - }, - width: opt(380), - position: opt<"left" | "center" | "right">("right"), - localSend: opt("localsend"), - bluetoothSettings: opt("wezterm -e sh -c 'printf \"\\033]0;Bluetooth Manager TUI\\007\"; bluetuith'"), - networkSettings: opt("wezterm -e sh -c 'printf \"\\033]0;Network Manager TUI\\007\"; nmtui'"), - media: { - monochromeIcon: opt(true), - coverSize: opt(100), - }, - }, - - datemenu: { - position: opt<"left" | "center" | "right">("center"), - weather: { - interval: opt(60_000), - unit: opt<"metric" | "imperial" | "standard">("metric"), - key: opt( - JSON.parse(Utils.readFile(`${App.configDir}/.weather`) || "{}")?.key || "", - ), - cities: opt>( - JSON.parse(Utils.readFile(`${App.configDir}/.weather`) || "{}")?.cities || [], - ), - }, - }, - - osd: { - progress: { - vertical: opt(true), - pack: { - h: opt<"start" | "center" | "end">("end"), - v: opt<"start" | "center" | "end">("center"), - }, - }, - microphone: { - pack: { - h: opt<"start" | "center" | "end">("center"), - v: opt<"start" | "center" | "end">("end"), - }, - }, - }, - - notifications: { - position: opt>(["top", "right"]), - blacklist: opt(["Spotify"]), - width: opt(440), - }, - - hyprland: { - gaps: opt(2.4), - inactiveBorder: opt("#282828"), - gapsWhenOnly: opt(true), - }, -}) - -globalThis["options"] = options -export default options diff --git a/roles/ags/files/agsv2/package.json b/roles/ags/files/agsv2/package.json deleted file mode 100644 index bc86b1b..0000000 --- a/roles/ags/files/agsv2/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "ags-dotfiles", - "author": "Aylur", - "kofi": "https://ko-fi.com/aylur", - "repository": { - "type": "git", - "url": "git+https://github.com/Aylur/dotfiles.git" - }, - "devDependencies": { - "@girs/accountsservice-1.0": "^1.0.0-3.2.7", - "@typescript-eslint/eslint-plugin": "^6.20.0", - "eslint": "^8.56.0", - "eslint-config-standard-with-typescript": "^43.0.1", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-promise": "^6.1.1", - "typescript": "^5.3.3" - } -} diff --git a/roles/ags/files/agsv2/service/asusctl.ts b/roles/ags/files/agsv2/service/asusctl.ts deleted file mode 100644 index 6c2d071..0000000 --- a/roles/ags/files/agsv2/service/asusctl.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { sh } from "lib/utils" - -type Profile = "Performance" | "Balanced" | "Quiet" -type Mode = "Hybrid" | "Integrated" - -class Asusctl extends Service { - static { - Service.register(this, {}, { - "profile": ["string", "r"], - "mode": ["string", "r"], - }) - } - - get available() { - return Utils.exec("which asusctl", () => true, () => false) - } - - #profile: Profile = "Balanced" - #mode: Mode = "Hybrid" - - async nextProfile() { - await sh("asusctl profile -n") - const profile = await sh("asusctl profile -p") - const p = profile.split(" ")[3] as Profile - this.#profile = p - this.changed("profile") - } - - async setProfile(prof: Profile) { - await sh(`asusctl profile --profile-set ${prof}`) - this.#profile = prof - this.changed("profile") - } - - async nextMode() { - await sh(`supergfxctl -m ${this.#mode === "Hybrid" ? "Integrated" : "Hybrid"}`) - this.#mode = await sh("supergfxctl -g") as Mode - this.changed("profile") - } - - constructor() { - super() - - if (this.available) { - sh("asusctl profile -p").then(p => this.#profile = p.split(" ")[3] as Profile) - sh("supergfxctl -g").then(m => this.#mode = m as Mode) - } - } - - get profiles(): Profile[] { return ["Performance", "Balanced", "Quiet"] } - get profile() { return this.#profile } - get mode() { return this.#mode } -} - -export default new Asusctl diff --git a/roles/ags/files/agsv2/service/brightness.ts b/roles/ags/files/agsv2/service/brightness.ts deleted file mode 100644 index f229ebc..0000000 --- a/roles/ags/files/agsv2/service/brightness.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { bash, dependencies, sh } from "lib/utils" - -if (!dependencies("brightnessctl")) - App.quit() - -const get = (args: string) => Number(Utils.exec(`brightnessctl ${args}`)) -const screen = await bash`ls -w1 /sys/class/backlight | head -1` -const kbd = await bash`ls -w1 /sys/class/leds | head -1` - -class Brightness extends Service { - static { - Service.register(this, {}, { - "screen": ["float", "rw"], - "kbd": ["int", "rw"], - }) - } - - #kbdMax = get(`--device ${kbd} max`) - #kbd = get(`--device ${kbd} get`) - #screenMax = get("max") - #screen = get("get") / (get("max") || 1) - - get kbd() { return this.#kbd } - get screen() { return this.#screen } - - set kbd(value) { - if (value < 0 || value > this.#kbdMax) - return - - sh(`brightnessctl -d ${kbd} s ${value} -q`).then(() => { - this.#kbd = value - this.changed("kbd") - }) - } - - set screen(percent) { - if (percent < 0) - percent = 0 - - if (percent > 1) - percent = 1 - - sh(`brightnessctl set ${Math.floor(percent * 100)}% -q`).then(() => { - this.#screen = percent - this.changed("screen") - }) - } - - constructor() { - super() - - const screenPath = `/sys/class/backlight/${screen}/brightness` - const kbdPath = `/sys/class/leds/${kbd}/brightness` - - Utils.monitorFile(screenPath, async f => { - const v = await Utils.readFileAsync(f) - this.#screen = Number(v) / this.#screenMax - this.changed("screen") - }) - - Utils.monitorFile(kbdPath, async f => { - const v = await Utils.readFileAsync(f) - this.#kbd = Number(v) / this.#kbdMax - this.changed("kbd") - }) - } -} - -export default new Brightness diff --git a/roles/ags/files/agsv2/service/colorpicker.ts b/roles/ags/files/agsv2/service/colorpicker.ts deleted file mode 100644 index 5918f31..0000000 --- a/roles/ags/files/agsv2/service/colorpicker.ts +++ /dev/null @@ -1,56 +0,0 @@ -import icons from "lib/icons" -import { bash, dependencies } from "lib/utils" - -const COLORS_CACHE = Utils.CACHE_DIR + "/colorpicker.json" -const MAX_NUM_COLORS = 10 - -class ColorPicker extends Service { - static { - Service.register(this, {}, { - "colors": ["jsobject"], - }) - } - - #notifID = 0 - #colors = JSON.parse(Utils.readFile(COLORS_CACHE) || "[]") as string[] - - get colors() { return [...this.#colors] } - set colors(colors) { - this.#colors = colors - this.changed("colors") - } - - // TODO: doesn't work? - async wlCopy(color: string) { - if (dependencies("wl-copy")) - bash(`wl-copy ${color}`) - } - - readonly pick = async () => { - if (!dependencies("hyprpicker")) - return - - const color = await bash("hyprpicker -a -r") - if (!color) - return - - this.wlCopy(color) - const list = this.colors - if (!list.includes(color)) { - list.push(color) - if (list.length > MAX_NUM_COLORS) - list.shift() - - this.colors = list - Utils.writeFile(JSON.stringify(list, null, 2), COLORS_CACHE) - } - - this.#notifID = await Utils.notify({ - id: this.#notifID, - iconName: icons.ui.colorpicker, - summary: color, - }) - } -} - -export default new ColorPicker diff --git a/roles/ags/files/agsv2/service/nix.ts b/roles/ags/files/agsv2/service/nix.ts deleted file mode 100644 index 72392bd..0000000 --- a/roles/ags/files/agsv2/service/nix.ts +++ /dev/null @@ -1,110 +0,0 @@ -import icons from "lib/icons" -import { bash, dependencies } from "lib/utils" -import options from "options" - -const CACHE = `${Utils.CACHE_DIR}/nixpkgs` -const PREFIX = "legacyPackages.x86_64-linux." -const MAX = options.launcher.nix.max -const nixpkgs = options.launcher.nix.pkgs - -export type Nixpkg = { - name: string - description: string - pname: string - version: string -} - -class Nix extends Service { - static { - Service.register(this, {}, { - "available": ["boolean", "r"], - "ready": ["boolean", "rw"], - }) - } - - #db: { [name: string]: Nixpkg } = {} - #ready = true - - private set ready(r: boolean) { - this.#ready = r - this.changed("ready") - } - - get db() { return this.#db } - get ready() { return this.#ready } - get available() { - return Utils.exec("which nix", () => true, () => false) - } - - constructor() { - super() - if (!this.available) - return this - - this.#updateList() - nixpkgs.connect("changed", this.#updateList) - } - - query = async (filter: string) => { - if (!dependencies("fzf", "nix") || !this.#ready) - return [] as string[] - - return bash(`cat ${CACHE} | fzf -f ${filter} -e | head -n ${MAX} `) - .then(str => str.split("\n").filter(i => i)) - } - - nix(cmd: string, bin: string, args: string) { - return Utils.execAsync(`nix ${cmd} ${nixpkgs}#${bin} --impure ${args}`) - } - - run = async (input: string) => { - if (!dependencies("nix")) - return - - try { - const [bin, ...args] = input.trim().split(/\s+/) - - this.ready = false - await this.nix("shell", bin, "--command sh -c 'exit'") - this.ready = true - - this.nix("run", bin, ["--", ...args].join(" ")) - } catch (err) { - if (typeof err === "string") - Utils.notify("NixRun Error", err, icons.nix.nix) - else - logError(err) - } finally { - this.ready = true - } - } - - #updateList = async () => { - if (!dependencies("nix")) - return - - this.ready = false - this.#db = {} - - const search = await bash(`nix search ${nixpkgs} --json`) - if (!search) { - this.ready = true - return - } - - const json = Object.entries(JSON.parse(search) as { - [name: string]: Nixpkg - }) - - for (const [pkg, info] of json) { - const name = pkg.replace(PREFIX, "") - this.#db[name] = { ...info, name } - } - - const list = Object.keys(this.#db).join("\n") - await Utils.writeFile(list, CACHE) - this.ready = true - } -} - -export default new Nix diff --git a/roles/ags/files/agsv2/service/powermenu.ts b/roles/ags/files/agsv2/service/powermenu.ts deleted file mode 100644 index de0fc18..0000000 --- a/roles/ags/files/agsv2/service/powermenu.ts +++ /dev/null @@ -1,48 +0,0 @@ -import options from "options" - -const { sleep, reboot, logout, shutdown, lock } = options.powermenu - -export type Action = "sleep" | "reboot" | "logout" | "shutdown" | "lock" - -class PowerMenu extends Service { - static { - Service.register(this, {}, { - "title": ["string"], - "cmd": ["string"], - }) - } - - #title = "" - #cmd = "" - - get title() { return this.#title } - - action(action: Action) { - [this.#cmd, this.#title] = { - sleep: [sleep.value, "Sleep"], - reboot: [reboot.value, "Reboot"], - logout: [logout.value, "Log Out"], - shutdown: [shutdown.value, "Shutdown"], - lock: [lock.value, "Lock"], - }[action] - - this.notify("cmd") - this.notify("title") - this.emit("changed") - App.closeWindow("powermenu") - App.openWindow("verification") - } - - readonly shutdown = () => { - this.action("shutdown") - } - - readonly exec = () => { - App.closeWindow("verification") - Utils.exec(this.#cmd) - } -} - -const powermenu = new PowerMenu -Object.assign(globalThis, { powermenu }) -export default powermenu diff --git a/roles/ags/files/agsv2/service/screenrecord.ts b/roles/ags/files/agsv2/service/screenrecord.ts deleted file mode 100644 index 58721d2..0000000 --- a/roles/ags/files/agsv2/service/screenrecord.ts +++ /dev/null @@ -1,102 +0,0 @@ -import GLib from "gi://GLib" -import icons from "lib/icons" -import { dependencies, sh, bash } from "lib/utils" - -const now = () => GLib.DateTime.new_now_local().format("%Y-%m-%d_%H-%M-%S") - -class Recorder extends Service { - static { - Service.register(this, {}, { - "timer": ["int"], - "recording": ["boolean"], - }) - } - - #recordings = Utils.HOME + "/Videos/Screencasting" - #screenshots = Utils.HOME + "/Pictures/Screenshots" - #file = "" - #interval = 0 - - recording = false - timer = 0 - - async start() { - if (!dependencies("slurp", "wf-recorder")) - return - - if (this.recording) - return - - Utils.ensureDirectory(this.#recordings) - this.#file = `${this.#recordings}/${now()}.mp4` - sh(`wf-recorder -g "${await sh("slurp")}" -f ${this.#file} --pixel-format yuv420p`) - - this.recording = true - this.changed("recording") - - this.timer = 0 - this.#interval = Utils.interval(1000, () => { - this.changed("timer") - this.timer++ - }) - } - - async stop() { - if (!this.recording) - return - - await bash("killall -INT wf-recorder") - this.recording = false - this.changed("recording") - GLib.source_remove(this.#interval) - - Utils.notify({ - iconName: icons.fallback.video, - summary: "Screenrecord", - body: this.#file, - actions: { - "Show in Files": () => sh(`xdg-open ${this.#recordings}`), - "View": () => sh(`xdg-open ${this.#file}`), - }, - }) - } - - async screenshot(full = false) { - if (!dependencies("slurp", "wayshot")) - return - - const file = `${this.#screenshots}/${now()}.png` - Utils.ensureDirectory(this.#screenshots) - - if (full) { - await sh(`wayshot -f ${file}`) - } - else { - const size = await sh("slurp") - if (!size) - return - - await sh(`wayshot -f ${file} -s "${size}"`) - } - - bash(`wl-copy < ${file}`) - - Utils.notify({ - image: file, - summary: "Screenshot", - body: file, - actions: { - "Show in Files": () => sh(`xdg-open ${this.#screenshots}`), - "View": () => sh(`xdg-open ${file}`), - "Edit": () => { - if (dependencies("swappy")) - sh(`swappy -f ${file}`) - }, - }, - }) - } -} - -const recorder = new Recorder -Object.assign(globalThis, { recorder }) -export default recorder diff --git a/roles/ags/files/agsv2/service/sh.ts b/roles/ags/files/agsv2/service/sh.ts deleted file mode 100644 index eb5f20b..0000000 --- a/roles/ags/files/agsv2/service/sh.ts +++ /dev/null @@ -1,48 +0,0 @@ -import GLib from "gi://GLib?version=2.0" -import { bash, dependencies } from "lib/utils" -import icons from "lib/icons" -import options from "options" - -const MAX = options.launcher.sh.max -const BINS = `${Utils.CACHE_DIR}/binaries` - -async function ls(path: string) { - return Utils.execAsync(`ls ${path}`).catch(() => "") -} - -async function reload() { - const bins = await Promise.all(GLib.getenv("PATH")! - .split(":") - .map(ls)) - - Utils.writeFile(bins.join("\n"), BINS) -} - -async function query(filter: string) { - if (!dependencies("fzf")) - return [] as string[] - - return bash(`cat ${BINS} | fzf -f ${filter} | head -n ${MAX}`) - .then(str => Array.from(new Set(str.split("\n").filter(i => i)).values())) - .catch(err => { print(err); return [] }) -} - -function run(args: string) { - Utils.execAsync(args) - .then(out => { - print(`:sh ${args.trim()}:`) - print(out) - }) - .catch(err => { - Utils.notify("ShRun Error", err, icons.app.terminal) - }) -} - -class Sh extends Service { - static { Service.register(this) } - constructor() { super(); reload() } - query = query - run = run -} - -export default new Sh diff --git a/roles/ags/files/agsv2/service/wallpaper.ts b/roles/ags/files/agsv2/service/wallpaper.ts deleted file mode 100644 index 47ff8d4..0000000 --- a/roles/ags/files/agsv2/service/wallpaper.ts +++ /dev/null @@ -1,99 +0,0 @@ -import options from "options" -import { dependencies, sh } from "lib/utils" - -export type Resolution = 1920 | 1366 | 3840 -export type Market = - | "random" - | "en-US" - | "ja-JP" - | "en-AU" - | "en-GB" - | "de-DE" - | "en-NZ" - | "en-CA" - -const WP = `${Utils.HOME}/.config/background` -const Cache = `${Utils.HOME}/Pictures/Wallpapers/Bing` - -class Wallpaper extends Service { - static { - Service.register(this, {}, { - "wallpaper": ["string"], - }) - } - - #blockMonitor = false - - #wallpaper() { - if (!dependencies("swww")) - return - - sh("hyprctl cursorpos").then(pos => { - sh([ - "swww", "img", - "--invert-y", - "--transition-type", "grow", - "--transition-pos", pos.replace(" ", ""), - WP, - ]).then(() => { - this.changed("wallpaper") - }) - }) - } - - async #setWallpaper(path: string) { - this.#blockMonitor = true - - await sh(`cp ${path} ${WP}`) - this.#wallpaper() - - this.#blockMonitor = false - } - - async #fetchBing() { - const res = await Utils.fetch("https://bing.biturl.top/", { - params: { - resolution: options.wallpaper.resolution.value, - format: "json", - image_format: "jpg", - index: "random", - mkt: options.wallpaper.market.value, - }, - }).then(res => res.text()) - - if (!res.startsWith("{")) - return console.warn("bing api", res) - - const { url } = JSON.parse(res) - const file = `${Cache}/${url.replace("https://www.bing.com/th?id=", "")}` - - if (dependencies("curl")) { - Utils.ensureDirectory(Cache) - await sh(`curl "${url}" --output ${file}`) - this.#setWallpaper(file) - } - } - - readonly random = () => { this.#fetchBing() } - readonly set = (path: string) => { this.#setWallpaper(path) } - get wallpaper() { return WP } - - constructor() { - super() - - if (!dependencies("swww")) - return this - - // gtk portal - Utils.monitorFile(WP, () => { - if (!this.#blockMonitor) - this.#wallpaper() - }) - - Utils.execAsync("swww-daemon") - .then(this.#wallpaper) - .catch(() => null) - } -} - -export default new Wallpaper diff --git a/roles/ags/files/agsv2/service/weather.ts b/roles/ags/files/agsv2/service/weather.ts deleted file mode 100644 index 14f2df2..0000000 --- a/roles/ags/files/agsv2/service/weather.ts +++ /dev/null @@ -1,59 +0,0 @@ -import options from "options" - -const { interval, key, cities, unit } = options.datemenu.weather - -class Weather extends Service { - static { - Service.register(this, {}, { - "forecasts": ["jsobject"], - }) - } - - #forecasts: Forecast[] = [] - get forecasts() { return this.#forecasts } - - async #fetch(placeid: number) { - const url = "https://api.openweathermap.org/data/2.5/forecast" - const res = await Utils.fetch(url, { - params: { - id: placeid, - appid: key.value, - untis: unit.value, - }, - }) - return await res.json() - } - - constructor() { - super() - if (!key.value) - return this - - Utils.interval(interval.value, () => { - Promise.all(cities.value.map(this.#fetch)).then(forecasts => { - this.#forecasts = forecasts as Forecast[] - this.changed("forecasts") - }) - }) - } -} - -export default new Weather - -type Forecast = { - city: { - name: string, - } - list: Array<{ - dt: number - main: { - temp: number - feels_like: number - }, - weather: Array<{ - main: string, - description: string, - icon: string, - }> - }> -} diff --git a/roles/ags/files/agsv2/style/extra.scss b/roles/ags/files/agsv2/style/extra.scss deleted file mode 100644 index e7f9d44..0000000 --- a/roles/ags/files/agsv2/style/extra.scss +++ /dev/null @@ -1,67 +0,0 @@ -@import './mixins/button.scss'; - -* { - font-size: $font-size; - font-family: $font-name; -} - -separator { - &.horizontal { - min-height: $border-width; - } - - &.vertical { - min-width: $border-width; - } -} - -window.popup { - >* { - border: none; - box-shadow: none; - } - - menu { - border-radius: $popover-radius; - background-color: $bg; - padding: $popover-padding; - border: $border-width solid $popover-border-color; - - separator { - background-color: $border-color; - } - - menuitem { - @include button; - padding: $spacing * .5; - margin: ($spacing * .5) 0; - - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - } - } -} - -tooltip { - * { - all: unset; - } - - background-color: transparent; - border: none; - - >*>* { - background-color: $bg; - border-radius: $radius; - border: $border-width solid $popover-border-color; - color: $fg; - padding: 8px; - margin: 4px; - box-shadow: 0 0 3px 0 $shadow-color; - } -} diff --git a/roles/ags/files/agsv2/style/mixins/a11y-button.scss b/roles/ags/files/agsv2/style/mixins/a11y-button.scss deleted file mode 100644 index 00b24c6..0000000 --- a/roles/ags/files/agsv2/style/mixins/a11y-button.scss +++ /dev/null @@ -1,48 +0,0 @@ -@import './button'; - -@mixin accs-button($flat: false, $reactive: true) { - @include unset; - color: $fg; - - >* { - border-radius: $radius; - transition: $transition; - - @if $flat { - background-color: transparent; - box-shadow: none; - } - - @else { - background-color: $widget-bg; - box-shadow: inset 0 0 0 $border-width $border-color; - } - } - - - @if $reactive { - - &:focus>*, - &.focused>* { - @include button-focus; - } - - &:hover>* { - @include button-hover; - } - - &:active, - &.active, - &.on, - &:checked { - >* { - @include button-active; - } - - &:hover>* { - box-shadow: inset 0 0 0 $border-width $border-color, - inset 0 0 0 99px $hover-bg; - } - } - } -} diff --git a/roles/ags/files/agsv2/style/mixins/button.scss b/roles/ags/files/agsv2/style/mixins/button.scss deleted file mode 100644 index 79ec275..0000000 --- a/roles/ags/files/agsv2/style/mixins/button.scss +++ /dev/null @@ -1,70 +0,0 @@ -@mixin button-focus() { - box-shadow: inset 0 0 0 $border-width $primary-bg; - background-color: $hover-bg; - color: $hover-fg; -} - -@mixin button-hover() { - box-shadow: inset 0 0 0 $border-width $border-color; - background-color: $hover-bg; - color: $hover-fg; -} - -@mixin button-active() { - box-shadow: inset 0 0 0 $border-width $border-color; - background-image: $active-gradient; - background-color: $primary-bg; - color: $primary-fg; -} - -@mixin button-disabled() { - box-shadow: none; - background-color: transparent; - color: transparentize($fg, 0.7); -} - -@mixin button($flat: false, $reactive: true, $radius: $radius, $focusable: true) { - all: unset; - transition: $transition; - border-radius: $radius; - color: $fg; - - @if $flat { - background-color: transparent; - background-image: none; - box-shadow: none; - } - - @else { - background-color: $widget-bg; - box-shadow: inset 0 0 0 $border-width $border-color; - } - - @if $reactive { - @if $focusable { - &:focus { - @include button-focus; - } - } - - &:hover { - @include button-hover; - } - - &:active, - &.on, - &.active, - &:checked { - @include button-active; - - &:hover { - box-shadow: inset 0 0 0 $border-width $border-color, - inset 0 0 0 99px $hover-bg; - } - } - } - - &:disabled { - @include button-disabled; - } -} diff --git a/roles/ags/files/agsv2/style/mixins/floating-widget.scss b/roles/ags/files/agsv2/style/mixins/floating-widget.scss deleted file mode 100644 index 613668d..0000000 --- a/roles/ags/files/agsv2/style/mixins/floating-widget.scss +++ /dev/null @@ -1,12 +0,0 @@ -@mixin floating-widget { - @if $shadows { - box-shadow: 0 0 5px 0 $shadow-color; - } - - margin: max($spacing, 8px); - border: $border-width solid $popover-border-color; - background-color: $bg; - color: $fg; - border-radius: $popover-radius; - padding: $popover-padding; -} diff --git a/roles/ags/files/agsv2/style/mixins/hidden.scss b/roles/ags/files/agsv2/style/mixins/hidden.scss deleted file mode 100644 index ea6a42c..0000000 --- a/roles/ags/files/agsv2/style/mixins/hidden.scss +++ /dev/null @@ -1,15 +0,0 @@ -@mixin hidden { - background-color: transparent; - background-image: none; - border-color: transparent; - box-shadow: none; - -gtk-icon-transform: scale(0); - - * { - background-color: transparent; - background-image: none; - border-color: transparent; - box-shadow: none; - -gtk-icon-transform: scale(0); - } -} diff --git a/roles/ags/files/agsv2/style/mixins/media.scss b/roles/ags/files/agsv2/style/mixins/media.scss deleted file mode 100644 index 3178029..0000000 --- a/roles/ags/files/agsv2/style/mixins/media.scss +++ /dev/null @@ -1,42 +0,0 @@ -@mixin media() { - @include widget; - padding: $padding; - - .cover { - @if $shadows { - box-shadow: 2px 2px 2px 0 $shadow-color; - } - - background-size: cover; - background-position: center; - border-radius: $radius*0.8; - margin-right: $spacing; - } - - button { - @include button($flat: true); - padding: $padding * .5; - - &.play-pause { - margin: 0 ($spacing * .5); - } - - image { - font-size: 1.2em; - } - } - - .artist { - color: transparentize($fg, .2); - font-size: .9em; - } - - scale { - @include slider($width: .5em, $slider: false, $gradient: linear-gradient($fg, $fg)); - margin-bottom: $padding * .5; - - trough { - border: none; - } - } -} diff --git a/roles/ags/files/agsv2/style/mixins/scrollable.scss b/roles/ags/files/agsv2/style/mixins/scrollable.scss deleted file mode 100644 index b66f246..0000000 --- a/roles/ags/files/agsv2/style/mixins/scrollable.scss +++ /dev/null @@ -1,42 +0,0 @@ -@mixin scrollable($top: false, $bottom: false) { - - @if $top and $shadows { - undershoot.top { - background: linear-gradient(to bottom, $shadow-color, transparent, transparent, transparent, transparent, transparent); - } - } - - @if $bottom and $shadows { - undershoot.bottom { - background: linear-gradient(to top, $shadow-color, transparent, transparent, transparent, transparent, transparent); - } - } - - scrollbar, - scrollbar * { - all: unset; - } - - scrollbar.vertical { - transition: $transition; - background-color: transparentize($bg, 0.7); - - &:hover { - background-color: transparentize($bg, 0.3); - - slider { - background-color: transparentize($fg, 0.3); - min-width: .6em; - } - } - } - - - scrollbar.vertical slider { - background-color: transparentize($fg, 0.5); - border-radius: $radius; - min-width: .4em; - min-height: 2em; - transition: $transition; - } -} diff --git a/roles/ags/files/agsv2/style/mixins/slider.scss b/roles/ags/files/agsv2/style/mixins/slider.scss deleted file mode 100644 index b90e566..0000000 --- a/roles/ags/files/agsv2/style/mixins/slider.scss +++ /dev/null @@ -1,74 +0,0 @@ -@import './unset'; - -@mixin slider($width: 0.7em, $slider-width: .5em, $gradient: $active-gradient, $slider: true, $focusable: true, $radius: $radius) { - @include unset($rec: true); - - trough { - transition: $transition; - border-radius: $radius; - border: $border; - background-color: $widget-bg; - min-height: $width; - min-width: $width; - - highlight, - progress { - border-radius: max($radius - $border-width, 0); - background-image: $gradient; - min-height: $width; - min-width: $width; - } - } - - slider { - box-shadow: none; - background-color: transparent; - border: $border-width solid transparent; - transition: $transition; - border-radius: $radius; - min-height: $width; - min-width: $width; - margin: -$slider-width; - } - - &:hover { - trough { - background-color: $hover-bg; - } - - slider { - @if $slider { - background-color: $fg; - border-color: $border-color; - - @if $shadows { - box-shadow: 0 0 3px 0 $shadow-color; - } - } - } - } - - &:disabled { - - highlight, - progress { - background-color: transparentize($fg, 0.4); - background-image: none; - } - } - - @if $focusable { - trough:focus { - background-color: $hover-bg; - box-shadow: inset 0 0 0 $border-width $primary-bg; - - slider { - @if $slider { - background-color: $fg; - box-shadow: inset 0 0 0 $border-width $primary-bg; - } - } - } - - } -} diff --git a/roles/ags/files/agsv2/style/mixins/spacing.scss b/roles/ags/files/agsv2/style/mixins/spacing.scss deleted file mode 100644 index 4096fba..0000000 --- a/roles/ags/files/agsv2/style/mixins/spacing.scss +++ /dev/null @@ -1,53 +0,0 @@ -@mixin spacing($multiplier: 1, $spacing: $spacing, $rec: false) { - &.horizontal>* { - margin: 0 calc($spacing * $multiplier / 2); - - &:first-child { - margin-left: 0; - } - - &:last-child { - margin-right: 0; - } - } - - &.vertical>* { - margin: calc($spacing * $multiplier / 2) 0; - - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - } - - @if $rec { - box { - &.horizontal>* { - margin: 0 $spacing * $multiplier / 2; - - &:first-child { - margin-left: 0; - } - - &:last-child { - margin-right: 0; - } - } - - &.vertical>* { - margin: $spacing * $multiplier / 2 0; - - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - } - } - } -} diff --git a/roles/ags/files/agsv2/style/mixins/switch.scss b/roles/ags/files/agsv2/style/mixins/switch.scss deleted file mode 100644 index 2abf360..0000000 --- a/roles/ags/files/agsv2/style/mixins/switch.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import './button'; - -@mixin switch { - @include button; - - slider { - background-color: $primary-fg; - border-radius: $radius; - min-width: 24px; - min-height: 24px; - } - - image { - color: transparent; - } -} diff --git a/roles/ags/files/agsv2/style/mixins/unset.scss b/roles/ags/files/agsv2/style/mixins/unset.scss deleted file mode 100644 index eb80af5..0000000 --- a/roles/ags/files/agsv2/style/mixins/unset.scss +++ /dev/null @@ -1,9 +0,0 @@ -@mixin unset($rec: false) { - all: unset; - - @if $rec { - * { - all: unset - } - } -} diff --git a/roles/ags/files/agsv2/style/mixins/widget.scss b/roles/ags/files/agsv2/style/mixins/widget.scss deleted file mode 100644 index 053f1aa..0000000 --- a/roles/ags/files/agsv2/style/mixins/widget.scss +++ /dev/null @@ -1,7 +0,0 @@ -@mixin widget { - transition: $transition; - border-radius: $radius; - color: $fg; - background-color: $widget-bg; - border: $border; -} diff --git a/roles/ags/files/agsv2/style/style.ts b/roles/ags/files/agsv2/style/style.ts deleted file mode 100644 index b8f4a1a..0000000 --- a/roles/ags/files/agsv2/style/style.ts +++ /dev/null @@ -1,112 +0,0 @@ -/* eslint-disable max-len */ -import { type Opt } from "lib/option" -import options from "options" -import { bash, dependencies } from "lib/utils" - -const deps = [ - "font", - "theme", - "bar.corners", - "bar.flatButtons", - "bar.position", - "bar.battery.charging", - "bar.battery.blocks", -] - -const { - dark, - light, - blur, - scheme, - padding, - spacing, - radius, - shadows, - widget, - border, -} = options.theme - -const popoverPaddingMultiplier = 1.6 - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const t = (dark: Opt | string, light: Opt | string) => scheme.value === "dark" - ? `${dark}` : `${light}` - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const $ = (name: string, value: string | Opt) => `$${name}: ${value};` - -const variables = () => [ - $("bg", blur.value ? `transparentize(${t(dark.bg, light.bg)}, ${blur.value / 100})` : t(dark.bg, light.bg)), - $("fg", t(dark.fg, light.fg)), - - $("primary-bg", t(dark.primary.bg, light.primary.bg)), - $("primary-fg", t(dark.primary.fg, light.primary.fg)), - - $("error-bg", t(dark.error.bg, light.error.bg)), - $("error-fg", t(dark.error.fg, light.error.fg)), - - $("scheme", scheme), - $("padding", `${padding}pt`), - $("spacing", `${spacing}pt`), - $("radius", `${radius}px`), - $("transition", `${options.transition}ms`), - - $("shadows", `${shadows}`), - - $("widget-bg", `transparentize(${t(dark.widget, light.widget)}, ${widget.opacity.value / 100})`), - - $("hover-bg", `transparentize(${t(dark.widget, light.widget)}, ${(widget.opacity.value * .9) / 100})`), - $("hover-fg", `lighten(${t(dark.fg, light.fg)}, 8%)`), - - $("border-width", `${border.width}px`), - $("border-color", `transparentize(${t(dark.border, light.border)}, ${border.opacity.value / 100})`), - $("border", "$border-width solid $border-color"), - - $("active-gradient", `linear-gradient(to right, ${t(dark.primary.bg, light.primary.bg)}, darken(${t(dark.primary.bg, light.primary.bg)}, 4%))`), - $("shadow-color", t("rgba(0,0,0,.6)", "rgba(0,0,0,.4)")), - $("text-shadow", t("2pt 2pt 2pt $shadow-color", "none")), - $("box-shadow", t("2pt 2pt 2pt 0 $shadow-color, inset 0 0 0 $border-width $border-color", "none")), - - $("popover-border-color", `transparentize(${t(dark.border, light.border)}, ${Math.max(((border.opacity.value - 1) / 100), 0)})`), - $("popover-padding", `$padding * ${popoverPaddingMultiplier}`), - $("popover-radius", radius.value === 0 ? "0" : "$radius + $popover-padding"), - - $("font-size", `${options.font.size}pt`), - $("font-name", options.font.name), - - // etc - $("charging-bg", options.bar.battery.charging), - $("bar-battery-blocks", options.bar.battery.blocks), - $("bar-position", options.bar.position), - $("hyprland-gaps-multiplier", options.hyprland.gaps), - $("screen-corner-multiplier", `${options.bar.corners.value * 0.01}`), -] - -async function resetCss() { - if (!dependencies("sass", "fd")) - return - - try { - const vars = `${TMP}/variables.scss` - const scss = `${TMP}/main.scss` - const css = `${TMP}/main.css` - - const fd = await bash(`fd ".scss" ${App.configDir}`) - const files = fd.split(/\s+/) - const imports = [vars, ...files].map(f => `@import '${f}';`) - - await Utils.writeFile(variables().join("\n"), vars) - await Utils.writeFile(imports.join("\n"), scss) - await bash`sass ${scss} ${css}` - - App.applyCss(css, true) - } catch (error) { - error instanceof Error - ? logError(error) - : console.error(error) - } -} - -Utils.monitorFile(`${App.configDir}/style`, resetCss) -options.handler(deps, resetCss) -await resetCss() diff --git a/roles/ags/files/agsv2/style/widgets/bar.scss b/roles/ags/files/agsv2/style/widgets/bar.scss deleted file mode 100644 index f3b0019..0000000 --- a/roles/ags/files/agsv2/style/widgets/bar.scss +++ /dev/null @@ -1,265 +0,0 @@ -@use 'sass:color'; - -$bar-spacing: $spacing * .3; -$button-radius: $radius; - -@mixin panel-button($flat: true, $reactive: true) { - @include accs-button($flat, $reactive); - - >* { - border-radius: $button-radius; - margin: $bar-spacing; - } - - label, - image { - font-weight: bold; - } - - >* { - padding: $padding * 0.4 $padding * 0.8; - } -} - -.bar { - transition: $transition; - background-color: $bg; - - .panel-button { - @include panel-button; - - &:not(.flat) { - - @include accs-button($flat: false); - } - } - - .launcher { - .colored { - color: transparentize($primary-bg, 0.2); - } - - &:hover .colored { - color: $primary-bg; - } - - &:active .colored, - &.active .colored { - color: $primary-fg; - } - } - - .workspaces { - label { - font-size: 0; - min-width: 5pt; - min-height: 5pt; - border-radius: $radius*.6; - box-shadow: inset 0 0 0 $border-width $border-color; - margin: 0 $padding * .5; - transition: $transition* .5; - background-color: transparentize($fg, .8); - - &.occupied { - background-color: transparentize($fg, .2); - min-width: 7pt; - min-height: 7pt; - } - - &.active { - // background-color: $primary-bg; - background-image: $active-gradient; - min-width: 20pt; - min-height: 12pt; - } - } - - &.active, - &:active { - label { - background-color: transparentize($primary-fg, .3); - - &.occupied { - background-color: transparentize($primary-fg, .15); - } - - &.active { - background-color: $primary-fg; - } - } - } - } - - .media label { - margin: 0 ($spacing * .5) - } - - .taskbar .indicator.active { - background-color: $primary-bg; - border-radius: $radius; - min-height: 4pt; - min-width: 6pt; - margin: 2pt; - } - - .powermenu.colored, - .recorder { - image { - color: transparentize($error-bg, 0.3); - } - - &:hover image { - color: transparentize($error-bg, 0.15); - } - - &:active image { - color: $primary-fg; - } - } - - .quicksettings>box>box { - @include spacing($spacing: if($bar-spacing==0, $padding / 2, $bar-spacing)); - } - - .quicksettings:not(.active):not(:active) { - .bluetooth { - color: $primary-bg; - - label { - font-size: $font-size * .7; - color: $fg; - text-shadow: $text-shadow; - } - } - } - - .battery-bar { - >* { - padding: 0; - } - - &.bar-hidden>box { - padding: 0 $spacing * .5; - - image { - margin: 0; - } - } - - levelbar * { - all: unset; - transition: $transition; - } - - .whole { - @if $shadows { - image { - -gtk-icon-shadow: $text-shadow; - } - - label { - text-shadow: $text-shadow; - } - } - } - - .regular image { - margin-left: $spacing * .5; - } - - trough { - @include widget; - min-height: 12pt; - min-width: 12pt; - } - - .regular trough { - margin-right: $spacing * .5; - } - - block { - margin: 0; - - &:last-child { - border-radius: 0 $button-radius $button-radius 0; - } - - &:first-child { - border-radius: $button-radius 0 0 $button-radius; - } - } - - .vertical { - block { - &:last-child { - border-radius: 0 0 $button-radius $button-radius; - } - - &:first-child { - border-radius: $button-radius $button-radius 0 0; - } - } - - } - - @for $i from 1 through $bar-battery-blocks { - block:nth-child(#{$i}).filled { - background-color: color.mix($bg, $primary-bg, $i*3) - } - - &.low block:nth-child(#{$i}).filled { - background-color: color.mix($bg, $error-bg, $i*3) - } - - &.charging block:nth-child(#{$i}).filled { - background-color: color.mix($bg, $charging-bg, $i*3) - } - - &:active .regular block:nth-child(#{$i}).filled { - background-color: color.mix($bg, $primary-fg, $i*3) - } - } - - &.low image { - color: $error-bg - } - - &.charging image { - color: $charging-bg - } - - &:active image { - color: $primary-fg - } - } -} - -.bar.transparent { - background-color: transparent; - - .panel-button { - &:hover>* { - box-shadow: 1px 1px 3px 0 $shadow-color, inset 0 0 0 $border-width $border-color; - background-color: $bg; - } - - &:not(:hover):not(.active) { - - label, - image { - text-shadow: $text-shadow; - -gtk-icon-shadow: $text-shadow; - } - } - } - - .workspaces label { - box-shadow: inset 0 0 0 $border-width $border-color, - 1px 1px 3px 0 $shadow-color; - } - - .battery-bar trough { - box-shadow: 1px 1px 3px 0 $shadow-color; - - } -} diff --git a/roles/ags/files/agsv2/style/widgets/datemenu.scss b/roles/ags/files/agsv2/style/widgets/datemenu.scss deleted file mode 100644 index 0bbc5d2..0000000 --- a/roles/ags/files/agsv2/style/widgets/datemenu.scss +++ /dev/null @@ -1,110 +0,0 @@ -@import "./notifications.scss"; - -@mixin calendar { - @include widget; - padding: $padding*2 $padding*2 0; - - calendar { - all: unset; - - &.button { - @include button($flat: true); - } - - &:selected { - box-shadow: inset 0 -8px 0 0 transparentize($primary-bg, 0.5), - inset 0 0 0 1px $primary-bg; - border-radius: $radius*0.6; - } - - &.header { - background-color: transparent; - border: none; - color: transparentize($fg, 0.5); - } - - &.highlight { - background-color: transparent; - color: transparentize($primary-bg, 0.5); - } - - &:indeterminate { - color: transparentize($fg, 0.9); - } - - font-size: 1.1em; - padding: .2em; - } -} - -window#datemenu .datemenu { - @include floating-widget; - - .notifications { - .header { - margin-bottom: $spacing; - margin-right: $spacing; - - >label { - margin-left: $radius * .5; - } - - button { - @include button; - padding: $padding*.7 $padding; - } - } - - .notification-scrollable { - @include scrollable($top: true, $bottom: true); - } - - .notification-list { - margin-right: $spacing; - } - - .notification { - @include notification; - @include widget; - padding: $padding; - margin-bottom: $spacing; - } - - .placeholder { - image { - font-size: 7em; - } - - label { - font-size: 1.2em; - } - } - } - - - separator { - background-color: $popover-border-color; - border-radius: $radius; - margin-right: $spacing; - } - - .datemenu { - @include spacing; - } - - .clock-box { - padding: $padding; - - .clock { - font-size: 5em; - } - - .uptime { - color: transparentize($fg, 0.2); - } - } - - .calendar { - @include calendar; - } -} diff --git a/roles/ags/files/agsv2/style/widgets/greeter.scss b/roles/ags/files/agsv2/style/widgets/greeter.scss deleted file mode 100644 index 0767906..0000000 --- a/roles/ags/files/agsv2/style/widgets/greeter.scss +++ /dev/null @@ -1,64 +0,0 @@ -@import "../mixins/floating-widget.scss"; -@import "../mixins/widget.scss"; -@import "../mixins/spacing.scss"; -@import "../mixins/unset.scss"; -@import "../mixins/a11y-button.scss"; -@import "./bar.scss"; - -window#greeter { - background-color: lighten($bg, 6%); - color: $fg; - - .bar { - background-color: transparent; - - .date { - @include unset($rec: true); - @include panel-button($flat: true, $reactive: false); - } - } - - .auth { - @include floating_widget; - border-radius: $radius; - min-width: 400px; - padding: 0; - - .wallpaper { - min-height: 220px; - background-size: cover; - border-top-left-radius: $radius; - border-top-right-radius: $radius; - } - - .wallpaper-contrast { - min-height: 100px; - } - - .avatar { - border-radius: 99px; - min-width: 140px; - min-height: 140px; - background-size: cover; - box-shadow: 3px 3px 6px 0 $shadow-color; - margin-bottom: $spacing; - } - - - .password { - entry { - @include button; - padding: $padding*.7 $padding; - margin-left: $spacing*.5; - } - - margin: 0 $padding*4; - margin-top: $spacing; - } - - .response-box { - color: $error-bg; - margin: $spacing 0; - } - } -} diff --git a/roles/ags/files/agsv2/style/widgets/keybinds.scss b/roles/ags/files/agsv2/style/widgets/keybinds.scss deleted file mode 100644 index 828299f..0000000 --- a/roles/ags/files/agsv2/style/widgets/keybinds.scss +++ /dev/null @@ -1,44 +0,0 @@ -@use "sass:math"; -@use "sass:color"; - -window#keybinds { - .keybinds { - @include floating_widget; - color: $fg; - background-color: $bg; - min-width: 85rem; - min-height: 60rem; - } - - .keybinds-list { - margin: $spacing; - padding: $padding; - } - - .column-list { - margin-right: $spacing * 0.1; - padding-right: $padding * 0.1; - - .catagory-group { - background-color: $widget-bg; - margin: $spacing; - padding: $padding; - border-radius: $radius; - border: $border; - } - - .catagory-row { - padding-bottom: $padding * 0.5; - } - - .catagory-label { - padding-top: $padding * 0.5; - padding-bottom: $padding * 0.5; - } - } -} - -.header { - margin-top: $spacing; - padding-top: $padding; -} \ No newline at end of file diff --git a/roles/ags/files/agsv2/style/widgets/launcher.scss b/roles/ags/files/agsv2/style/widgets/launcher.scss deleted file mode 100644 index 926abc3..0000000 --- a/roles/ags/files/agsv2/style/widgets/launcher.scss +++ /dev/null @@ -1,143 +0,0 @@ -@use "sass:math"; -@use "sass:color"; - -window#launcher .launcher { - @include floating_widget; - - .quicklaunch { - @include spacing; - - button { - @include button($flat: true); - padding: $padding; - } - } - - entry { - @include button; - padding: $padding; - margin: $spacing; - - selection { - color: color.mix($fg, $bg, 50%); - background-color: transparent; - } - - label, - image { - color: $fg; - } - } - - image.spinner { - color: $primary-bg; - margin-right: $spacing; - } - - separator { - margin: 4pt 0; - background-color: $popover-border-color; - } - - button.app-item { - @include button($flat: true, $reactive: false); - - >box { - @include spacing(0.5); - } - - transition: $transition; - padding: $padding; - - label { - transition: $transition; - - &.title { - color: $fg; - } - - &.description { - color: transparentize($fg, 0.3); - } - } - - image { - transition: $transition; - } - - &:hover, - &:focus { - .title { - color: $primary-bg; - } - - .description { - color: transparentize($primary-bg, .4); - } - - image { - -gtk-icon-shadow: 2px 2px $primary-bg; - } - } - - &:active { - background-color: transparentize($primary-bg, 0.5); - border-radius: $radius; - box-shadow: inset 0 0 0 $border-width $border-color; - - .title { - color: $fg; - } - } - } - - button.help, - button.nix-item { - @include button($flat: true, $reactive: false); - padding: 0 ($padding * .5); - - label { - transition: $transition; - color: $fg; - } - - .name { - font-size: 1.2em; - font-weight: bold; - } - - .description { - color: transparentize($fg, .3) - } - - &:hover, - &:focus { - label { - text-shadow: $text-shadow; - } - - .name, - .version { - color: $primary-bg; - } - - .description { - color: transparentize($primary-bg, .3) - } - } - } - - button.sh-item { - @include button($flat: true, $reactive: false); - padding: 0 ($padding * .5); - - transition: $transition; - color: $fg; - - &:hover, - &:focus { - color: $primary-bg; - text-shadow: $text-shadow; - } - } -} diff --git a/roles/ags/files/agsv2/style/widgets/notifications.scss b/roles/ags/files/agsv2/style/widgets/notifications.scss deleted file mode 100644 index a79d9f2..0000000 --- a/roles/ags/files/agsv2/style/widgets/notifications.scss +++ /dev/null @@ -1,79 +0,0 @@ -@mixin notification() { - &.critical { - box-shadow: inset 0 0 .5em 0 $error-bg; - } - - &:hover button.close-button { - @include button-hover; - background-color: transparentize($error-bg, .5); - } - - .content { - .title { - margin-right: $spacing; - color: $fg; - font-size: 1.1em; - } - - .time { - color: transparentize($fg, .2); - } - - .description { - font-size: .9em; - color: transparentize($fg, .2); - } - - .icon { - border-radius: $radius*0.8; - margin-right: $spacing; - - &.img { - border: $border; - } - } - } - - box.actions { - @include spacing(0.5); - margin-top: $spacing; - - button { - @include button; - border-radius: $radius*0.8; - font-size: 1.2em; - padding: $padding * 0.7; - } - } - - button.close-button { - @include button($flat: true); - margin-left: $spacing / 2; - border-radius: $radius*0.8; - min-width: 1.2em; - min-height: 1.2em; - - &:hover { - background-color: transparentize($error-bg, .2); - } - - &:active { - background-image: none; - background-color: $error-bg; - } - } -} - -window.notifications { - @include unset; - - .notification { - @include notification; - @include floating-widget; - border-radius: $radius; - - .description { - min-width: 350px; - } - } -} diff --git a/roles/ags/files/agsv2/style/widgets/osd.scss b/roles/ags/files/agsv2/style/widgets/osd.scss deleted file mode 100644 index 111a486..0000000 --- a/roles/ags/files/agsv2/style/widgets/osd.scss +++ /dev/null @@ -1,26 +0,0 @@ -window.indicator { - .progress { - @include floating-widget; - padding: $padding * .5; - border-radius: if($radius >0, calc($radius + $padding*.5), 0); - @debug $radius; - - .fill { - border-radius: $radius; - background-color: $primary-bg; - color: $primary-fg; - - image { - -gtk-icon-transform: scale(0.7); - } - } - } - - .microphone { - @include floating-widget; - margin: $spacing * 2; - padding: $popover-padding * 2; - font-size: 58px; - color: transparentize($fg, .1) - } -} diff --git a/roles/ags/files/agsv2/style/widgets/overview.scss b/roles/ags/files/agsv2/style/widgets/overview.scss deleted file mode 100644 index c0c4b13..0000000 --- a/roles/ags/files/agsv2/style/widgets/overview.scss +++ /dev/null @@ -1,34 +0,0 @@ -window#overview .overview { - @include floating-widget; - @include spacing; - - .workspace { - &.active>widget { - border-color: $primary-bg - } - - >widget { - @include widget; - border-radius: if($radius ==0, 0, $radius + $padding); - - &:hover { - background-color: $hover-bg; - } - - &:drop(active) { - border-color: $primary-bg; - } - } - } - - .client { - @include button; - border-radius: $radius; - margin: $padding; - - &.hidden { - @include hidden; - transition: 0; - } - } -} diff --git a/roles/ags/files/agsv2/style/widgets/powermenu.scss b/roles/ags/files/agsv2/style/widgets/powermenu.scss deleted file mode 100644 index d5ce0de..0000000 --- a/roles/ags/files/agsv2/style/widgets/powermenu.scss +++ /dev/null @@ -1,110 +0,0 @@ -window#powermenu, -window#verification { - // the fraction has to be more than hyprland ignorealpha - background-color: rgba(0, 0, 0, .4); -} - -window#verification .verification { - @include floating-widget; - padding: $popover-padding * 1.5; - min-width: 300px; - min-height: 100px; - - .text-box { - margin-bottom: $spacing; - - .title { - font-size: 1.6em; - } - - .desc { - color: transparentize($fg, 0.1); - font-size: 1.1em; - } - } - - .buttons { - @include spacing; - margin-top: $padding; - - button { - @include button; - font-size: 1.5em; - padding: $padding; - } - } -} - -window#powermenu .powermenu { - @include floating-widget; - - &.line { - padding: $popover-padding * 1.5; - - button { - padding: $popover-padding; - } - - label { - margin-bottom: $spacing * -.5; - } - } - - &.box { - padding: $popover-padding * 2; - - button { - padding: $popover-padding * 1.5; - } - - label { - margin-bottom: $spacing * -1; - } - } - - button { - @include unset; - - image { - @include button; - border-radius: $radius + ($popover-padding * 1.4); - min-width: 1.7em; - min-height: 1.7em; - font-size: 4em; - } - - label, - image { - color: transparentize($fg, 0.1); - } - - label { - margin-top: $spacing * .3; - } - - &:hover { - image { - @include button-hover; - } - - label { - color: $fg; - } - } - - &:focus image { - @include button-focus; - } - - &:active image { - @include button-active; - } - - &:focus, - &:active { - label { - color: $primary-bg; - } - } - } -} diff --git a/roles/ags/files/agsv2/style/widgets/quicksettings.scss b/roles/ags/files/agsv2/style/widgets/quicksettings.scss deleted file mode 100644 index c7fc8b6..0000000 --- a/roles/ags/files/agsv2/style/widgets/quicksettings.scss +++ /dev/null @@ -1,186 +0,0 @@ -window#quicksettings .quicksettings { - @include floating-widget; - @include spacing; - - padding: $popover-padding * 1.4; - - .avatar { - @include widget; - border-radius: $radius * 3; - } - - .header { - @include spacing(1); - color: transparentize($fg, .15); - - button { - @include button; - padding: $padding; - - image { - font-size: 2.4em; - } - } - } - - .right-buttons { - - button { - @include button; - margin: $spacing * 0.5; - padding: $padding; - } - } - - .sliders-box { - @include widget; - padding: $padding; - - button { - @include button($flat: true); - padding: $padding * .5; - } - - .volume button.arrow:last-child { - margin-left: $spacing * .4; - } - - .volume, - .brightness { - padding: $padding * .5; - } - - scale { - @include slider; - margin: 0 ($spacing * .5); - - &.muted highlight { - background-image: none; - background-color: transparentize($fg, $amount: .2); - } - } - } - - .row { - @include spacing; - } - - .menu { - @include unset; - @include widget; - padding: $padding; - margin-top: $spacing; - - .icon { - margin: 0 ($spacing * .5); - margin-left: $spacing * .2; - } - - .title { - font-weight: bold; - } - - separator { - margin: ($radius * .5); - background-color: $border-color; - } - - button { - @include button($flat: true); - padding: ($padding * .5); - - image:first-child { - margin-right: $spacing * .5; - } - } - - .bluetooth-devices { - @include spacing(.5); - } - - switch { - @include switch; - } - } - - .sliders-box .menu { - margin: ($spacing * .5) 0; - - &.app-mixer { - .mixer-item { - padding: $padding * .5; - padding-left: 0; - padding-right: $padding * 2; - - scale { - @include slider($width: .5em); - } - - image { - font-size: 1.2em; - margin: 0 $padding; - } - } - } - } - - .toggle-button { - @include button; - font-weight: bold; - - image { - font-size: 1.3em; - } - - label { - margin-left: $spacing * .3; - } - - button { - @include button($flat: true); - - &:first-child { - padding: $padding * 1.2; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - &:last-child { - padding: $padding * .5; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - } - - &.active { - background-color: $primary-bg; - - label, - image { - color: $primary-fg; - } - } - } - - .simple-toggle { - @include button; - font-weight: bold; - padding: $padding * 1.2; - - label { - margin-left: $spacing * .3; - } - - image { - font-size: 1.3em; - } - } - - .media { - @include spacing; - - .player { - @include media; - } - } -} \ No newline at end of file diff --git a/roles/ags/files/agsv2/style/widgets/screencorner.scss b/roles/ags/files/agsv2/style/widgets/screencorner.scss deleted file mode 100644 index 9061ac2..0000000 --- a/roles/ags/files/agsv2/style/widgets/screencorner.scss +++ /dev/null @@ -1,52 +0,0 @@ -$_shadow-size: $padding; -$_radius: $radius * $hyprland-gaps-multiplier * $screen-corner-multiplier; -$_margin: 99px; - -window.screen-corner:not(.hidden) { - transition: $transition; - - box.shadow { - margin-right: $_margin * -1; - margin-left: $_margin * -1; - - @if $shadows { - box-shadow: inset 0 0 $_shadow-size 0 $shadow-color; - } - - @if $bar-position =="top" { - margin-bottom: $_margin * -1; - } - - @if $bar-position =="bottom" { - margin-top: $_margin * -1; - } - } - - box.border { - @if $bar-position =="top" { - border-top: $border-width solid $bg; - } - - @if $bar-position =="bottom" { - border-bottom: $border-width solid $bg; - } - - margin-right: $_margin; - margin-left: $_margin; - } - - box.corner { - box-shadow: 0 0 0 $border-width $border-color; - } - - &.corners { - box.border { - border-radius: if($radius>0, $_radius, 0); - box-shadow: 0 0 0 $_radius $bg; - } - - box.corner { - border-radius: if($radius>0, $_radius, 0); - } - } -} diff --git a/roles/ags/files/agsv2/style/widgets/settingsdialog.scss b/roles/ags/files/agsv2/style/widgets/settingsdialog.scss deleted file mode 100644 index 67fbec2..0000000 --- a/roles/ags/files/agsv2/style/widgets/settingsdialog.scss +++ /dev/null @@ -1,176 +0,0 @@ -window.settings-dialog { - background-color: $bg; - color: $fg; - - .header { - background-color: $widget-bg; - margin-top: $spacing * 0; - - .pager { - @include spacing(.5); - } - - padding: $padding; - - button { - @include button; - font-weight: normal; - padding: $padding * .5 $padding; - - box { - @include spacing($spacing: 3em); - } - } - - button.close { - padding: $padding * .5; - } - - button.reset { - @include button($flat: true); - padding: $padding * .5; - } - } - - .sidebar { - background-color: $widget-bg; - color: $fg; - padding: $padding; - min-width: 10rem; - border-right: 2px solid $widget-bg; - - button { - @include button; - padding: $padding; - font-weight: normal; - color: $fg; - background-color: $widget-bg; - border: none; - margin: $spacing*.5; - - &:hover { - background-color: lighten($widget-bg, 10%); - } - } - - button.active { - background-color: $primary-bg; - color: $fg; - } - } - - - .page { - @include scrollable($top: true); - - .page-content { - padding: $padding * 2; - padding-top: 0; - margin: $spacing; - } - } - - .group { - .group-title { - color: $primary-bg; - margin-bottom: $spacing * .5; - } - - .group-reset { - @include button($flat: true); - margin: $spacing * .5; - padding: $padding * .5; - - &:disabled { - color: transparent; - } - } - - &:not(:first-child) { - margin-top: $spacing; - } - } - - .row { - background-color: $widget-bg; - padding: $padding * 2; - border: $border; - border-top: none; - - &:first-child { - border-radius: $radius $radius 0 0; - border: $border; - } - - &:last-child { - border-radius: 0 0 $radius $radius; - } - - &:first-child:last-child { - border-radius: $radius; - border: $border; - } - - button.reset { - margin-left: $spacing; - } - - label.id, - label.note { - color: transparentize($fg, .4); - } - - entry, - button { - @include button; - padding: $padding; - } - - switch { - @include switch; - } - - spinbutton { - @include unset; - - entry { - border-radius: $radius 0 0 $radius; - } - - button { - border-radius: 0; - } - - button:last-child { - border-radius: 0 $radius $radius 0; - } - } - - .enum-setter { - label { - background-color: $widget-bg; - border: $border; - padding: 0 $padding; - border-radius: $radius 0 0 $radius; - } - - button { - border-radius: 0; - } - - button:last-child { - border-radius: 0 $radius $radius 0; - } - } - - &.wallpaper { - button { - margin-top: $spacing * .5; - } - - .preview { - border-radius: $radius; - } - } - } -} diff --git a/roles/ags/files/agsv2/tsconfig.json b/roles/ags/files/agsv2/tsconfig.json deleted file mode 100644 index 1708aa3..0000000 --- a/roles/ags/files/agsv2/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ES2022", - "lib": [ - "ES2022" - ], - "allowJs": true, - "checkJs": true, - "strict": true, - "noImplicitAny": false, - "baseUrl": ".", - "typeRoots": [ - "./types", - "./node_modules/@girs" - ], - "skipLibCheck": true - } -} diff --git a/roles/ags/files/agsv2/widget/PopupWindow.ts b/roles/ags/files/agsv2/widget/PopupWindow.ts deleted file mode 100644 index b53b6fd..0000000 --- a/roles/ags/files/agsv2/widget/PopupWindow.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { type WindowProps } from "types/widgets/window" -import { type RevealerProps } from "types/widgets/revealer" -import { type EventBoxProps } from "types/widgets/eventbox" -import type Gtk from "gi://Gtk?version=3.0" -import options from "options" - -type Transition = RevealerProps["transition"] -type Child = WindowProps["child"] - -type PopupWindowProps = Omit & { - name: string - layout?: keyof ReturnType - transition?: Transition, -} - -export const Padding = (name: string, { - css = "", - hexpand = true, - vexpand = true, -}: EventBoxProps = {}) => Widget.EventBox({ - hexpand, - vexpand, - can_focus: false, - child: Widget.Box({ css }), - setup: w => w.on("button-press-event", () => App.toggleWindow(name)), -}) - -const PopupRevealer = ( - name: string, - child: Child, - transition: Transition = "slide_down", -) => Widget.Box( - { css: "padding: 1px;" }, - Widget.Revealer({ - transition, - child: Widget.Box({ - class_name: "window-content", - child, - }), - transitionDuration: options.transition.bind(), - setup: self => self.hook(App, (_, wname, visible) => { - if (wname === name) - self.reveal_child = visible - }), - }), -) - -const Layout = (name: string, child: Child, transition?: Transition) => ({ - "center": () => Widget.CenterBox({}, - Padding(name), - Widget.CenterBox( - { vertical: true }, - Padding(name), - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - "top": () => Widget.CenterBox({}, - Padding(name), - Widget.Box( - { vertical: true }, - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - "top-right": () => Widget.Box({}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - PopupRevealer(name, child, transition), - Padding(name), - ), - ), - "top-center": () => Widget.Box({}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - "top-left": () => Widget.Box({}, - Widget.Box( - { - hexpand: false, - vertical: true, - }, - PopupRevealer(name, child, transition), - Padding(name), - ), - Padding(name), - ), - "bottom-left": () => Widget.Box({}, - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name), - PopupRevealer(name, child, transition), - ), - Padding(name), - ), - "bottom-center": () => Widget.Box({}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name), - PopupRevealer(name, child, transition), - ), - Padding(name), - ), - "bottom-right": () => Widget.Box({}, - Padding(name), - Widget.Box( - { - hexpand: false, - vertical: true, - }, - Padding(name), - PopupRevealer(name, child, transition), - ), - ), -}) - -export default ({ - name, - child, - layout = "center", - transition, - exclusivity = "ignore", - ...props -}: PopupWindowProps) => Widget.Window({ - name, - class_names: [name, "popup-window"], - setup: w => w.keybind("Escape", () => App.closeWindow(name)), - visible: false, - keymode: "on-demand", - exclusivity, - layer: "top", - anchor: ["top", "bottom", "right", "left"], - child: Layout(name, child, transition)[layout](), - ...props, -}) diff --git a/roles/ags/files/agsv2/widget/RegularWindow.ts b/roles/ags/files/agsv2/widget/RegularWindow.ts deleted file mode 100644 index 1e4225d..0000000 --- a/roles/ags/files/agsv2/widget/RegularWindow.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Gtk from "gi://Gtk?version=3.0" - -export default Widget.subclass(Gtk.Window) diff --git a/roles/ags/files/agsv2/widget/bar/Bar.ts b/roles/ags/files/agsv2/widget/bar/Bar.ts deleted file mode 100644 index 9970757..0000000 --- a/roles/ags/files/agsv2/widget/bar/Bar.ts +++ /dev/null @@ -1,60 +0,0 @@ -import BatteryBar from "./buttons/BatteryBar" -import ColorPicker from "./buttons/ColorPicker" -import Date from "./buttons/Date" -import Launcher from "./buttons/Launcher" -import Media from "./buttons/Media" -import PowerMenu from "./buttons/PowerMenu" -import SysTray from "./buttons/SysTray" -import SystemIndicators from "./buttons/SystemIndicators" -import Taskbar from "./buttons/Taskbar" -import Workspaces from "./buttons/Workspaces" -import ScreenRecord from "./buttons/ScreenRecord" -import Messages from "./buttons/Messages" -import options from "options" - -const { start, center, end } = options.bar.layout -const { transparent, position } = options.bar - -export type BarWidget = keyof typeof widget - -const widget = { - battery: BatteryBar, - colorpicker: ColorPicker, - date: Date, - launcher: Launcher, - media: Media, - powermenu: PowerMenu, - systray: SysTray, - system: SystemIndicators, - taskbar: Taskbar, - workspaces: Workspaces, - screenrecord: ScreenRecord, - messages: Messages, - expander: () => Widget.Box({ expand: true }), -} - -export default (monitor: number) => Widget.Window({ - monitor, - class_name: "bar", - name: `bar${monitor}`, - exclusivity: "exclusive", - anchor: position.bind().as(pos => [pos, "right", "left"]), - child: Widget.CenterBox({ - css: "min-width: 2px; min-height: 2px;", - startWidget: Widget.Box({ - hexpand: true, - children: start.bind().as(s => s.map(w => widget[w]())), - }), - centerWidget: Widget.Box({ - hpack: "center", - children: center.bind().as(c => c.map(w => widget[w]())), - }), - endWidget: Widget.Box({ - hexpand: true, - children: end.bind().as(e => e.map(w => widget[w]())), - }), - }), - setup: self => self.hook(transparent, () => { - self.toggleClassName("transparent", transparent.value) - }), -}) diff --git a/roles/ags/files/agsv2/widget/bar/PanelButton.ts b/roles/ags/files/agsv2/widget/bar/PanelButton.ts deleted file mode 100644 index 1e5fafc..0000000 --- a/roles/ags/files/agsv2/widget/bar/PanelButton.ts +++ /dev/null @@ -1,46 +0,0 @@ -import options from "options" -import { ButtonProps } from "types/widgets/button" - -type PanelButtonProps = ButtonProps & { - window?: string, - flat?: boolean -} - -export default ({ - window = "", - flat, - child, - setup, - ...rest -}: PanelButtonProps) => Widget.Button({ - child: Widget.Box({ child }), - setup: self => { - let open = false - - self.toggleClassName("panel-button") - self.toggleClassName(window) - - self.hook(options.bar.flatButtons, () => { - self.toggleClassName("flat", flat ?? options.bar.flatButtons.value) - }) - - self.hook(App, (_, win, visible) => { - if (win !== window) - return - - if (open && !visible) { - open = false - self.toggleClassName("active", false) - } - - if (visible) { - open = true - self.toggleClassName("active") - } - }) - - if (setup) - setup(self) - }, - ...rest, -}) diff --git a/roles/ags/files/agsv2/widget/bar/ScreenCorners.ts b/roles/ags/files/agsv2/widget/bar/ScreenCorners.ts deleted file mode 100644 index b9ef611..0000000 --- a/roles/ags/files/agsv2/widget/bar/ScreenCorners.ts +++ /dev/null @@ -1,29 +0,0 @@ -import options from "options" - -const { corners, transparent } = options.bar - -export default (monitor: number) => Widget.Window({ - monitor, - name: `corner${monitor}`, - class_name: "screen-corner", - anchor: ["top", "bottom", "right", "left"], - click_through: true, - child: Widget.Box({ - class_name: "shadow", - child: Widget.Box({ - class_name: "border", - expand: true, - child: Widget.Box({ - class_name: "corner", - expand: true, - }), - }), - }), - setup: self => self - .hook(corners, () => { - self.toggleClassName("corners", corners.value > 0) - }) - .hook(transparent, () => { - self.toggleClassName("hidden", transparent.value) - }), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/BatteryBar.ts b/roles/ags/files/agsv2/widget/bar/buttons/BatteryBar.ts deleted file mode 100644 index 18de329..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/BatteryBar.ts +++ /dev/null @@ -1,94 +0,0 @@ -import icons from "lib/icons" -import options from "options" -import PanelButton from "../PanelButton" - -const battery = await Service.import("battery") -const { bar, percentage, blocks, width, low } = options.bar.battery - -const Indicator = () => Widget.Icon({ - setup: self => self.hook(battery, () => { - self.icon = battery.charging || battery.charged - ? icons.battery.charging - : battery.icon_name - }), -}) - -const PercentLabel = () => Widget.Revealer({ - transition: "slide_right", - click_through: true, - reveal_child: percentage.bind(), - child: Widget.Label({ - label: battery.bind("percent").as(p => `${p}%`), - }), -}) - -const LevelBar = () => { - const level = Widget.LevelBar({ - bar_mode: "discrete", - max_value: blocks.bind(), - visible: bar.bind().as(b => b !== "hidden"), - value: battery.bind("percent").as(p => (p / 100) * blocks.value), - }) - const update = () => { - level.value = (battery.percent / 100) * blocks.value - level.css = `block { min-width: ${width.value / blocks.value}pt; }` - } - return level - .hook(width, update) - .hook(blocks, update) - .hook(bar, () => { - level.vpack = bar.value === "whole" ? "fill" : "center" - level.hpack = bar.value === "whole" ? "fill" : "center" - }) -} - -const WholeButton = () => Widget.Overlay({ - vexpand: true, - child: LevelBar(), - class_name: "whole", - pass_through: true, - overlay: Widget.Box({ - hpack: "center", - children: [ - Widget.Icon({ - icon: icons.battery.charging, - visible: Utils.merge([ - battery.bind("charging"), - battery.bind("charged"), - ], (ing, ed) => ing || ed), - }), - Widget.Box({ - hpack: "center", - vpack: "center", - child: PercentLabel(), - }), - ], - }), -}) - -const Regular = () => Widget.Box({ - class_name: "regular", - children: [ - Indicator(), - PercentLabel(), - LevelBar(), - ], -}) - -export default () => PanelButton({ - class_name: "battery-bar", - hexpand: false, - on_clicked: () => { percentage.value = !percentage.value }, - visible: battery.bind("available"), - child: Widget.Box({ - expand: true, - visible: battery.bind("available"), - child: bar.bind().as(b => b === "whole" ? WholeButton() : Regular()), - }), - setup: self => self - .hook(bar, w => w.toggleClassName("bar-hidden", bar.value === "hidden")) - .hook(battery, w => { - w.toggleClassName("charging", battery.charging || battery.charged) - w.toggleClassName("low", battery.percent < low.value) - }), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/ColorPicker.ts b/roles/ags/files/agsv2/widget/bar/buttons/ColorPicker.ts deleted file mode 100644 index 5b1f3f6..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/ColorPicker.ts +++ /dev/null @@ -1,37 +0,0 @@ -import PanelButton from "../PanelButton" -import colorpicker from "service/colorpicker" -import Gdk from "gi://Gdk" - -const css = (color: string) => ` -* { - background-color: ${color}; - color: transparent; -} -*:hover { - color: white; - text-shadow: 2px 2px 3px rgba(0,0,0,.8); -}` - -export default () => { - const menu = Widget.Menu({ - class_name: "colorpicker", - children: colorpicker.bind("colors").as(c => c.map(color => Widget.MenuItem({ - child: Widget.Label(color), - css: css(color), - on_activate: () => colorpicker.wlCopy(color), - }))), - }) - - return PanelButton({ - class_name: "color-picker", - child: Widget.Icon("color-select-symbolic"), - tooltip_text: colorpicker.bind("colors").as(v => `${v.length} colors`), - on_clicked: colorpicker.pick, - on_secondary_click: self => { - if (colorpicker.colors.length === 0) - return - - menu.popup_at_widget(self, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null) - }, - }) -} diff --git a/roles/ags/files/agsv2/widget/bar/buttons/Date.ts b/roles/ags/files/agsv2/widget/bar/buttons/Date.ts deleted file mode 100644 index 4c71afb..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/Date.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { clock } from "lib/variables" -import PanelButton from "../PanelButton" -import options from "options" - -const { format, action } = options.bar.date -const time = Utils.derive([clock, format], (c, f) => c.format(f) || "") - -export default () => PanelButton({ - window: "datemenu", - on_clicked: action.bind(), - child: Widget.Label({ - justification: "center", - label: time.bind(), - }), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/Launcher.ts b/roles/ags/files/agsv2/widget/bar/buttons/Launcher.ts deleted file mode 100644 index f3fee6b..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/Launcher.ts +++ /dev/null @@ -1,49 +0,0 @@ -import PanelButton from "../PanelButton" -import options from "options" -import nix from "service/nix" - -const { icon, label, action } = options.bar.launcher - -function Spinner() { - const child = Widget.Icon({ - icon: icon.icon.bind(), - class_name: Utils.merge([ - icon.colored.bind(), - nix.bind("ready"), - ], (c, r) => `${c ? "colored" : ""} ${r ? "" : "spinning"}`), - css: ` - @keyframes spin { - to { -gtk-icon-transform: rotate(1turn); } - } - - image.spinning { - animation-name: spin; - animation-duration: 1s; - animation-timing-function: linear; - animation-iteration-count: infinite; - } - `, - }) - - return Widget.Revealer({ - transition: "slide_left", - child, - reveal_child: Utils.merge([ - icon.icon.bind(), - nix.bind("ready"), - ], (i, r) => Boolean(i || r)), - }) -} - -export default () => PanelButton({ - window: "launcher", - on_clicked: action.bind(), - child: Widget.Box([ - Spinner(), - Widget.Label({ - class_name: label.colored.bind().as(c => c ? "colored" : ""), - visible: label.label.bind().as(v => !!v), - label: label.label.bind(), - }), - ]), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/Media.ts b/roles/ags/files/agsv2/widget/bar/buttons/Media.ts deleted file mode 100644 index b3aab61..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/Media.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { type MprisPlayer } from "types/service/mpris" -import PanelButton from "../PanelButton" -import options from "options" -import icons from "lib/icons" -import { icon } from "lib/utils" - -const mpris = await Service.import("mpris") -const { length, direction, preferred, monochrome, format } = options.bar.media - -const getPlayer = (name = preferred.value) => - mpris.getPlayer(name) || mpris.players[0] || null - -const Content = (player: MprisPlayer) => { - const revealer = Widget.Revealer({ - click_through: true, - visible: length.bind().as(l => l > 0), - transition: direction.bind().as(d => `slide_${d}` as const), - setup: self => { - let current = "" - self.hook(player, () => { - if (current === player.track_title) - return - - current = player.track_title - self.reveal_child = true - Utils.timeout(3000, () => { - !self.is_destroyed && (self.reveal_child = false) - }) - }) - }, - child: Widget.Label({ - truncate: "end", - max_width_chars: length.bind().as(n => n > 0 ? n : -1), - label: Utils.merge([ - player.bind("track_title"), - player.bind("track_artists"), - format.bind(), - ], () => `${format}` - .replace("{title}", player.track_title) - .replace("{artists}", player.track_artists.join(", ")) - .replace("{artist}", player.track_artists[0] || "") - .replace("{album}", player.track_album) - .replace("{name}", player.name) - .replace("{identity}", player.identity), - ), - }), - }) - - const playericon = Widget.Icon({ - icon: Utils.merge([player.bind("entry"), monochrome.bind()], (entry => { - const name = `${entry}${monochrome.value ? "-symbolic" : ""}` - return icon(name, icons.fallback.audio) - })), - }) - - return Widget.Box({ - attribute: { revealer }, - children: direction.bind().as(d => d === "right" - ? [playericon, revealer] : [revealer, playericon]), - }) -} - -export default () => { - let player = getPlayer() - - const btn = PanelButton({ - class_name: "media", - child: Widget.Icon(icons.fallback.audio), - }) - - const update = () => { - player = getPlayer() - btn.visible = !!player - - if (!player) - return - - const content = Content(player) - const { revealer } = content.attribute - btn.child = content - btn.on_primary_click = () => { player.playPause() } - btn.on_secondary_click = () => { player.playPause() } - btn.on_scroll_up = () => { player.next() } - btn.on_scroll_down = () => { player.previous() } - btn.on_hover = () => { revealer.reveal_child = true } - btn.on_hover_lost = () => { revealer.reveal_child = false } - } - - return btn - .hook(preferred, update) - .hook(mpris, update, "notify::players") -} diff --git a/roles/ags/files/agsv2/widget/bar/buttons/Messages.ts b/roles/ags/files/agsv2/widget/bar/buttons/Messages.ts deleted file mode 100644 index a8971e9..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/Messages.ts +++ /dev/null @@ -1,16 +0,0 @@ -import icons from "lib/icons" -import PanelButton from "../PanelButton" -import options from "options" - -const n = await Service.import("notifications") -const notifs = n.bind("notifications") -const action = options.bar.messages.action.bind() - -export default () => PanelButton({ - class_name: "messages", - on_clicked: action, - visible: notifs.as(n => n.length > 0), - child: Widget.Box([ - Widget.Icon(icons.notifications.message), - ]), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/PowerMenu.ts b/roles/ags/files/agsv2/widget/bar/buttons/PowerMenu.ts deleted file mode 100644 index 4432ade..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/PowerMenu.ts +++ /dev/null @@ -1,15 +0,0 @@ -import icons from "lib/icons" -import PanelButton from "../PanelButton" -import options from "options" - -const { monochrome, action } = options.bar.powermenu - -export default () => PanelButton({ - window: "powermenu", - on_clicked: action.bind(), - child: Widget.Icon(icons.powermenu.shutdown), - setup: self => self.hook(monochrome, () => { - self.toggleClassName("colored", !monochrome.value) - self.toggleClassName("box") - }), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/ScreenRecord.ts b/roles/ags/files/agsv2/widget/bar/buttons/ScreenRecord.ts deleted file mode 100644 index 1d6eb36..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/ScreenRecord.ts +++ /dev/null @@ -1,21 +0,0 @@ -import PanelButton from "../PanelButton" -import screenrecord from "service/screenrecord" -import icons from "lib/icons" - -export default () => PanelButton({ - class_name: "recorder", - on_clicked: () => screenrecord.stop(), - visible: screenrecord.bind("recording"), - child: Widget.Box({ - children: [ - Widget.Icon(icons.recorder.recording), - Widget.Label({ - label: screenrecord.bind("timer").as(time => { - const sec = time % 60 - const min = Math.floor(time / 60) - return `${min}:${sec < 10 ? "0" + sec : sec}` - }), - }), - ], - }), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/SysTray.ts b/roles/ags/files/agsv2/widget/bar/buttons/SysTray.ts deleted file mode 100644 index 9f569d1..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/SysTray.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { type TrayItem } from "types/service/systemtray" -import PanelButton from "../PanelButton" -import Gdk from "gi://Gdk" -import options from "options" - -const systemtray = await Service.import("systemtray") -const { ignore } = options.bar.systray - -const SysTrayItem = (item: TrayItem) => PanelButton({ - class_name: "tray-item", - child: Widget.Icon({ icon: item.bind("icon") }), - tooltip_markup: item.bind("tooltip_markup"), - setup: self => { - const { menu } = item - if (!menu) - return - - const id = menu.connect("popped-up", () => { - self.toggleClassName("active") - menu.connect("notify::visible", () => { - self.toggleClassName("active", menu.visible) - }) - menu.disconnect(id!) - }) - - self.connect("destroy", () => menu.disconnect(id)) - }, - - on_primary_click: btn => item.menu?.popup_at_widget( - btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null), - - on_secondary_click: btn => item.menu?.popup_at_widget( - btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null), -}) - -export default () => Widget.Box() - .bind("children", systemtray, "items", i => i - .filter(({ id }) => !ignore.value.includes(id)) - .map(SysTrayItem)) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/SystemIndicators.ts b/roles/ags/files/agsv2/widget/bar/buttons/SystemIndicators.ts deleted file mode 100644 index 3fd916d..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/SystemIndicators.ts +++ /dev/null @@ -1,98 +0,0 @@ -import PanelButton from "../PanelButton" -import icons from "lib/icons" -import asusctl from "service/asusctl" - -const notifications = await Service.import("notifications") -const bluetooth = await Service.import("bluetooth") -const audio = await Service.import("audio") -const network = await Service.import("network") -const powerprof = await Service.import("powerprofiles") - -const ProfileIndicator = () => { - const visible = asusctl.available - ? asusctl.bind("profile").as(p => p !== "Balanced") - : powerprof.bind("active_profile").as(p => p !== "balanced") - - const icon = asusctl.available - ? asusctl.bind("profile").as(p => icons.asusctl.profile[p]) - : powerprof.bind("active_profile").as(p => icons.powerprofile[p]) - - return Widget.Icon({ visible, icon }) -} - -const ModeIndicator = () => { - if (!asusctl.available) { - return Widget.Icon({ - setup(self) { - Utils.idle(() => self.visible = false) - }, - }) - } - - return Widget.Icon({ - visible: asusctl.bind("mode").as(m => m !== "Hybrid"), - icon: asusctl.bind("mode").as(m => icons.asusctl.mode[m]), - }) -} - -const MicrophoneIndicator = () => Widget.Icon() - .hook(audio, self => self.visible = - audio.recorders.length > 0 - || audio.microphone.is_muted - || false) - .hook(audio.microphone, self => { - const vol = audio.microphone.is_muted ? 0 : audio.microphone.volume - const { muted, low, medium, high } = icons.audio.mic - const cons = [[67, high], [34, medium], [1, low], [0, muted]] as const - self.icon = cons.find(([n]) => n <= vol * 100)?.[1] || "" - }) - -const DNDIndicator = () => Widget.Icon({ - visible: notifications.bind("dnd"), - icon: icons.notifications.silent, -}) - -const BluetoothIndicator = () => Widget.Overlay({ - class_name: "bluetooth", - passThrough: true, - visible: bluetooth.bind("enabled"), - child: Widget.Icon({ - icon: icons.bluetooth.enabled, - }), - overlay: Widget.Label({ - hpack: "end", - vpack: "start", - label: bluetooth.bind("connected_devices").as(c => `${c.length}`), - visible: bluetooth.bind("connected_devices").as(c => c.length > 0), - }), -}) - -const NetworkIndicator = () => Widget.Icon().hook(network, self => { - const icon = network[network.primary || "wifi"]?.icon_name - self.icon = icon || "" - self.visible = !!icon -}) - -const AudioIndicator = () => Widget.Icon() - .hook(audio.speaker, self => { - const vol = audio.speaker.is_muted ? 0 : audio.speaker.volume - const { muted, low, medium, high, overamplified } = icons.audio.volume - const cons = [[101, overamplified], [67, high], [34, medium], [1, low], [0, muted]] as const - self.icon = cons.find(([n]) => n <= vol * 100)?.[1] || "" - }) - -export default () => PanelButton({ - window: "quicksettings", - on_clicked: () => App.toggleWindow("quicksettings"), - on_scroll_up: () => audio.speaker.volume += 0.02, - on_scroll_down: () => audio.speaker.volume -= 0.02, - child: Widget.Box([ - ProfileIndicator(), - ModeIndicator(), - DNDIndicator(), - BluetoothIndicator(), - NetworkIndicator(), - AudioIndicator(), - MicrophoneIndicator(), - ]), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/Taskbar.ts b/roles/ags/files/agsv2/widget/bar/buttons/Taskbar.ts deleted file mode 100644 index 13aa72d..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/Taskbar.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { launchApp, icon } from "lib/utils" -import icons from "lib/icons" -import options from "options" -import PanelButton from "../PanelButton" - -const hyprland = await Service.import("hyprland") -const apps = await Service.import("applications") -const { monochrome, exclusive, iconSize } = options.bar.taskbar -const { position } = options.bar - -const focus = (address: string) => hyprland.messageAsync( - `dispatch focuswindow address:${address}`) - -const DummyItem = (address: string) => Widget.Box({ - attribute: { address }, - visible: false, -}) - -const AppItem = (address: string) => { - const client = hyprland.getClient(address) - if (!client || client.class === "") - return DummyItem(address) - - const app = apps.list.find(app => app.match(client.class)) - - const btn = PanelButton({ - class_name: "panel-button", - tooltip_text: Utils.watch(client.title, hyprland, () => - hyprland.getClient(address)?.title || "", - ), - on_primary_click: () => focus(address), - on_middle_click: () => app && launchApp(app), - child: Widget.Icon({ - size: iconSize.bind(), - icon: monochrome.bind().as(m => icon( - (app?.icon_name || client.class) + (m ? "-symbolic" : ""), - icons.fallback.executable + (m ? "-symbolic" : ""), - )), - }), - }) - - return Widget.Box( - { - attribute: { address }, - visible: Utils.watch(true, [exclusive, hyprland], () => { - return exclusive.value - ? hyprland.active.workspace.id === client.workspace.id - : true - }), - }, - Widget.Overlay({ - child: btn, - pass_through: true, - overlay: Widget.Box({ - className: "indicator", - hpack: "center", - vpack: position.bind().as(p => p === "top" ? "start" : "end"), - setup: w => w.hook(hyprland, () => { - w.toggleClassName("active", hyprland.active.client.address === address) - }), - }), - }), - ) -} - -function sortItems(arr: T[]) { - return arr.sort(({ attribute: a }, { attribute: b }) => { - const aclient = hyprland.getClient(a.address)! - const bclient = hyprland.getClient(b.address)! - return aclient.workspace.id - bclient.workspace.id - }) -} - -export default () => Widget.Box({ - class_name: "taskbar", - children: hyprland.bind("clients").as(clients => sortItems(clients.map(c => AppItem(c.address)))), - // children: sortItems(hyprland.clients.map(c => AppItem(c.address))), - setup: w => w - .hook(hyprland, (w, address?: string) => { - if (typeof address === "string") - w.children = w.children.filter(ch => ch.attribute.address !== address) - }, "client-removed") - .hook(hyprland, (w, address?: string) => { - if (typeof address === "string") - w.children = sortItems([...w.children, AppItem(address)]) - }, "client-added") - .hook(hyprland, (w, event?: string) => { - if (event === "movewindow") - w.children = sortItems(w.children) - }, "event"), -}) diff --git a/roles/ags/files/agsv2/widget/bar/buttons/Workspaces.ts b/roles/ags/files/agsv2/widget/bar/buttons/Workspaces.ts deleted file mode 100644 index 73ea347..0000000 --- a/roles/ags/files/agsv2/widget/bar/buttons/Workspaces.ts +++ /dev/null @@ -1,38 +0,0 @@ -import PanelButton from "../PanelButton" -import options from "options" -import { sh, range } from "lib/utils" - -const hyprland = await Service.import("hyprland") -const { workspaces } = options.bar.workspaces - -const dispatch = (arg: string | number) => { - sh(`hyprctl dispatch workspace ${arg}`) -} - -const Workspaces = (ws: number) => Widget.Box({ - children: range(ws || 20).map(i => Widget.Label({ - attribute: i, - vpack: "center", - label: `${i}`, - setup: self => self.hook(hyprland, () => { - self.toggleClassName("active", hyprland.active.workspace.id === i) - self.toggleClassName("occupied", (hyprland.getWorkspace(i)?.windows || 0) > 0) - }), - })), - setup: box => { - if (ws === 0) { - box.hook(hyprland.active.workspace, () => box.children.map(btn => { - btn.visible = hyprland.workspaces.some(ws => ws.id === btn.attribute) - })) - } - }, -}) - -export default () => PanelButton({ - window: "overview", - class_name: "workspaces", - on_scroll_up: () => dispatch("m+1"), - on_scroll_down: () => dispatch("m-1"), - on_clicked: () => App.toggleWindow("overview"), - child: workspaces.bind().as(Workspaces), -}) diff --git a/roles/ags/files/agsv2/widget/datemenu/DateColumn.ts b/roles/ags/files/agsv2/widget/datemenu/DateColumn.ts deleted file mode 100644 index 94e7051..0000000 --- a/roles/ags/files/agsv2/widget/datemenu/DateColumn.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { clock, uptime } from "lib/variables" - -function up(up: number) { - const h = Math.floor(up / 60) - const m = Math.floor(up % 60) - return `uptime: ${h}:${m < 10 ? "0" + m : m}` -} - -export default () => Widget.Box({ - vertical: true, - class_name: "date-column vertical", - children: [ - Widget.Box({ - class_name: "clock-box", - vertical: true, - children: [ - Widget.Label({ - class_name: "clock", - label: clock.bind().as(t => t.format("%H:%M")!), - }), - Widget.Label({ - class_name: "uptime", - label: uptime.bind().as(up), - }), - ], - }), - Widget.Box({ - class_name: "calendar", - children: [ - Widget.Calendar({ - hexpand: true, - hpack: "center", - }), - ], - }), - ], -}) diff --git a/roles/ags/files/agsv2/widget/datemenu/DateMenu.ts b/roles/ags/files/agsv2/widget/datemenu/DateMenu.ts deleted file mode 100644 index f7fdf6d..0000000 --- a/roles/ags/files/agsv2/widget/datemenu/DateMenu.ts +++ /dev/null @@ -1,36 +0,0 @@ -import PopupWindow from "widget/PopupWindow" -import NotificationColumn from "./NotificationColumn" -import DateColumn from "./DateColumn" -import options from "options" - -const { bar, datemenu } = options -const pos = bar.position.bind() -const layout = Utils.derive([bar.position, datemenu.position], (bar, qs) => - `${bar}-${qs}` as const, -) - -const Settings = () => Widget.Box({ - class_name: "datemenu horizontal", - vexpand: false, - children: [ - NotificationColumn(), - Widget.Separator({ orientation: 1 }), - DateColumn(), - ], -}) - -const DateMenu = () => PopupWindow({ - name: "datemenu", - exclusivity: "exclusive", - transition: pos.as(pos => pos === "top" ? "slide_down" : "slide_up"), - layout: layout.value, - child: Settings(), -}) - -export function setupDateMenu() { - App.addWindow(DateMenu()) - layout.connect("changed", () => { - App.removeWindow("datemenu") - App.addWindow(DateMenu()) - }) -} diff --git a/roles/ags/files/agsv2/widget/datemenu/NotificationColumn.ts b/roles/ags/files/agsv2/widget/datemenu/NotificationColumn.ts deleted file mode 100644 index 07d6829..0000000 --- a/roles/ags/files/agsv2/widget/datemenu/NotificationColumn.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { type Notification as Notif } from "types/service/notifications" -import Notification from "widget/notifications/Notification" -import options from "options" -import icons from "lib/icons" - -const notifications = await Service.import("notifications") -const notifs = notifications.bind("notifications") - -const Animated = (n: Notif) => Widget.Revealer({ - transition_duration: options.transition.value, - transition: "slide_down", - child: Notification(n), - setup: self => Utils.timeout(options.transition.value, () => { - if (!self.is_destroyed) - self.reveal_child = true - }), -}) - -const ClearButton = () => Widget.Button({ - on_clicked: notifications.clear, - sensitive: notifs.as(n => n.length > 0), - child: Widget.Box({ - children: [ - Widget.Label("Clear "), - Widget.Icon({ - icon: notifs.as(n => icons.trash[n.length > 0 ? "full" : "empty"]), - }), - ], - }), -}) - -const Header = () => Widget.Box({ - class_name: "header", - children: [ - Widget.Label({ label: "Notifications", hexpand: true, xalign: 0 }), - ClearButton(), - ], -}) - -const NotificationList = () => { - const map: Map> = new Map - const box = Widget.Box({ - vertical: true, - children: notifications.notifications.map(n => { - const w = Animated(n) - map.set(n.id, w) - return w - }), - visible: notifs.as(n => n.length > 0), - }) - - function remove(_: unknown, id: number) { - const n = map.get(id) - if (n) { - n.reveal_child = false - Utils.timeout(options.transition.value, () => { - n.destroy() - map.delete(id) - }) - } - } - - return box - .hook(notifications, remove, "closed") - .hook(notifications, (_, id: number) => { - if (id !== undefined) { - if (map.has(id)) - remove(null, id) - - const n = notifications.getNotification(id)! - - const w = Animated(n) - map.set(id, w) - box.children = [w, ...box.children] - } - }, "notified") -} - -const Placeholder = () => Widget.Box({ - class_name: "placeholder", - vertical: true, - vpack: "center", - hpack: "center", - vexpand: true, - hexpand: true, - visible: notifs.as(n => n.length === 0), - children: [ - Widget.Icon(icons.notifications.silent), - Widget.Label("Your inbox is empty"), - ], -}) - -export default () => Widget.Box({ - class_name: "notifications", - css: options.notifications.width.bind().as(w => `min-width: ${w}px`), - vertical: true, - children: [ - Header(), - Widget.Scrollable({ - vexpand: true, - hscroll: "never", - class_name: "notification-scrollable", - child: Widget.Box({ - class_name: "notification-list vertical", - vertical: true, - children: [ - NotificationList(), - Placeholder(), - ], - }), - }), - ], -}) diff --git a/roles/ags/files/agsv2/widget/desktop/Desktop.ts b/roles/ags/files/agsv2/widget/desktop/Desktop.ts deleted file mode 100644 index f711967..0000000 --- a/roles/ags/files/agsv2/widget/desktop/Desktop.ts +++ /dev/null @@ -1,40 +0,0 @@ -import options from "options" -import { matugen } from "lib/matugen" -const mpris = await Service.import("mpris") - -const pref = () => options.bar.media.preferred.value - -export default (monitor: number) => Widget.Window({ - monitor, - layer: "bottom", - name: `desktop${monitor}`, - class_name: "desktop", - anchor: ["top", "bottom", "left", "right"], - child: Widget.Box({ - expand: true, - css: options.theme.dark.primary.bg.bind().as(c => ` - transition: 500ms; - background-color: ${c}`), - child: Widget.Box({ - class_name: "wallpaper", - expand: true, - vpack: "center", - hpack: "center", - setup: self => self - .hook(mpris, () => { - const img = mpris.getPlayer(pref())!.cover_path - matugen("image", img) - Utils.timeout(500, () => self.css = ` - background-image: url('${img}'); - background-size: contain; - background-repeat: no-repeat; - transition: 200ms; - min-width: 700px; - min-height: 700px; - border-radius: 30px; - box-shadow: 25px 25px 30px 0 rgba(0,0,0,0.5);`, - ) - }), - }), - }), -}) diff --git a/roles/ags/files/agsv2/widget/keybinds/Header.ts b/roles/ags/files/agsv2/widget/keybinds/Header.ts deleted file mode 100644 index 44bd011..0000000 --- a/roles/ags/files/agsv2/widget/keybinds/Header.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Header.ts -export default Widget.Box( - { vertical: true, className: 'header' }, - Widget.Box( - { hpack: 'center', className: 'title' }, - Widget.Label({ - label: 'Shortcut keys', - css: 'font-size: 3em; margin-right: 0.25em;', - }), - Widget.Label({ - label: '⌘ + /', - css: 'font-size: 2em;', - className: 'cheatsheet-key', - }), - ), - Widget.Label({ - useMarkup: true, - selectable: true, - className: 'data-stored', - label: 'Sheet data stored in ~/.config/ags/widget/keybinds/data/shortcuts.ts' - }), - Widget.Label({ - className: 'data-stored', - label: 'Window Directions: h = left, j = down, k = up, l = right' - }), -); diff --git a/roles/ags/files/agsv2/widget/keybinds/data/shortcuts.ts b/roles/ags/files/agsv2/widget/keybinds/data/shortcuts.ts deleted file mode 100644 index ca8643f..0000000 --- a/roles/ags/files/agsv2/widget/keybinds/data/shortcuts.ts +++ /dev/null @@ -1,93 +0,0 @@ -// TODO: Update keybindings -// COLUMN1 -const column1 = [ - { - icon: '', - name: 'Windows: Workspaces & Actions', - binds: [ - { keys: ['⌘', '+', '0 - 9'], action: 'Go to x Workspace' }, - { keys: ['⌘', '+', 'scroll'], action: 'Go to Adjacent Workspace' }, - { keys: ['⌘', '+', '󰘶', '+', '0 - 9'], action: 'Move X Window to X to Workspace' }, - { keys: ['⌘', '+', '󰳾'], action: 'Pick Up & Move Window'}, - { keys: ['⌘', '+', '󰳽'], action: 'Resize Window Upon Window Border'}, - { keys: ['⌘', '+', 'Q'], action: 'Quit Window' }, - { keys: ['⌘', '+', 'P'], action: 'Pass SUPER key through to a VM' }, - { keys: ['⌘', '+', '󱊷'], action: 'Regrab SUPER key from the VM' }, - ], - appeartick: 1 - }, - - { - icon: '', - name: 'Windows: Type', - binds: [ - { keys: ['⌘', '+', '󰘶', '+', 'F'], action: 'Fullscreen' }, - { keys: ['⌘', '+', ''], action: 'Focus window' }, - { keys: ['⌘', '+', 'T'], action: 'Toggle Fake Floating' }, - { keys: ['⌘', '+', 'J'], action: 'Flip Window position' }, - { keys: ['⌘', '+', 'G'], action: 'Display Window Name'}, - ], - appeartick: 1 - }, -] - -// COLUMN2 -const column2 = [ - { - icon: '󰜬', - name: 'Widgets (AGS)', - binds: [ - { keys: ['⌘', '+', '󰘶', '+', 'R'], action: 'Restart AGS' }, - { keys: ['⌘', '+', '󰘴', '+', '󰌑'], action: 'Toggle App Launcher' }, - { keys: ['⌘', '+', ''], action: 'Toggle Overview' }, - { keys: ['⌘', '+', '󱊮'], action: 'Toggle Power Menu' }, - { keys: ['⌘', '+', '/'], action: 'Toggle Shortcuts Menu' }, - ], - appeartick: 2 - }, - { - icon: '', - name: 'Screen Utilities', - binds: [ - { keys: ['PrtScr'], action: 'Selective Screenshot' }, - { keys: ['󰘶', '+', 'PrtScr'], action: 'Screenshot Full screen' }, - { keys: ['⌘', '+', '󱊶'], action: 'Selective Record' }, - ], - appeartick: 2 - }, - { - icon: '', - name: 'Session Shortcuts', - binds: [ - { keys: ['⌘', '+', 'L'], action: 'Lock Session' }, - { keys: ['⌘', '+', '󰘶', '+', 'M'], action: 'Log Out (Exit Hyprland)' }, - ], - appeartick: 2 - }, -] - -// COLUMN3 -const column3 = [ - { - icon: '󱓞', - name: 'Apps', - binds: [ - { keys: ['⌘', '+', '󰌑'], action: 'Launch Terminal: Wezterm' }, - { keys: ['⌘', '+', 'B'], action: 'Launch Browser: Zed Browser' }, - { keys: ['⌘', '+', 'E'], action: 'Launch File Manager: Nautilus' }, - { keys: ['󰘴', '+', '󰘵', '+', '󰆴'], action: 'Launch System Monitor: Mission Control' }, - ], - appeartick: 3 - }, - { - icon: '󰯃', - name: 'Scripts', - binds: [ - { keys: ['⌘', '+', '󰘵', '+', 'G'], action: 'Enable/Disable Gaming Mode' }, - { keys: ['󰘴', '+', '⌘', '+', 'N'], action: 'Toggle Night Light' }, - ], - appeartick: 3 - } -] - -export default [column1, column2, column3] diff --git a/roles/ags/files/agsv2/widget/keybinds/index.ts b/roles/ags/files/agsv2/widget/keybinds/index.ts deleted file mode 100644 index f29bc9c..0000000 --- a/roles/ags/files/agsv2/widget/keybinds/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import PopupWindow from "../PopupWindow"; -import Header from "./Header"; -import data from "./data/shortcuts"; - -function Keybinds() { - const [column1, column2, column3] = data; - - const renderColumn = (columnData) => ( - Widget.Box({ - vertical: true, - className: 'column-list', - children: columnData.map((category) => Widget.Box({ - vertical: true, - className: 'catagory-group', - children: [ - Widget.Label({ - xalign: 0, - className: 'catagory-label', - css: 'font-size: 1.5em;', - label: `${category.icon} ${category.name}` - }), - Widget.Box({ - vertical: false, - children: [ - Widget.Box({ - vertical: true, - homogeneous: true, - children: category.binds.map((keybinds) => Widget.Box({ - vertical: false, - className: 'catagory-row', - children: [ - Widget.Label({ - label: `${keybinds.keys.join(' ')} `, - className: `${['OR', '+'].includes(keybinds.keys[0]) ? 'cheatsheet-key-notkey' : 'cheatsheet-key cheatsheet-color-' + category.id}`, - }) - ] - })) - }), - Widget.Box({ - vertical: true, - homogeneous: true, - children: category.binds.map((keybinds) => Widget.Label({ - xalign: 0, - label: keybinds.action, - className: 'catagory-row', - })) - }) - ] - }) - ] - })) - }) - ); - - return Widget.Box({ - vertical: true, - className: "keybinds", - children: [ - Header, - Widget.Box({ - vertical: false, - className: "keybinds-list", - children: [ - renderColumn(column1), - renderColumn(column2), - renderColumn(column3) - ] - }) - ] - }); -} - -export default () => PopupWindow({ - name: 'keybinds', - layout: 'center', - child: Keybinds() -}); diff --git a/roles/ags/files/agsv2/widget/launcher/AppLauncher.ts b/roles/ags/files/agsv2/widget/launcher/AppLauncher.ts deleted file mode 100644 index 131fc85..0000000 --- a/roles/ags/files/agsv2/widget/launcher/AppLauncher.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { type Application } from "types/service/applications" -import { launchApp, icon } from "lib/utils" -import options from "options" -import icons from "lib/icons" - -const apps = await Service.import("applications") -const { query } = apps -const { iconSize } = options.launcher.apps - -const QuickAppButton = (app: Application) => Widget.Button({ - hexpand: true, - tooltip_text: app.name, - on_clicked: () => { - App.closeWindow("launcher") - launchApp(app) - }, - child: Widget.Icon({ - size: iconSize.bind(), - icon: icon(app.icon_name, icons.fallback.executable), - }), -}) - -const AppItem = (app: Application) => { - const title = Widget.Label({ - class_name: "title", - label: app.name, - hexpand: true, - xalign: 0, - vpack: "center", - truncate: "end", - }) - - const description = Widget.Label({ - class_name: "description", - label: app.description || "", - hexpand: true, - wrap: true, - max_width_chars: 30, - xalign: 0, - justification: "left", - vpack: "center", - }) - - const appicon = Widget.Icon({ - icon: icon(app.icon_name, icons.fallback.executable), - size: iconSize.bind(), - }) - - const textBox = Widget.Box({ - vertical: true, - vpack: "center", - children: app.description ? [title, description] : [title], - }) - - return Widget.Button({ - class_name: "app-item", - attribute: { app }, - child: Widget.Box({ - children: [appicon, textBox], - }), - on_clicked: () => { - App.closeWindow("launcher") - launchApp(app) - }, - }) -} -export function Favorites() { - const favs = options.launcher.apps.favorites.bind() - return Widget.Revealer({ - visible: favs.as(f => f.length > 0), - child: Widget.Box({ - vertical: true, - children: favs.as(favs => favs.flatMap(fs => [ - Widget.Separator(), - Widget.Box({ - class_name: "quicklaunch horizontal", - children: fs - .map(f => query(f)?.[0]) - .filter(f => f) - .map(QuickAppButton), - }), - ])), - }), - }) -} - -export function Launcher() { - const applist = Variable(query("")) - const max = options.launcher.apps.max - let first = applist.value[0] - - function SeparatedAppItem(app: Application) { - return Widget.Revealer( - { attribute: { app } }, - Widget.Box( - { vertical: true }, - Widget.Separator(), - AppItem(app), - ), - ) - } - - const list = Widget.Box({ - vertical: true, - children: applist.bind().as(list => list.map(SeparatedAppItem)), - setup: self => self - .hook(apps, () => applist.value = query(""), "notify::frequents"), - }) - - return Object.assign(list, { - filter(text: string | null) { - first = query(text || "")[0] - list.children.reduce((i, item) => { - if (!text || i >= max.value) { - item.reveal_child = false - return i - } - if (item.attribute.app.match(text)) { - item.reveal_child = true - return ++i - } - item.reveal_child = false - return i - }, 0) - }, - launchFirst() { - launchApp(first) - }, - }) -} diff --git a/roles/ags/files/agsv2/widget/launcher/Launcher.ts b/roles/ags/files/agsv2/widget/launcher/Launcher.ts deleted file mode 100644 index 68b71c8..0000000 --- a/roles/ags/files/agsv2/widget/launcher/Launcher.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { type Binding } from "lib/utils" -import PopupWindow, { Padding } from "widget/PopupWindow" -import icons from "lib/icons" -import options from "options" -import nix from "service/nix" -import * as AppLauncher from "./AppLauncher" -import * as NixRun from "./NixRun" -import * as ShRun from "./ShRun" - -const { width, margin } = options.launcher -const isnix = nix.available - -function Launcher() { - const favs = AppLauncher.Favorites() - const applauncher = AppLauncher.Launcher() - const sh = ShRun.ShRun() - const shicon = ShRun.Icon() - const nix = NixRun.NixRun() - const nixload = NixRun.Spinner() - - function HelpButton(cmd: string, desc: string | Binding) { - return Widget.Box( - { vertical: true }, - Widget.Separator(), - Widget.Button( - { - class_name: "help", - on_clicked: () => { - entry.grab_focus() - entry.text = `:${cmd} ` - entry.set_position(-1) - }, - }, - Widget.Box([ - Widget.Label({ - class_name: "name", - label: `:${cmd}`, - }), - Widget.Label({ - hexpand: true, - hpack: "end", - class_name: "description", - label: desc, - }), - ]), - ), - ) - } - - const help = Widget.Revealer({ - child: Widget.Box( - { vertical: true }, - HelpButton("sh", "run a binary"), - isnix ? HelpButton("nx", options.launcher.nix.pkgs.bind().as(pkg => - `run a nix package from ${pkg}`, - )) : Widget.Box(), - ), - }) - - const entry = Widget.Entry({ - hexpand: true, - primary_icon_name: icons.ui.search, - on_accept: ({ text }) => { - if (text?.startsWith(":nx")) - nix.run(text.substring(3)) - else if (text?.startsWith(":sh")) - sh.run(text.substring(3)) - else - applauncher.launchFirst() - - App.toggleWindow("launcher") - entry.text = "" - }, - on_change: ({ text }) => { - text ||= "" - favs.reveal_child = text === "" - help.reveal_child = text.split(" ").length === 1 && text?.startsWith(":") - - if (text?.startsWith(":nx")) - nix.filter(text.substring(3)) - else - nix.filter("") - - if (text?.startsWith(":sh")) - sh.filter(text.substring(3)) - else - sh.filter("") - - if (!text?.startsWith(":")) - applauncher.filter(text) - }, - }) - - function focus() { - entry.text = "Search" - entry.set_position(-1) - entry.select_region(0, -1) - entry.grab_focus() - favs.reveal_child = true - } - - const layout = Widget.Box({ - css: width.bind().as(v => `min-width: ${v}pt;`), - class_name: "launcher", - vertical: true, - vpack: "start", - setup: self => self.hook(App, (_, win, visible) => { - if (win !== "launcher") - return - - entry.text = "" - if (visible) - focus() - }), - children: [ - Widget.Box([entry, nixload, shicon]), - favs, - help, - applauncher, - nix, - sh, - ], - }) - - return Widget.Box( - { vertical: true, css: "padding: 1px" }, - Padding("launcher", { - css: margin.bind().as(v => `min-height: ${v}pt;`), - vexpand: false, - }), - layout, - ) -} - -export default () => PopupWindow({ - name: "launcher", - layout: "top", - child: Launcher(), -}) diff --git a/roles/ags/files/agsv2/widget/launcher/NixRun.ts b/roles/ags/files/agsv2/widget/launcher/NixRun.ts deleted file mode 100644 index cec9e09..0000000 --- a/roles/ags/files/agsv2/widget/launcher/NixRun.ts +++ /dev/null @@ -1,118 +0,0 @@ -import icons from "lib/icons" -import nix, { type Nixpkg } from "service/nix" - -const iconVisible = Variable(false) - -function Item(pkg: Nixpkg) { - const name = Widget.Label({ - class_name: "name", - label: pkg.name.split(".").at(-1), - }) - - const subpkg = pkg.name.includes(".") ? Widget.Label({ - class_name: "description", - hpack: "end", - hexpand: true, - label: ` ${pkg.name.split(".").slice(0, -1).join(".")}`, - }) : null - - const version = Widget.Label({ - class_name: "version", - label: pkg.version, - hexpand: true, - hpack: "end", - }) - - const description = pkg.description ? Widget.Label({ - class_name: "description", - label: pkg.description, - justification: "left", - wrap: true, - hpack: "start", - max_width_chars: 40, - }) : null - - return Widget.Box( - { - attribute: { name: pkg.name }, - vertical: true, - }, - Widget.Separator(), - Widget.Button( - { - class_name: "nix-item", - on_clicked: () => { - nix.run(pkg.name) - App.closeWindow("launcher") - }, - }, - Widget.Box( - { vertical: true }, - Widget.Box([name, version]), - Widget.Box([ - description as ReturnType, - subpkg as ReturnType, - ]), - ), - ), - ) -} - -export function Spinner() { - const icon = Widget.Icon({ - icon: icons.nix.nix, - class_name: "spinner", - css: ` - @keyframes spin { - to { -gtk-icon-transform: rotate(1turn); } - } - - image.spinning { - animation-name: spin; - animation-duration: 1s; - animation-timing-function: linear; - animation-iteration-count: infinite; - } - `, - setup: self => self.hook(nix, () => { - self.toggleClassName("spinning", !nix.ready) - }), - }) - - return Widget.Revealer({ - transition: "slide_left", - child: icon, - reveal_child: Utils.merge([ - nix.bind("ready"), - iconVisible.bind(), - ], (ready, show) => !ready || show), - }) -} - -export function NixRun() { - const list = Widget.Box>({ - vertical: true, - }) - - const revealer = Widget.Revealer({ - child: list, - }) - - async function filter(term: string) { - iconVisible.value = Boolean(term) - - if (!term) - revealer.reveal_child = false - - if (term.trim()) { - const found = await nix.query(term) - list.children = found.map(k => Item(nix.db[k])) - revealer.reveal_child = true - } - } - - return Object.assign(revealer, { - filter, - run: nix.run, - }) -} diff --git a/roles/ags/files/agsv2/widget/launcher/ShRun.ts b/roles/ags/files/agsv2/widget/launcher/ShRun.ts deleted file mode 100644 index 179e77d..0000000 --- a/roles/ags/files/agsv2/widget/launcher/ShRun.ts +++ /dev/null @@ -1,66 +0,0 @@ -import icons from "lib/icons" -import sh from "service/sh" - -const iconVisible = Variable(false) - -function Item(bin: string) { - return Widget.Box( - { - attribute: { bin }, - vertical: true, - }, - Widget.Separator(), - Widget.Button({ - child: Widget.Label({ - label: bin, - hpack: "start", - }), - class_name: "sh-item", - on_clicked: () => { - Utils.execAsync(bin) - App.closeWindow("launcher") - }, - }), - ) -} - -export function Icon() { - const icon = Widget.Icon({ - icon: icons.app.terminal, - class_name: "spinner", - }) - - return Widget.Revealer({ - transition: "slide_left", - child: icon, - reveal_child: iconVisible.bind(), - }) -} - -export function ShRun() { - const list = Widget.Box>({ - vertical: true, - }) - - const revealer = Widget.Revealer({ - child: list, - }) - - async function filter(term: string) { - iconVisible.value = Boolean(term) - - if (!term) - revealer.reveal_child = false - - if (term.trim()) { - const found = await sh.query(term) - list.children = found.map(Item) - revealer.reveal_child = true - } - } - - return Object.assign(revealer, { - filter, - run: sh.run, - }) -} diff --git a/roles/ags/files/agsv2/widget/notifications/Notification.ts b/roles/ags/files/agsv2/widget/notifications/Notification.ts deleted file mode 100644 index f05fba2..0000000 --- a/roles/ags/files/agsv2/widget/notifications/Notification.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { type Notification } from "types/service/notifications" -import GLib from "gi://GLib" -import icons from "lib/icons" - -const time = (time: number, format = "%H:%M") => GLib.DateTime - .new_from_unix_local(time) - .format(format) - -const NotificationIcon = ({ app_entry, app_icon, image }: Notification) => { - if (image) { - return Widget.Box({ - vpack: "start", - hexpand: false, - class_name: "icon img", - css: ` - background-image: url("${image}"); - background-size: cover; - background-repeat: no-repeat; - background-position: center; - min-width: 78px; - min-height: 78px; - `, - }) - } - - let icon = icons.fallback.notification - if (Utils.lookUpIcon(app_icon)) - icon = app_icon - - if (Utils.lookUpIcon(app_entry || "")) - icon = app_entry || "" - - return Widget.Box({ - vpack: "start", - hexpand: false, - class_name: "icon", - css: ` - min-width: 78px; - min-height: 78px; - `, - child: Widget.Icon({ - icon, - size: 58, - hpack: "center", hexpand: true, - vpack: "center", vexpand: true, - }), - }) -} - -export default (notification: Notification) => { - const content = Widget.Box({ - class_name: "content", - children: [ - NotificationIcon(notification), - Widget.Box({ - hexpand: true, - vertical: true, - children: [ - Widget.Box({ - children: [ - Widget.Label({ - class_name: "title", - xalign: 0, - justification: "left", - hexpand: true, - max_width_chars: 24, - truncate: "end", - wrap: true, - label: notification.summary.trim(), - use_markup: true, - }), - Widget.Label({ - class_name: "time", - vpack: "start", - label: time(notification.time), - }), - Widget.Button({ - class_name: "close-button", - vpack: "start", - child: Widget.Icon("window-close-symbolic"), - on_clicked: notification.close, - }), - ], - }), - Widget.Label({ - class_name: "description", - hexpand: true, - use_markup: true, - xalign: 0, - justification: "left", - label: notification.body.trim(), - max_width_chars: 24, - wrap: true, - }), - ], - }), - ], - }) - - const actionsbox = notification.actions.length > 0 ? Widget.Revealer({ - transition: "slide_down", - child: Widget.EventBox({ - child: Widget.Box({ - class_name: "actions horizontal", - children: notification.actions.map(action => Widget.Button({ - class_name: "action-button", - on_clicked: () => notification.invoke(action.id), - hexpand: true, - child: Widget.Label(action.label), - })), - }), - }), - }) : null - - const eventbox = Widget.EventBox({ - vexpand: false, - on_primary_click: notification.dismiss, - on_hover() { - if (actionsbox) - actionsbox.reveal_child = true - }, - on_hover_lost() { - if (actionsbox) - actionsbox.reveal_child = true - - notification.dismiss() - }, - child: Widget.Box({ - vertical: true, - children: actionsbox ? [content, actionsbox] : [content], - }), - }) - - return Widget.Box({ - class_name: `notification ${notification.urgency}`, - child: eventbox, - }) -} diff --git a/roles/ags/files/agsv2/widget/notifications/NotificationPopups.ts b/roles/ags/files/agsv2/widget/notifications/NotificationPopups.ts deleted file mode 100644 index a4a2b54..0000000 --- a/roles/ags/files/agsv2/widget/notifications/NotificationPopups.ts +++ /dev/null @@ -1,90 +0,0 @@ -import Notification from "./Notification" -import options from "options" - -const notifications = await Service.import("notifications") -const { transition } = options -const { position } = options.notifications -const { timeout, idle } = Utils - -function Animated(id: number) { - const n = notifications.getNotification(id)! - const widget = Notification(n) - - const inner = Widget.Revealer({ - transition: "slide_left", - transition_duration: transition.value, - child: widget, - }) - - const outer = Widget.Revealer({ - transition: "slide_down", - transition_duration: transition.value, - child: inner, - }) - - const box = Widget.Box({ - hpack: "end", - child: outer, - }) - - idle(() => { - outer.reveal_child = true - timeout(transition.value, () => { - inner.reveal_child = true - }) - }) - - return Object.assign(box, { - dismiss() { - inner.reveal_child = false - timeout(transition.value, () => { - outer.reveal_child = false - timeout(transition.value, () => { - box.destroy() - }) - }) - }, - }) -} - -function PopupList() { - const map: Map> = new Map - const box = Widget.Box({ - hpack: "end", - vertical: true, - css: options.notifications.width.bind().as(w => `min-width: ${w}px;`), - }) - - function remove(_: unknown, id: number) { - map.get(id)?.dismiss() - map.delete(id) - } - - return box - .hook(notifications, (_, id: number) => { - if (id !== undefined) { - if (map.has(id)) - remove(null, id) - - if (notifications.dnd) - return - - const w = Animated(id) - map.set(id, w) - box.children = [w, ...box.children] - } - }, "notified") - .hook(notifications, remove, "dismissed") - .hook(notifications, remove, "closed") -} - -export default (monitor: number) => Widget.Window({ - monitor, - name: `notifications${monitor}`, - anchor: position.bind(), - class_name: "notifications", - child: Widget.Box({ - css: "padding: 2px;", - child: PopupList(), - }), -}) diff --git a/roles/ags/files/agsv2/widget/osd/OSD.ts b/roles/ags/files/agsv2/widget/osd/OSD.ts deleted file mode 100644 index 467e6dc..0000000 --- a/roles/ags/files/agsv2/widget/osd/OSD.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { icon } from "lib/utils" -import icons from "lib/icons" -import Progress from "./Progress" -import brightness from "service/brightness" -import options from "options" - -const audio = await Service.import("audio") -const { progress, microphone } = options.osd - -const DELAY = 2500 - -function OnScreenProgress(vertical: boolean) { - const indicator = Widget.Icon({ - size: 42, - vpack: "start", - }) - const progress = Progress({ - vertical, - width: vertical ? 42 : 300, - height: vertical ? 300 : 42, - child: indicator, - }) - - const revealer = Widget.Revealer({ - transition: "slide_left", - child: progress, - }) - - let count = 0 - function show(value: number, icon: string) { - revealer.reveal_child = true - indicator.icon = icon - progress.setValue(value) - count++ - Utils.timeout(DELAY, () => { - count-- - - if (count === 0) - revealer.reveal_child = false - }) - } - - return revealer - .hook(brightness, () => show( - brightness.screen, - icons.brightness.screen, - ), "notify::screen") - .hook(brightness, () => show( - brightness.kbd, - icons.brightness.keyboard, - ), "notify::kbd") - .hook(audio.speaker, () => show( - audio.speaker.volume, - icon(audio.speaker.icon_name || "", icons.audio.type.speaker), - ), "notify::volume") -} - -function MicrophoneMute() { - const icon = Widget.Icon({ - class_name: "microphone", - }) - - const revealer = Widget.Revealer({ - transition: "slide_up", - child: icon, - }) - - let count = 0 - let mute = audio.microphone.stream?.is_muted ?? false - - return revealer.hook(audio.microphone, () => Utils.idle(() => { - if (mute !== audio.microphone.stream?.is_muted) { - mute = audio.microphone.stream!.is_muted - icon.icon = icons.audio.mic[mute ? "muted" : "high"] - revealer.reveal_child = true - count++ - - Utils.timeout(DELAY, () => { - count-- - if (count === 0) - revealer.reveal_child = false - }) - } - })) -} - -export default (monitor: number) => Widget.Window({ - monitor, - name: `indicator${monitor}`, - class_name: "indicator", - layer: "overlay", - click_through: true, - anchor: ["right", "left", "top", "bottom"], - child: Widget.Box({ - css: "padding: 2px;", - expand: true, - child: Widget.Overlay( - { child: Widget.Box({ expand: true }) }, - Widget.Box({ - hpack: progress.pack.h.bind(), - vpack: progress.pack.v.bind(), - child: progress.vertical.bind().as(OnScreenProgress), - }), - Widget.Box({ - hpack: microphone.pack.h.bind(), - vpack: microphone.pack.v.bind(), - child: MicrophoneMute(), - }), - ), - }), -}) diff --git a/roles/ags/files/agsv2/widget/osd/Progress.ts b/roles/ags/files/agsv2/widget/osd/Progress.ts deleted file mode 100644 index bcf27da..0000000 --- a/roles/ags/files/agsv2/widget/osd/Progress.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type Gtk from "gi://Gtk?version=3.0" -import GLib from "gi://GLib?version=2.0" -import { range } from "lib/utils" -import options from "options" - -type ProgressProps = { - height?: number - width?: number - vertical?: boolean - child: Gtk.Widget -} - -export default ({ - height = 18, - width = 180, - vertical = false, - child, -}: ProgressProps) => { - const fill = Widget.Box({ - class_name: "fill", - hexpand: vertical, - vexpand: !vertical, - hpack: vertical ? "fill" : "start", - vpack: vertical ? "end" : "fill", - child, - }) - - const container = Widget.Box({ - class_name: "progress", - child: fill, - css: ` - min-width: ${width}px; - min-height: ${height}px; - `, - }) - - let fill_size = 0 - let animations: number[] = [] - - return Object.assign(container, { - setValue(value: number) { - if (value < 0) - return - - if (animations.length > 0) { - for (const id of animations) - GLib.source_remove(id) - - animations = [] - } - - const axis = vertical ? "height" : "width" - const axisv = vertical ? height : width - const min = vertical ? width : height - const preferred = (axisv - min) * value + min - - if (!fill_size) { - fill_size = preferred - fill.css = `min-${axis}: ${preferred}px;` - return - } - - const frames = options.transition.value / 10 - const goal = preferred - fill_size - const step = goal / frames - - animations = range(frames, 0).map(i => Utils.timeout(5 * i, () => { - fill_size += step - fill.css = `min-${axis}: ${fill_size}px` - animations.shift() - })) - }, - }) -} diff --git a/roles/ags/files/agsv2/widget/overview/Overview.ts b/roles/ags/files/agsv2/widget/overview/Overview.ts deleted file mode 100644 index 8911920..0000000 --- a/roles/ags/files/agsv2/widget/overview/Overview.ts +++ /dev/null @@ -1,41 +0,0 @@ -import PopupWindow from "widget/PopupWindow" -import Workspace from "./Workspace" -import options from "options" -import { range } from "lib/utils" - -const hyprland = await Service.import("hyprland") - -const Overview = (ws: number) => Widget.Box({ - class_name: "overview horizontal", - children: ws > 0 - ? range(ws).map(Workspace) - : hyprland.workspaces - .map(({ id }) => Workspace(id)) - .sort((a, b) => a.attribute.id - b.attribute.id), - - setup: w => { - if (ws > 0) - return - - w.hook(hyprland, (w, id?: string) => { - if (id === undefined) - return - - w.children = w.children - .filter(ch => ch.attribute.id !== Number(id)) - }, "workspace-removed") - w.hook(hyprland, (w, id?: string) => { - if (id === undefined) - return - - w.children = [...w.children, Workspace(Number(id))] - .sort((a, b) => a.attribute.id - b.attribute.id) - }, "workspace-added") - }, -}) - -export default () => PopupWindow({ - name: "overview", - layout: "center", - child: options.overview.workspaces.bind().as(Overview), -}) diff --git a/roles/ags/files/agsv2/widget/overview/Window.ts b/roles/ags/files/agsv2/widget/overview/Window.ts deleted file mode 100644 index 02f71eb..0000000 --- a/roles/ags/files/agsv2/widget/overview/Window.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { type Client } from "types/service/hyprland" -import { createSurfaceFromWidget, icon } from "lib/utils" -import Gdk from "gi://Gdk" -import Gtk from "gi://Gtk?version=3.0" -import options from "options" -import icons from "lib/icons" - -const monochrome = options.overview.monochromeIcon -const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)] -const hyprland = await Service.import("hyprland") -const apps = await Service.import("applications") -const dispatch = (args: string) => hyprland.messageAsync(`dispatch ${args}`) - -export default ({ address, size: [w, h], class: c, title }: Client) => Widget.Button({ - class_name: "client", - attribute: { address }, - tooltip_text: `${title}`, - child: Widget.Icon({ - css: options.overview.scale.bind().as(v => ` - min-width: ${(v / 100) * w}px; - min-height: ${(v / 100) * h}px; - `), - icon: monochrome.bind().as(m => { - const app = apps.list.find(app => app.match(c)) - if (!app) - return icons.fallback.executable + (m ? "-symbolic" : "") - - - return icon( - app.icon_name + (m ? "-symbolic" : ""), - icons.fallback.executable + (m ? "-symbolic" : ""), - ) - }), - }), - on_secondary_click: () => dispatch(`closewindow address:${address}`), - on_clicked: () => { - dispatch(`focuswindow address:${address}`) - App.closeWindow("overview") - }, - setup: btn => btn - .on("drag-data-get", (_w, _c, data) => data.set_text(address, address.length)) - .on("drag-begin", (_, context) => { - Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(btn)) - btn.toggleClassName("hidden", true) - }) - .on("drag-end", () => btn.toggleClassName("hidden", false)) - .drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.COPY), -}) diff --git a/roles/ags/files/agsv2/widget/overview/Workspace.ts b/roles/ags/files/agsv2/widget/overview/Workspace.ts deleted file mode 100644 index 1b8d60b..0000000 --- a/roles/ags/files/agsv2/widget/overview/Workspace.ts +++ /dev/null @@ -1,76 +0,0 @@ -import Window from "./Window" -import Gdk from "gi://Gdk" -import Gtk from "gi://Gtk?version=3.0" -import options from "options" - -const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)] -const scale = (size: number) => (options.overview.scale.value / 100) * size -const hyprland = await Service.import("hyprland") - -const dispatch = (args: string) => hyprland.messageAsync(`dispatch ${args}`) - -const size = (id: number) => { - const def = { h: 1080, w: 1920 } - const ws = hyprland.getWorkspace(id) - if (!ws) - return def - - const mon = hyprland.getMonitor(ws.monitorID) - return mon ? { h: mon.height, w: mon.width } : def -} - -export default (id: number) => { - const fixed = Widget.Fixed() - - // TODO: early return if position is unchaged - async function update() { - const json = await hyprland.messageAsync("j/clients").catch(() => null) - if (!json) - return - - fixed.get_children().forEach(ch => ch.destroy()) - const clients = JSON.parse(json) as typeof hyprland.clients - clients - .filter(({ workspace }) => workspace.id === id) - .forEach(c => { - const x = c.at[0] - (hyprland.getMonitor(c.monitor)?.x || 0) - const y = c.at[1] - (hyprland.getMonitor(c.monitor)?.y || 0) - c.mapped && fixed.put(Window(c), scale(x), scale(y)) - }) - fixed.show_all() - } - - return Widget.Box({ - attribute: { id }, - tooltipText: `${id}`, - class_name: "workspace", - vpack: "center", - css: options.overview.scale.bind().as(v => ` - min-width: ${(v / 100) * size(id).w}px; - min-height: ${(v / 100) * size(id).h}px; - `), - setup(box) { - box.hook(options.overview.scale, update) - box.hook(hyprland, update, "notify::clients") - box.hook(hyprland.active.client, update) - box.hook(hyprland.active.workspace, () => { - box.toggleClassName("active", hyprland.active.workspace.id === id) - }) - }, - child: Widget.EventBox({ - expand: true, - on_primary_click: () => { - App.closeWindow("overview") - dispatch(`workspace ${id}`) - }, - setup: eventbox => { - eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY) - eventbox.connect("drag-data-received", (_w, _c, _x, _y, data) => { - const address = new TextDecoder().decode(data.get_data()) - dispatch(`movetoworkspacesilent ${id},address:${address}`) - }) - }, - child: fixed, - }), - }) -} diff --git a/roles/ags/files/agsv2/widget/powermenu/PowerMenu.ts b/roles/ags/files/agsv2/widget/powermenu/PowerMenu.ts deleted file mode 100644 index cebf1f3..0000000 --- a/roles/ags/files/agsv2/widget/powermenu/PowerMenu.ts +++ /dev/null @@ -1,61 +0,0 @@ -import PopupWindow from "widget/PopupWindow" -import powermenu, { type Action } from "service/powermenu" -import icons from "lib/icons" -import options from "options" -import type Gtk from "gi://Gtk?version=3.0" - -const { layout, labels } = options.powermenu - -const SysButton = (action: Action, label: string) => Widget.Button({ - on_clicked: () => powermenu.action(action), - child: Widget.Box({ - vertical: true, - class_name: "system-button", - children: [ - Widget.Icon(icons.powermenu[action]), - Widget.Label({ - label, - visible: labels.bind(), - }), - ], - }), -}) - -export default () => PopupWindow({ - name: "powermenu", - transition: "crossfade", - child: Widget.Box({ - class_name: "powermenu horizontal", - setup: self => self.hook(layout, () => { - self.toggleClassName("box", layout.value === "box") - self.toggleClassName("line", layout.value === "line") - }), - children: layout.bind().as(layout => { - switch (layout) { - case "line": return [ - SysButton("shutdown", "Shutdown"), - SysButton("reboot", "Reboot"), - SysButton("sleep", "Sleep"), - SysButton("logout", "Log Out"), - SysButton("lock", "Lock"), - ] - case "box": return [ - Widget.Box( - { vertical: true }, - SysButton("shutdown", "Shutdown"), - SysButton("lock", "Lock"), - ), - Widget.Box( - { vertical: true }, - SysButton("reboot", "Reboot"), - SysButton("logout", "Log Out"), - ), - Widget.Box( - { vertical: true }, - SysButton("sleep", "Sleep"), - ), - ] - } - }), - }), -}) diff --git a/roles/ags/files/agsv2/widget/powermenu/Verification.ts b/roles/ags/files/agsv2/widget/powermenu/Verification.ts deleted file mode 100644 index 3145ce5..0000000 --- a/roles/ags/files/agsv2/widget/powermenu/Verification.ts +++ /dev/null @@ -1,47 +0,0 @@ -import PopupWindow from "widget/PopupWindow" -import powermenu from "service/powermenu" - -export default () => PopupWindow({ - name: "verification", - transition: "crossfade", - child: Widget.Box({ - class_name: "verification", - vertical: true, - children: [ - Widget.Box({ - class_name: "text-box", - vertical: true, - children: [ - Widget.Label({ - class_name: "title", - label: powermenu.bind("title"), - }), - Widget.Label({ - class_name: "desc", - label: "Are you sure?", - }), - ], - }), - Widget.Box({ - class_name: "buttons horizontal", - vexpand: true, - vpack: "end", - homogeneous: true, - children: [ - Widget.Button({ - child: Widget.Label("No"), - on_clicked: () => App.toggleWindow("verification"), - setup: self => self.hook(App, (_, name: string, visible: boolean) => { - if (name === "verification" && visible) - self.grab_focus() - }), - }), - Widget.Button({ - child: Widget.Label("Yes"), - on_clicked: powermenu.exec, - }), - ], - }), - ], - }), -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/QuickSettings.ts b/roles/ags/files/agsv2/widget/quicksettings/QuickSettings.ts deleted file mode 100644 index 4bc46bc..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/QuickSettings.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type Gtk from "gi://Gtk?version=3.0" -import { ProfileSelector, ProfileToggle } from "./widgets/PowerProfile" -import { Header } from "./widgets/Header" -import { Volume, Microphone, SinkSelector, AppMixer } from "./widgets/Volume" -import { Brightness } from "./widgets/Brightness" -import { NetworkToggle, WifiSelection } from "./widgets/Network" -import { BluetoothToggle, BluetoothDevices } from "./widgets/Bluetooth" -import { DND } from "./widgets/DND" -import { DarkModeToggle } from "./widgets/DarkMode" -import { MicMute } from "./widgets/MicMute" -import { Media } from "./widgets/Media" -import PopupWindow from "widget/PopupWindow" -import options from "options" - -const { bar, quicksettings } = options -const media = (await Service.import("mpris")).bind("players") -const layout = Utils.derive([bar.position, quicksettings.position], (bar, qs) => - `${bar}-${qs}` as const, -) - -const Row = ( - toggles: Array<() => Gtk.Widget> = [], - menus: Array<() => Gtk.Widget> = [], -) => Widget.Box({ - vertical: true, - children: [ - Widget.Box({ - homogeneous: true, - class_name: "row horizontal", - children: toggles.map(w => w()), - }), - ...menus.map(w => w()), - ], -}) - -const Settings = () => Widget.Box({ - vertical: true, - class_name: "quicksettings vertical", - css: quicksettings.width.bind().as(w => `min-width: ${w}px;`), - children: [ - Header(), - Widget.Box({ - class_name: "sliders-box vertical", - vertical: true, - children: [ - Row( - [Volume], - [SinkSelector, AppMixer], - ), - Microphone(), - Brightness(), - ], - }), - Row( - [NetworkToggle, BluetoothToggle], - [WifiSelection, BluetoothDevices], - ), - Row( - [ProfileToggle, DarkModeToggle], - [ProfileSelector], - ), - Row([MicMute, DND]), - Widget.Box({ - visible: media.as(l => l.length > 0), - child: Media(), - }), - ], -}) - -const QuickSettings = () => PopupWindow({ - name: "quicksettings", - exclusivity: "exclusive", - transition: bar.position.bind().as(pos => pos === "top" ? "slide_down" : "slide_up"), - layout: layout.value, - child: Settings(), -}) - -export function setupQuickSettings() { - App.addWindow(QuickSettings()) - layout.connect("changed", () => { - App.removeWindow("quicksettings") - App.addWindow(QuickSettings()) - }) -} diff --git a/roles/ags/files/agsv2/widget/quicksettings/ToggleButton.ts b/roles/ags/files/agsv2/widget/quicksettings/ToggleButton.ts deleted file mode 100644 index 1380acf..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/ToggleButton.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { type IconProps } from "types/widgets/icon" -import { type LabelProps } from "types/widgets/label" -import type GObject from "gi://GObject?version=2.0" -import type Gtk from "gi://Gtk?version=3.0" -import icons from "lib/icons" - -export const opened = Variable("") -App.connect("window-toggled", (_, name: string, visible: boolean) => { - if (name === "quicksettings" && !visible) - Utils.timeout(500, () => opened.value = "") -}) - -export const Arrow = (name: string, activate?: false | (() => void)) => { - let deg = 0 - let iconOpened = false - const icon = Widget.Icon(icons.ui.arrow.right).hook(opened, () => { - if (opened.value === name && !iconOpened || opened.value !== name && iconOpened) { - const step = opened.value === name ? 10 : -10 - iconOpened = !iconOpened - for (let i = 0; i < 9; ++i) { - Utils.timeout(15 * i, () => { - deg += step - icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`) - }) - } - } - }) - return Widget.Button({ - child: icon, - class_name: "arrow", - on_clicked: () => { - opened.value = opened.value === name ? "" : name - if (typeof activate === "function") - activate() - }, - }) -} - -type ArrowToggleButtonProps = { - name: string - icon: IconProps["icon"] - label: LabelProps["label"] - activate: () => void - deactivate: () => void - activateOnArrow?: boolean - connection: [GObject.Object, () => boolean] -} -export const ArrowToggleButton = ({ - name, - icon, - label, - activate, - deactivate, - activateOnArrow = true, - connection: [service, condition], -}: ArrowToggleButtonProps) => Widget.Box({ - class_name: "toggle-button", - setup: self => self.hook(service, () => { - self.toggleClassName("active", condition()) - }), - children: [ - Widget.Button({ - child: Widget.Box({ - hexpand: true, - children: [ - Widget.Icon({ - class_name: "icon", - icon, - }), - Widget.Label({ - class_name: "label", - max_width_chars: 10, - truncate: "end", - label, - }), - ], - }), - on_clicked: () => { - if (condition()) { - deactivate() - if (opened.value === name) - opened.value = "" - } else { - activate() - } - }, - }), - Arrow(name, activateOnArrow && activate), - ], -}) - -type MenuProps = { - name: string - icon: IconProps["icon"] - title: LabelProps["label"] - content: Gtk.Widget[] -} -export const Menu = ({ name, icon, title, content }: MenuProps) => Widget.Revealer({ - transition: "slide_down", - reveal_child: opened.bind().as(v => v === name), - child: Widget.Box({ - class_names: ["menu", name], - vertical: true, - children: [ - Widget.Box({ - class_name: "title-box", - children: [ - Widget.Icon({ - class_name: "icon", - icon, - }), - Widget.Label({ - class_name: "title", - truncate: "end", - label: title, - }), - ], - }), - Widget.Separator(), - Widget.Box({ - vertical: true, - class_name: "content vertical", - children: content, - }), - ], - }), -}) - -type SimpleToggleButtonProps = { - icon: IconProps["icon"] - label: LabelProps["label"] - toggle: () => void - connection: [GObject.Object, () => boolean] -} -export const SimpleToggleButton = ({ - icon, - label, - toggle, - connection: [service, condition], -}: SimpleToggleButtonProps) => Widget.Button({ - on_clicked: toggle, - class_name: "simple-toggle", - setup: self => self.hook(service, () => { - self.toggleClassName("active", condition()) - }), - child: Widget.Box([ - Widget.Icon({ icon }), - Widget.Label({ - max_width_chars: 10, - truncate: "end", - label, - }), - ]), -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/Bluetooth.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/Bluetooth.ts deleted file mode 100644 index 0b825c3..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/Bluetooth.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { type BluetoothDevice } from "types/service/bluetooth" -import { Menu, ArrowToggleButton } from "../ToggleButton" -import { dependencies, sh } from "lib/utils" -import options from "options" -import icons from "lib/icons" - -const bluetooth = await Service.import("bluetooth") - -export const BluetoothToggle = () => ArrowToggleButton({ - name: "bluetooth", - icon: bluetooth.bind("enabled").as(p => icons.bluetooth[p ? "enabled" : "disabled"]), - label: Utils.watch("Disabled", bluetooth, () => { - if (!bluetooth.enabled) - return "Disabled" - - if (bluetooth.connected_devices.length === 1) - return bluetooth.connected_devices[0].alias - - return `${bluetooth.connected_devices.length} Connected` - }), - connection: [bluetooth, () => bluetooth.enabled], - deactivate: () => bluetooth.enabled = false, - activate: () => bluetooth.enabled = true, -}) - -const DeviceItem = (device: BluetoothDevice) => Widget.Box({ - children: [ - Widget.Icon(device.icon_name + "-symbolic"), - Widget.Label(device.name), - Widget.Label({ - label: `${device.battery_percentage}%`, - visible: device.bind("battery_percentage").as(p => p > 0), - }), - Widget.Box({ hexpand: true }), - Widget.Spinner({ - active: device.bind("connecting"), - visible: device.bind("connecting"), - }), - Widget.Switch({ - active: device.connected, - visible: device.bind("connecting").as(p => !p), - setup: self => self.on("notify::active", () => { - device.setConnection(self.active) - }), - }), - ], -}) - -export const BluetoothDevices = () => Menu({ - name: "bluetooth", - icon: icons.bluetooth.disabled, - title: "Bluetooth", - content: [ - Widget.Box({ - class_name: "bluetooth-devices", - hexpand: true, - vertical: true, - children: bluetooth.bind("devices").as(ds => ds - .filter(d => d.name) - .map(DeviceItem)), - }), - Widget.Separator(), - Widget.Button({ - on_clicked: () => sh(options.quicksettings.bluetoothSettings.value), - child: Widget.Box({ - children: [ - Widget.Icon(icons.ui.settings), - Widget.Label("Settings"), - ], - }), - }), - ], -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/Brightness.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/Brightness.ts deleted file mode 100644 index a3ce565..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/Brightness.ts +++ /dev/null @@ -1,23 +0,0 @@ -import icons from "lib/icons" -import brightness from "service/brightness" - -const BrightnessSlider = () => Widget.Slider({ - draw_value: false, - hexpand: true, - value: brightness.bind("screen"), - on_change: ({ value }) => brightness.screen = value, -}) - -export const Brightness = () => Widget.Box({ - class_name: "brightness", - children: [ - Widget.Button({ - vpack: "center", - child: Widget.Icon(icons.brightness.indicator), - on_clicked: () => brightness.screen = 0, - tooltip_text: brightness.bind("screen").as(v => - `Screen Brightness: ${Math.floor(v * 100)}%`), - }), - BrightnessSlider(), - ], -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/DND.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/DND.ts deleted file mode 100644 index 7fc1fd0..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/DND.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { SimpleToggleButton } from "../ToggleButton" -import icons from "lib/icons" - -const n = await Service.import("notifications") -const dnd = n.bind("dnd") - -export const DND = () => SimpleToggleButton({ - icon: dnd.as(dnd => icons.notifications[dnd ? "silent" : "noisy"]), - label: dnd.as(dnd => dnd ? "Silent" : "Noisy"), - toggle: () => n.dnd = !n.dnd, - connection: [n, () => n.dnd], -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/DarkMode.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/DarkMode.ts deleted file mode 100644 index 9ec94df..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/DarkMode.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { SimpleToggleButton } from "../ToggleButton" -import icons from "lib/icons" -import options from "options" - -const { scheme } = options.theme - -export const DarkModeToggle = () => SimpleToggleButton({ - icon: scheme.bind().as(s => icons.color[s]), - label: scheme.bind().as(s => s === "dark" ? "Dark" : "Light"), - toggle: () => scheme.value = scheme.value === "dark" ? "light" : "dark", - connection: [scheme, () => scheme.value === "dark"], -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/Header.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/Header.ts deleted file mode 100644 index 4ab548b..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/Header.ts +++ /dev/null @@ -1,85 +0,0 @@ -import icons from "lib/icons" -import { uptime } from "lib/variables" -import { dependencies, sh } from "lib/utils" -import options from "options" -import powermenu, { Action } from "service/powermenu" - -const battery = await Service.import("battery") -const { image, size } = options.quicksettings.avatar - -function up(up: number) { - const h = Math.floor(up / 60) - const m = Math.floor(up % 60) - return `${h}h ${m < 10 ? "0" + m : m}m` -} - -const Avatar = () => Widget.Box({ - class_name: "avatar", - css: Utils.merge([image.bind(), size.bind()], (img, size) => ` - min-width: ${size * 2}px; - min-height: ${size * 2}px; - background-image: url('${img}'); - background-size: cover; - `), -}) - -const SysButton = (action: Action) => Widget.Button({ - vpack: "center", - child: Widget.Icon(icons.powermenu[action]), - on_clicked: () => powermenu.action(action), -}) - -const LocalSend = () => Widget.Button({ - vpack: "center", - child: Widget.Icon(icons.ui.airdrop), - on_clicked: () => sh(options.quicksettings.localSend.value), -}) - -export const Header = () => Widget.Box( - { class_name: "header horizontal" }, - Avatar(), - Widget.Box({ - vertical: true, - vpack: "center", - children: [ - Widget.Box({ - visible: battery.bind("available"), - children: [ - Widget.Icon({ icon: battery.bind("icon_name") }), - Widget.Label({ label: battery.bind("percent").as(p => `${p}%`) }), - ], - }), - Widget.Box([ - Widget.Icon({ icon: icons.ui.time }), - Widget.Label({ label: uptime.bind().as(up) }), - ]), - ], - }), - Widget.Box({ hexpand: true }), - Widget.Box({ - vertical: true, - class_name: "right-buttons", - children: [ - Widget.Box({ - children: [ - LocalSend(), - Widget.Button({ - vpack: "center", - child: Widget.Icon(icons.ui.settings), - on_clicked: () => { - App.closeWindow("quicksettings") - App.closeWindow("settings-dialog") - App.openWindow("settings-dialog") - }, - }), - ], - }), - Widget.Box({ - children: [ - SysButton("logout"), - SysButton("shutdown"), - ], - }), - ], - }), -) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/Media.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/Media.ts deleted file mode 100644 index 52254ea..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/Media.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { type MprisPlayer } from "types/service/mpris" -import icons from "lib/icons" -import options from "options" -import { icon } from "lib/utils" - -const mpris = await Service.import("mpris") -const players = mpris.bind("players") -const { media } = options.quicksettings - -function lengthStr(length: number) { - const min = Math.floor(length / 60) - const sec = Math.floor(length % 60) - const sec0 = sec < 10 ? "0" : "" - return `${min}:${sec0}${sec}` -} - -const Player = (player: MprisPlayer) => { - const cover = Widget.Box({ - class_name: "cover", - vpack: "start", - css: Utils.merge([ - player.bind("cover_path"), - player.bind("track_cover_url"), - media.coverSize.bind(), - ], (path, url, size) => ` - min-width: ${size}px; - min-height: ${size}px; - background-image: url('${path || url}'); - `), - }) - - const title = Widget.Label({ - class_name: "title", - max_width_chars: 20, - truncate: "end", - hpack: "start", - label: player.bind("track_title"), - }) - - const artist = Widget.Label({ - class_name: "artist", - max_width_chars: 20, - truncate: "end", - hpack: "start", - label: player.bind("track_artists").as(a => a.join(", ")), - }) - - const positionSlider = Widget.Slider({ - class_name: "position", - draw_value: false, - on_change: ({ value }) => player.position = value * player.length, - setup: self => { - const update = () => { - const { length, position } = player - self.visible = length > 0 - self.value = length > 0 ? position / length : 0 - } - self.hook(player, update) - self.hook(player, update, "position") - self.poll(1000, update) - }, - }) - - const positionLabel = Widget.Label({ - class_name: "position", - hpack: "start", - setup: self => { - const update = (_: unknown, time?: number) => { - self.label = lengthStr(time || player.position) - self.visible = player.length > 0 - } - self.hook(player, update, "position") - self.poll(1000, update) - }, - }) - - const lengthLabel = Widget.Label({ - class_name: "length", - hpack: "end", - visible: player.bind("length").as(l => l > 0), - label: player.bind("length").as(lengthStr), - }) - - const playericon = Widget.Icon({ - class_name: "icon", - hexpand: true, - hpack: "end", - vpack: "start", - tooltip_text: player.identity || "", - icon: Utils.merge([player.bind("entry"), media.monochromeIcon.bind()], (e, s) => { - const name = `${e}${s ? "-symbolic" : ""}` - return icon(name, icons.fallback.audio) - }), - }) - - const playPause = Widget.Button({ - class_name: "play-pause", - on_clicked: () => player.playPause(), - visible: player.bind("can_play"), - child: Widget.Icon({ - icon: player.bind("play_back_status").as(s => { - switch (s) { - case "Playing": return icons.mpris.playing - case "Paused": - case "Stopped": return icons.mpris.stopped - } - }), - }), - }) - - const prev = Widget.Button({ - on_clicked: () => player.previous(), - visible: player.bind("can_go_prev"), - child: Widget.Icon(icons.mpris.prev), - }) - - const next = Widget.Button({ - on_clicked: () => player.next(), - visible: player.bind("can_go_next"), - child: Widget.Icon(icons.mpris.next), - }) - - return Widget.Box( - { class_name: "player", vexpand: false }, - cover, - Widget.Box( - { vertical: true }, - Widget.Box([ - title, - playericon, - ]), - artist, - Widget.Box({ vexpand: true }), - positionSlider, - Widget.CenterBox({ - class_name: "footer horizontal", - start_widget: positionLabel, - center_widget: Widget.Box([ - prev, - playPause, - next, - ]), - end_widget: lengthLabel, - }), - ), - ) -} - -export const Media = () => Widget.Box({ - vertical: true, - class_name: "media vertical", - children: players.as(p => p.map(Player)), -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/MicMute.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/MicMute.ts deleted file mode 100644 index b6e9454..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/MicMute.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SimpleToggleButton } from "../ToggleButton" -import icons from "lib/icons" -const { microphone } = await Service.import("audio") - -const icon = () => microphone.is_muted || microphone.stream?.is_muted - ? icons.audio.mic.muted - : icons.audio.mic.high - -const label = () => microphone.is_muted || microphone.stream?.is_muted - ? "Muted" - : "Unmuted" - -export const MicMute = () => SimpleToggleButton({ - icon: Utils.watch(icon(), microphone, icon), - label: Utils.watch(label(), microphone, label), - toggle: () => microphone.is_muted = !microphone.is_muted, - connection: [microphone, () => microphone?.is_muted || false], -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/Network.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/Network.ts deleted file mode 100644 index 0ef4256..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/Network.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Menu, ArrowToggleButton } from "../ToggleButton" -import icons from "lib/icons.js" -import { dependencies, sh } from "lib/utils" -import options from "options" -const { wifi } = await Service.import("network") - -export const NetworkToggle = () => ArrowToggleButton({ - name: "network", - icon: wifi.bind("icon_name"), - label: wifi.bind("ssid").as(ssid => ssid || "Not Connected"), - connection: [wifi, () => wifi.enabled], - deactivate: () => wifi.enabled = false, - activate: () => { - wifi.enabled = true - wifi.scan() - }, -}) - -export const WifiSelection = () => Menu({ - name: "network", - icon: wifi.bind("icon_name"), - title: "Wifi Selection", - content: [ - Widget.Box({ - vertical: true, - setup: self => self.hook(wifi, () => self.children = - wifi.access_points - .sort((a, b) => b.strength - a.strength) - .slice(0, 10) - .map(ap => Widget.Button({ - on_clicked: () => { - if (dependencies("nmcli")) - Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`) - }, - child: Widget.Box({ - children: [ - Widget.Icon(ap.iconName), - Widget.Label(ap.ssid || ""), - Widget.Icon({ - icon: icons.ui.tick, - hexpand: true, - hpack: "end", - setup: self => Utils.idle(() => { - if (!self.is_destroyed) - self.visible = ap.active - }), - }), - ], - }), - })), - ), - }), - Widget.Separator(), - Widget.Button({ - on_clicked: () => sh(options.quicksettings.networkSettings.value), - child: Widget.Box({ - children: [ - Widget.Icon(icons.ui.settings), - Widget.Label("Settings"), - ], - }), - }), - ], -}) diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/PowerProfile.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/PowerProfile.ts deleted file mode 100644 index f566aaf..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/PowerProfile.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { ArrowToggleButton, Menu } from "../ToggleButton" -import icons from "lib/icons" - -import asusctl from "service/asusctl" -const asusprof = asusctl.bind("profile") - -const AsusProfileToggle = () => ArrowToggleButton({ - name: "asusctl-profile", - icon: asusprof.as(p => icons.asusctl.profile[p]), - label: asusprof, - connection: [asusctl, () => asusctl.profile !== "Balanced"], - activate: () => asusctl.setProfile("Quiet"), - deactivate: () => asusctl.setProfile("Balanced"), - activateOnArrow: false, -}) - -const AsusProfileSelector = () => Menu({ - name: "asusctl-profile", - icon: asusprof.as(p => icons.asusctl.profile[p]), - title: "Profile Selector", - content: [ - Widget.Box({ - vertical: true, - hexpand: true, - children: [ - Widget.Box({ - vertical: true, - children: asusctl.profiles.map(prof => Widget.Button({ - on_clicked: () => asusctl.setProfile(prof), - child: Widget.Box({ - children: [ - Widget.Icon(icons.asusctl.profile[prof]), - Widget.Label(prof), - ], - }), - })), - }), - ], - }), - Widget.Separator(), - Widget.Button({ - on_clicked: () => Utils.execAsync("rog-control-center"), - child: Widget.Box({ - children: [ - Widget.Icon(icons.ui.settings), - Widget.Label("Rog Control Center"), - ], - }), - }), - ], -}) - - -const pp = await Service.import("powerprofiles") -const profile = pp.bind("active_profile") -const profiles = pp.profiles.map(p => p.Profile) - -const pretty = (str: string) => str - .split("-") - .map(str => `${str.at(0)?.toUpperCase()}${str.slice(1)}`) - .join(" ") - -const PowerProfileToggle = () => ArrowToggleButton({ - name: "asusctl-profile", - icon: profile.as(p => icons.powerprofile[p]), - label: profile.as(pretty), - connection: [pp, () => pp.active_profile !== profiles[1]], - activate: () => pp.active_profile = profiles[0], - deactivate: () => pp.active_profile = profiles[1], - activateOnArrow: false, -}) - -const PowerProfileSelector = () => Menu({ - name: "asusctl-profile", - icon: profile.as(p => icons.powerprofile[p]), - title: "Profile Selector", - content: [Widget.Box({ - vertical: true, - hexpand: true, - child: Widget.Box({ - vertical: true, - children: profiles.map(prof => Widget.Button({ - on_clicked: () => pp.active_profile = prof, - child: Widget.Box({ - children: [ - Widget.Icon(icons.powerprofile[prof]), - Widget.Label(pretty(prof)), - ], - }), - })), - }), - })], -}) - -export const ProfileToggle = asusctl.available - ? AsusProfileToggle : PowerProfileToggle - -export const ProfileSelector = asusctl.available - ? AsusProfileSelector : PowerProfileSelector diff --git a/roles/ags/files/agsv2/widget/quicksettings/widgets/Volume.ts b/roles/ags/files/agsv2/widget/quicksettings/widgets/Volume.ts deleted file mode 100644 index 7d2c897..0000000 --- a/roles/ags/files/agsv2/widget/quicksettings/widgets/Volume.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { type Stream } from "types/service/audio" -import { Arrow, Menu } from "../ToggleButton" -import { dependencies, icon, sh } from "lib/utils" -import icons from "lib/icons.js" -const audio = await Service.import("audio") - -type Type = "microphone" | "speaker" - -const VolumeIndicator = (type: Type = "speaker") => Widget.Button({ - vpack: "center", - on_clicked: () => audio[type].is_muted = !audio[type].is_muted, - child: Widget.Icon({ - icon: audio[type].bind("icon_name") - .as(i => icon(i || "", icons.audio.mic.high)), - tooltipText: audio[type].bind("volume") - .as(vol => `Volume: ${Math.floor(vol * 100)}%`), - }), -}) - -const VolumeSlider = (type: Type = "speaker") => Widget.Slider({ - hexpand: true, - draw_value: false, - on_change: ({ value, dragging }) => { - if (dragging) { - audio[type].volume = value - audio[type].is_muted = false - } - }, - value: audio[type].bind("volume"), - class_name: audio[type].bind("is_muted").as(m => m ? "muted" : ""), -}) - -export const Volume = () => Widget.Box({ - class_name: "volume", - children: [ - VolumeIndicator("speaker"), - VolumeSlider("speaker"), - Widget.Box({ - vpack: "center", - child: Arrow("sink-selector"), - }), - Widget.Box({ - vpack: "center", - child: Arrow("app-mixer"), - visible: audio.bind("apps").as(a => a.length > 0), - }), - ], -}) - -export const Microphone = () => Widget.Box({ - class_name: "slider horizontal", - visible: audio.bind("recorders").as(a => a.length > 0), - children: [ - VolumeIndicator("microphone"), - VolumeSlider("microphone"), - ], -}) - -const MixerItem = (stream: Stream) => Widget.Box( - { - hexpand: true, - class_name: "mixer-item horizontal", - }, - Widget.Icon({ - tooltip_text: stream.bind("name").as(n => n || ""), - icon: stream.bind("name").as(n => { - return Utils.lookUpIcon(n || "") - ? (n || "") - : icons.fallback.audio - }), - }), - Widget.Box( - { vertical: true }, - Widget.Label({ - xalign: 0, - truncate: "end", - max_width_chars: 28, - label: stream.bind("description").as(d => d || ""), - }), - Widget.Slider({ - hexpand: true, - draw_value: false, - value: stream.bind("volume"), - on_change: ({ value }) => stream.volume = value, - }), - ), -) - -const SinkItem = (stream: Stream) => Widget.Button({ - hexpand: true, - on_clicked: () => audio.speaker = stream, - child: Widget.Box({ - children: [ - Widget.Icon({ - icon: icon(stream.icon_name || "", icons.fallback.audio), - tooltip_text: stream.icon_name || "", - }), - Widget.Label((stream.description || "").split(" ").slice(0, 4).join(" ")), - Widget.Icon({ - icon: icons.ui.tick, - hexpand: true, - hpack: "end", - visible: audio.speaker.bind("stream").as(s => s === stream.stream), - }), - ], - }), -}) - -const SettingsButton = () => Widget.Button({ - on_clicked: () => { - if (dependencies("pavucontrol")) - sh("pavucontrol") - }, - hexpand: true, - child: Widget.Box({ - children: [ - Widget.Icon(icons.ui.settings), - Widget.Label("Settings"), - ], - }), -}) - -export const AppMixer = () => Menu({ - name: "app-mixer", - icon: icons.audio.mixer, - title: "App Mixer", - content: [ - Widget.Box({ - vertical: true, - class_name: "vertical mixer-item-box", - children: audio.bind("apps").as(a => a.map(MixerItem)), - }), - Widget.Separator(), - SettingsButton(), - ], -}) - -export const SinkSelector = () => Menu({ - name: "sink-selector", - icon: icons.audio.type.headset, - title: "Sink Selector", - content: [ - Widget.Box({ - vertical: true, - children: audio.bind("speakers").as(a => a.map(SinkItem)), - }), - Widget.Separator(), - SettingsButton(), - ], -}) diff --git a/roles/ags/files/agsv2/widget/settings/Group.ts b/roles/ags/files/agsv2/widget/settings/Group.ts deleted file mode 100644 index e9356e0..0000000 --- a/roles/ags/files/agsv2/widget/settings/Group.ts +++ /dev/null @@ -1,34 +0,0 @@ -import icons from "lib/icons" -import Row from "./Row" - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export default (title: string, ...rows: ReturnType>[]) => Widget.Box( - { - class_name: "group", - vertical: true, - }, - Widget.Box([ - Widget.Label({ - hpack: "start", - vpack: "end", - class_name: "group-title", - label: title, - setup: w => Utils.idle(() => w.visible = !!title), - }), - title ? Widget.Button({ - hexpand: true, - hpack: "end", - child: Widget.Icon(icons.ui.refresh), - class_name: "group-reset", - sensitive: Utils.merge( - rows.map(({ attribute: { opt } }) => opt.bind().as(v => v !== opt.initial)), - (...values) => values.some(b => b), - ), - on_clicked: () => rows.forEach(row => row.attribute.opt.reset()), - }) : Widget.Box(), - ]), - Widget.Box({ - vertical: true, - children: rows, - }), -) diff --git a/roles/ags/files/agsv2/widget/settings/Page.ts b/roles/ags/files/agsv2/widget/settings/Page.ts deleted file mode 100644 index 220e560..0000000 --- a/roles/ags/files/agsv2/widget/settings/Page.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Group from "./Group" - -export default ( - name: string, - icon: string, - ...groups: ReturnType>[] -) => Widget.Box({ - class_name: "page", - attribute: { name, icon }, - child: Widget.Scrollable({ - css: "min-height: 300px;", - child: Widget.Box({ - class_name: "page-content", - vexpand: true, - vertical: true, - children: groups, - }), - }), -}) diff --git a/roles/ags/files/agsv2/widget/settings/Row.ts b/roles/ags/files/agsv2/widget/settings/Row.ts deleted file mode 100644 index 1e17096..0000000 --- a/roles/ags/files/agsv2/widget/settings/Row.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Opt } from "lib/option" -import Setter from "./Setter" -import icons from "lib/icons" - -export type RowProps = { - opt: Opt - title: string - note?: string - type?: - | "number" - | "color" - | "float" - | "object" - | "string" - | "enum" - | "boolean" - | "img" - | "font" - enums?: string[] - max?: number - min?: number -} - -export default (props: RowProps) => Widget.Box( - { - attribute: { opt: props.opt }, - class_name: "row", - tooltip_text: props.note ? `note: ${props.note}` : "", - }, - Widget.Box( - { vertical: true, vpack: "center" }, - Widget.Label({ - xalign: 0, - class_name: "row-title", - label: props.title, - }), - Widget.Label({ - xalign: 0, - class_name: "id", - label: props.opt.id, - }), - ), - Widget.Box({ hexpand: true }), - Widget.Box( - { vpack: "center" }, - Setter(props), - ), - Widget.Button({ - vpack: "center", - class_name: "reset", - child: Widget.Icon(icons.ui.refresh), - on_clicked: () => props.opt.reset(), - sensitive: props.opt.bind().as(v => v !== props.opt.initial), - }), -) diff --git a/roles/ags/files/agsv2/widget/settings/Setter.ts b/roles/ags/files/agsv2/widget/settings/Setter.ts deleted file mode 100644 index 7e455c9..0000000 --- a/roles/ags/files/agsv2/widget/settings/Setter.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { type RowProps } from "./Row" -import { Opt } from "lib/option" -import icons from "lib/icons" -import Gdk from "gi://Gdk" - -function EnumSetter(opt: Opt, values: string[]) { - const lbl = Widget.Label({ label: opt.bind().as(v => `${v}`) }) - const step = (dir: 1 | -1) => { - const i = values.findIndex(i => i === lbl.label) - opt.setValue(dir > 0 - ? i + dir > values.length - 1 ? values[0] : values[i + dir] - : i + dir < 0 ? values[values.length - 1] : values[i + dir], - ) - } - const next = Widget.Button({ - child: Widget.Icon(icons.ui.arrow.right), - on_clicked: () => step(+1), - }) - const prev = Widget.Button({ - child: Widget.Icon(icons.ui.arrow.left), - on_clicked: () => step(-1), - }) - return Widget.Box({ - class_name: "enum-setter", - children: [lbl, prev, next], - }) -} - -export default function Setter({ - opt, - type = typeof opt.value as RowProps["type"], - enums, - max = 1000, - min = 0, -}: RowProps) { - switch (type) { - case "number": return Widget.SpinButton({ - setup(self) { - self.set_range(min, max) - self.set_increments(1, 5) - self.on("value-changed", () => opt.value = self.value as T) - self.hook(opt, () => self.value = opt.value as number) - }, - }) - - case "float": - case "object": return Widget.Entry({ - on_accept: self => opt.value = JSON.parse(self.text || ""), - setup: self => self.hook(opt, () => self.text = JSON.stringify(opt.value)), - }) - - case "string": return Widget.Entry({ - on_accept: self => opt.value = self.text as T, - setup: self => self.hook(opt, () => self.text = opt.value as string), - }) - - case "enum": return EnumSetter(opt as unknown as Opt, enums!) - case "boolean": return Widget.Switch() - .on("notify::active", self => opt.value = self.active as T) - .hook(opt, self => self.active = opt.value as boolean) - - case "img": return Widget.FileChooserButton({ - on_file_set: ({ uri }) => { opt.value = uri!.replace("file://", "") as T }, - }) - - case "font": return Widget.FontButton({ - show_size: false, - use_size: false, - setup: self => self - .hook(opt, () => self.font = opt.value as string) - .on("font-set", ({ font }) => opt.value = font! - .split(" ").slice(0, -1).join(" ") as T), - }) - - case "color": return Widget.ColorButton() - .hook(opt, self => { - const rgba = new Gdk.RGBA() - rgba.parse(opt.value as string) - self.rgba = rgba - }) - .on("color-set", ({ rgba: { red, green, blue } }) => { - const hex = (n: number) => { - const c = Math.floor(255 * n).toString(16) - return c.length === 1 ? `0${c}` : c - } - opt.value = `#${hex(red)}${hex(green)}${hex(blue)}` as T - }) - - default: return Widget.Label({ - label: `no setter with type ${type}`, - }) - } -} diff --git a/roles/ags/files/agsv2/widget/settings/SettingsDialog.ts b/roles/ags/files/agsv2/widget/settings/SettingsDialog.ts deleted file mode 100644 index d81fe4d..0000000 --- a/roles/ags/files/agsv2/widget/settings/SettingsDialog.ts +++ /dev/null @@ -1,71 +0,0 @@ -import RegularWindow from "widget/RegularWindow" -import layout from "./layout" -import icons from "lib/icons" -import options from "options" - -const current = Variable(layout[0].attribute.name) - -const Header = () => Widget.Box({ - class_name: "header", - children: [ - Widget.Button({ - class_name: "reset", - on_clicked: options.reset, - hpack: "start", - vpack: "start", - child: Widget.Icon(icons.ui.refresh), - tooltip_text: "Reset", - }), - ], -}) - -const Sidebar = () => Widget.Box({ - class_name: "sidebar", - vertical: true, - children: layout.map(({ attribute: { name, icon } }) => Widget.Button({ - xalign: 0, - class_name: current.bind().as(v => `${v === name ? "active" : ""}`), - on_clicked: () => current.value = name, - child: Widget.Box([ - Widget.Icon(icon), - Widget.Label(name), - ]), - })), -}) - -const PagesStack = () => Widget.Stack({ - transition: "slide_left_right", - children: layout.reduce((obj, page) => ({ ...obj, [page.attribute.name]: page }), {}), - shown: current.bind() as never, -}) - -export default () => RegularWindow({ - name: "settings-dialog", - class_name: "settings-dialog", - title: "Settings", - setup(win) { - win.on("delete-event", () => { - win.hide() - return true - }) - win.set_default_size(800, 600) - }, - child: Widget.Box({ - vertical: true, - children: [ - Header(), - Widget.Box({ - vertical: false, - children: [ - Sidebar(), - Widget.Box({ - vertical: true, - children: [ - PagesStack(), - ], - }), - ], - }), - ], - }), -}) diff --git a/roles/ags/files/agsv2/widget/settings/Wallpaper.ts b/roles/ags/files/agsv2/widget/settings/Wallpaper.ts deleted file mode 100644 index 998f3b7..0000000 --- a/roles/ags/files/agsv2/widget/settings/Wallpaper.ts +++ /dev/null @@ -1,31 +0,0 @@ -import wallpaper from "service/wallpaper" - -export default () => Widget.Box( - { class_name: "row wallpaper" }, - Widget.Box( - { vertical: true }, - Widget.Label({ - xalign: 0, - class_name: "row-title", - label: "Wallpaper", - vpack: "start", - }), - Widget.Button({ - on_clicked: wallpaper.random, - label: "Random", - }), - Widget.FileChooserButton({ - on_file_set: ({ uri }) => wallpaper.set(uri!.replace("file://", "")), - }), - ), - Widget.Box({ hexpand: true }), - Widget.Box({ - class_name: "preview", - css: wallpaper.bind("wallpaper").as(wp => ` - min-height: 120px; - min-width: 200px; - background-image: url('${wp}'); - background-size: cover; - `), - }), -) diff --git a/roles/ags/files/agsv2/widget/settings/layout.ts b/roles/ags/files/agsv2/widget/settings/layout.ts deleted file mode 100644 index ddd9dd5..0000000 --- a/roles/ags/files/agsv2/widget/settings/layout.ts +++ /dev/null @@ -1,149 +0,0 @@ -/* eslint-disable max-len */ -import Row from "./Row" -import Group from "./Group" -import Page from "./Page" -import Wallpaper from "./Wallpaper" -import options from "options" -import icons from "lib/icons" - -const { - autotheme: at, - font, - theme, - bar: b, - launcher: l, - overview: ov, - powermenu: pm, - quicksettings: qs, - osd, - hyprland: h, -} = options - -const { - dark, - light, - blur, - scheme, - padding, - spacing, - radius, - shadows, - widget, - border, -} = theme - -export default [ - Page("Theme", icons.ui.themes, - Group("", - Wallpaper() as ReturnType, - Row({ opt: at, title: "Auto Generate Color Scheme" }), - Row({ opt: scheme, title: "Color Scheme", type: "enum", enums: ["dark", "light"] }), - ), - Group("Dark Colors", - Row({ opt: dark.bg, title: "Background", type: "color" }), - Row({ opt: dark.fg, title: "Foreground", type: "color" }), - Row({ opt: dark.primary.bg, title: "Primary", type: "color" }), - Row({ opt: dark.primary.fg, title: "On Primary", type: "color" }), - Row({ opt: dark.error.bg, title: "Error", type: "color" }), - Row({ opt: dark.error.fg, title: "On Error", type: "color" }), - Row({ opt: dark.widget, title: "Widget", type: "color" }), - Row({ opt: dark.border, title: "Border", type: "color" }), - ), - Group("Light Colors", - Row({ opt: light.bg, title: "Background", type: "color" }), - Row({ opt: light.fg, title: "Foreground", type: "color" }), - Row({ opt: light.primary.bg, title: "Primary", type: "color" }), - Row({ opt: light.primary.fg, title: "On Primary", type: "color" }), - Row({ opt: light.error.bg, title: "Error", type: "color" }), - Row({ opt: light.error.fg, title: "On Error", type: "color" }), - Row({ opt: light.widget, title: "Widget", type: "color" }), - Row({ opt: light.border, title: "Border", type: "color" }), - ), - Group("Theme", - Row({ opt: shadows, title: "Shadows" }), - Row({ opt: widget.opacity, title: "Widget Opacity", max: 100 }), - Row({ opt: border.opacity, title: "Border Opacity", max: 100 }), - Row({ opt: border.width, title: "Border Width" }), - Row({ opt: blur, title: "Blur", note: "0 to disable", max: 70 }), - ), - Group("UI", - Row({ opt: padding, title: "Padding" }), - Row({ opt: spacing, title: "Spacing" }), - Row({ opt: radius, title: "Roundness" }), - Row({ opt: font.size, title: "Font Size" }), - Row({ opt: font.name, title: "Font Name", type: "font" }), - ), - ), - Page("Bar", icons.ui.toolbars, - Group("General", - Row({ opt: b.transparent, title: "Transparent Bar", note: "Works best on empty-ish wallpapers" }), - Row({ opt: b.flatButtons, title: "Flat Buttons" }), - Row({ opt: b.position, title: "Position", type: "enum", enums: ["top", "bottom"] }), - Row({ opt: b.corners, title: "Corners" }), - ), - Group("Launcher", - Row({ opt: b.launcher.icon.icon, title: "Icon" }), - Row({ opt: b.launcher.icon.colored, title: "Colored Icon" }), - Row({ opt: b.launcher.label.label, title: "Label" }), - Row({ opt: b.launcher.label.colored, title: "Colored Label" }), - ), - Group("Workspaces", - Row({ opt: b.workspaces.workspaces, title: "Number of Workspaces", note: "0 to make it dynamic" }), - ), - Group("Taskbar", - Row({ opt: b.taskbar.iconSize, title: "Icon Size" }), - Row({ opt: b.taskbar.monochrome, title: "Monochrome" }), - Row({ opt: b.taskbar.exclusive, title: "Exclusive to workspaces" }), - ), - Group("Date", - Row({ opt: b.date.format, title: "Date Format" }), - ), - Group("Media", - Row({ opt: b.media.monochrome, title: "Monochrome" }), - Row({ opt: b.media.preferred, title: "Preferred Player" }), - Row({ opt: b.media.direction, title: "Slide Direction", type: "enum", enums: ["left", "right"] }), - Row({ opt: b.media.format, title: "Format of the Label" }), - Row({ opt: b.media.length, title: "Max Length of Label" }), - ), - Group("Battery", - Row({ opt: b.battery.bar, title: "Style", type: "enum", enums: ["hidden", "regular", "whole"] }), - Row({ opt: b.battery.blocks, title: "Number of Blocks" }), - Row({ opt: b.battery.width, title: "Width of Bar" }), - Row({ opt: b.battery.charging, title: "Charging Color", type: "color" }), - ), - Group("Powermenu", - Row({ opt: b.powermenu.monochrome, title: "Monochrome" }), - ), - ), - Page("General", icons.ui.settings, - Group("Hyprland", - Row({ opt: h.gapsWhenOnly, title: "Gaps When Only" }), - Row({ opt: h.inactiveBorder, type: "color", title: "Inactive Border Color" }), - ), - Group("Launcher", - Row({ opt: l.width, title: "Width" }), - Row({ opt: l.apps.iconSize, title: "Icon Size" }), - Row({ opt: l.apps.max, title: "Max Items" }), - ), - Group("Overview", - Row({ opt: ov.scale, title: "Scale", max: 100 }), - Row({ opt: ov.workspaces, title: "Workspaces", max: 11, note: "set this to 0 to make it dynamic" }), - Row({ opt: ov.monochromeIcon, title: "Monochrome Icons" }), - ), - Group("Powermenu", - Row({ opt: pm.layout, title: "Layout", type: "enum", enums: ["box", "line"] }), - Row({ opt: pm.labels, title: "Show Labels" }), - ), - Group("Quicksettings", - Row({ opt: qs.avatar.image, title: "Avatar", type: "img" }), - Row({ opt: qs.avatar.size, title: "Avatar Size" }), - Row({ opt: qs.media.monochromeIcon, title: "Media Monochrome Icons" }), - Row({ opt: qs.media.coverSize, title: "Media Cover Art Size" }), - ), - Group("On Screen Indicator", - Row({ opt: osd.progress.vertical, title: "Vertical" }), - Row({ opt: osd.progress.pack.h, title: "Horizontal Alignment", type: "enum", enums: ["start", "center", "end"] }), - Row({ opt: osd.progress.pack.v, title: "Vertical Alignment", type: "enum", enums: ["start", "center", "end"] }), - ), - ), -] as const