From c5b1f643769c4e0afaa0298c9cbbe126dee0934d Mon Sep 17 00:00:00 2001 From: Janis Hutz Date: Fri, 21 Mar 2025 16:16:56 +0100 Subject: [PATCH] Add ags stuff, but will probably redo entirely --- config/general/ags/.gitignore | 2 + config/general/ags/app.ts | 12 ++ config/general/ags/bar/Bar.tsx | 31 +++++ config/general/ags/env.d.ts | 21 +++ .../general/ags/launcher/handler.ts | 0 config/general/ags/launcher/launcher.scss | 0 config/general/ags/launcher/launcher.tsx | 0 .../notifications-preset/Notification.scss | 125 ++++++++++++++++++ .../ags/notifications-preset/Notification.tsx | 107 +++++++++++++++ .../NotificationPopups.tsx | 105 +++++++++++++++ config/general/ags/notifications/handler.ts | 20 +++ .../ags/notifications/notifications.scss | 0 .../ags/notifications/notifications.tsx | 0 config/general/ags/osd/OSD.scss | 30 +++++ config/general/ags/osd/OSD.tsx | 71 ++++++++++ config/general/ags/osd/brightness.ts | 47 +++++++ config/general/ags/package.json | 6 + config/general/ags/quickActions/handler.ts | 0 .../ags/quickActions/quickActions.scss | 0 .../general/ags/quickActions/quickActions.tsx | 0 config/general/ags/style.scss | 23 ++++ config/general/ags/tsconfig.json | 14 ++ config/general/ags/widgets/button.tsx | 0 install | 2 + scripts/build.js | 0 setup | 3 + 26 files changed, 619 insertions(+) create mode 100644 config/general/ags/.gitignore create mode 100644 config/general/ags/app.ts create mode 100644 config/general/ags/bar/Bar.tsx create mode 100644 config/general/ags/env.d.ts rename scripts/compile.js => config/general/ags/launcher/handler.ts (100%) create mode 100644 config/general/ags/launcher/launcher.scss create mode 100644 config/general/ags/launcher/launcher.tsx create mode 100644 config/general/ags/notifications-preset/Notification.scss create mode 100644 config/general/ags/notifications-preset/Notification.tsx create mode 100644 config/general/ags/notifications-preset/NotificationPopups.tsx create mode 100644 config/general/ags/notifications/handler.ts create mode 100644 config/general/ags/notifications/notifications.scss create mode 100644 config/general/ags/notifications/notifications.tsx create mode 100644 config/general/ags/osd/OSD.scss create mode 100644 config/general/ags/osd/OSD.tsx create mode 100644 config/general/ags/osd/brightness.ts create mode 100644 config/general/ags/package.json create mode 100644 config/general/ags/quickActions/handler.ts create mode 100644 config/general/ags/quickActions/quickActions.scss create mode 100644 config/general/ags/quickActions/quickActions.tsx create mode 100644 config/general/ags/style.scss create mode 100644 config/general/ags/tsconfig.json create mode 100644 config/general/ags/widgets/button.tsx create mode 100644 scripts/build.js diff --git a/config/general/ags/.gitignore b/config/general/ags/.gitignore new file mode 100644 index 0000000..298eb4d --- /dev/null +++ b/config/general/ags/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +@girs/ diff --git a/config/general/ags/app.ts b/config/general/ags/app.ts new file mode 100644 index 0000000..7a5e816 --- /dev/null +++ b/config/general/ags/app.ts @@ -0,0 +1,12 @@ +import { App } from "astal/gtk3" +import style from "./style.scss" +import Bar from "./bar/Bar" +import OSD from "./osd/OSD" + +App.start({ + css: style, + main() { + App.get_monitors().map(Bar); + OSD(App.get_monitors()[0]); + }, +}) diff --git a/config/general/ags/bar/Bar.tsx b/config/general/ags/bar/Bar.tsx new file mode 100644 index 0000000..0384535 --- /dev/null +++ b/config/general/ags/bar/Bar.tsx @@ -0,0 +1,31 @@ +import { App, Astal, Gtk, Gdk } from "astal/gtk3" +import { Variable } from "astal" + +const time = Variable("").poll(1000, "date") + +export default function Bar(gdkmonitor: Gdk.Monitor) { + const { TOP, LEFT, RIGHT } = Astal.WindowAnchor + + return + + + + + + +} diff --git a/config/general/ags/env.d.ts b/config/general/ags/env.d.ts new file mode 100644 index 0000000..467c0a4 --- /dev/null +++ b/config/general/ags/env.d.ts @@ -0,0 +1,21 @@ +declare const SRC: string + +declare module "inline:*" { + const content: string + export default content +} + +declare module "*.scss" { + const content: string + export default content +} + +declare module "*.blp" { + const content: string + export default content +} + +declare module "*.css" { + const content: string + export default content +} diff --git a/scripts/compile.js b/config/general/ags/launcher/handler.ts similarity index 100% rename from scripts/compile.js rename to config/general/ags/launcher/handler.ts diff --git a/config/general/ags/launcher/launcher.scss b/config/general/ags/launcher/launcher.scss new file mode 100644 index 0000000..e69de29 diff --git a/config/general/ags/launcher/launcher.tsx b/config/general/ags/launcher/launcher.tsx new file mode 100644 index 0000000..e69de29 diff --git a/config/general/ags/notifications-preset/Notification.scss b/config/general/ags/notifications-preset/Notification.scss new file mode 100644 index 0000000..a32f08b --- /dev/null +++ b/config/general/ags/notifications-preset/Notification.scss @@ -0,0 +1,125 @@ +@use "sass:string"; + +@function gtkalpha($c, $a) { + @return string.unquote("alpha(#{$c},#{$a})"); +} + +// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss +$fg-color: #{"@theme_fg_color"}; +$bg-color: #{"@theme_bg_color"}; +$error: red; + +window.NotificationPopups { + all: unset; +} + +eventbox.Notification { + + &:first-child>box { + margin-top: 1rem; + } + + &:last-child>box { + margin-bottom: 1rem; + } + + // eventboxes can not take margins so we style its inner box instead + >box { + min-width: 400px; + border-radius: 13px; + background-color: $bg-color; + margin: .5rem 1rem .5rem 1rem; + box-shadow: 2px 3px 8px 0 gtkalpha(black, .4); + border: 1pt solid gtkalpha($fg-color, .03); + } + + &.critical>box { + border: 1pt solid gtkalpha($error, .4); + + .header { + + .app-name { + color: gtkalpha($error, .8); + + } + + .app-icon { + color: gtkalpha($error, .6); + } + } + } + + .header { + padding: .5rem; + color: gtkalpha($fg-color, 0.5); + + .app-icon { + margin: 0 .4rem; + } + + .app-name { + margin-right: .3rem; + font-weight: bold; + + &:first-child { + margin-left: .4rem; + } + } + + .time { + margin: 0 .4rem; + } + + button { + padding: .2rem; + min-width: 0; + min-height: 0; + } + } + + separator { + margin: 0 .4rem; + background-color: gtkalpha($fg-color, .1); + } + + .content { + margin: 1rem; + margin-top: .5rem; + + .summary { + font-size: 1.2em; + color: $fg-color; + } + + .body { + color: gtkalpha($fg-color, 0.8); + } + + .image { + border: 1px solid gtkalpha($fg-color, .02); + margin-right: .5rem; + border-radius: 9px; + min-width: 100px; + min-height: 100px; + background-size: cover; + background-position: center; + } + } + + .actions { + margin: 1rem; + margin-top: 0; + + button { + margin: 0 .3rem; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + } +} diff --git a/config/general/ags/notifications-preset/Notification.tsx b/config/general/ags/notifications-preset/Notification.tsx new file mode 100644 index 0000000..5149d5b --- /dev/null +++ b/config/general/ags/notifications-preset/Notification.tsx @@ -0,0 +1,107 @@ +import { GLib } from "astal" +import { Gtk, Astal } from "astal/gtk3" +import { type EventBox } from "astal/gtk3/widget" +import Notifd from "gi://AstalNotifd" + +const isIcon = (icon: string) => + !!Astal.Icon.lookup_icon(icon) + +const fileExists = (path: string) => + GLib.file_test(path, GLib.FileTest.EXISTS) + +const time = (time: number, format = "%H:%M") => GLib.DateTime + .new_from_unix_local(time) + .format(format)! + +const urgency = (n: Notifd.Notification) => { + const { LOW, NORMAL, CRITICAL } = Notifd.Urgency + // match operator when? + switch (n.urgency) { + case LOW: return "low" + case CRITICAL: return "critical" + case NORMAL: + default: return "normal" + } +} + +type Props = { + setup(self: EventBox): void + onHoverLost(self: EventBox): void + notification: Notifd.Notification +} + +export default function Notification(props: Props) { + const { notification: n, onHoverLost, setup } = props + const { START, CENTER, END } = Gtk.Align + + return + + + {(n.appIcon || n.desktopEntry) && } + + + + {n.image && fileExists(n.image) && } + {n.image && isIcon(n.image) && + + } + + + + {n.get_actions().length > 0 && + {n.get_actions().map(({ label, id }) => ( + + ))} + } + + +} diff --git a/config/general/ags/notifications-preset/NotificationPopups.tsx b/config/general/ags/notifications-preset/NotificationPopups.tsx new file mode 100644 index 0000000..13fdd88 --- /dev/null +++ b/config/general/ags/notifications-preset/NotificationPopups.tsx @@ -0,0 +1,105 @@ +import { Astal, Gtk, Gdk } from "astal/gtk3" +import Notifd from "gi://AstalNotifd" +import Notification from "./Notification" +import { type Subscribable } from "astal/binding" +import { Variable, bind, timeout } from "astal" + +// see comment below in constructor +const TIMEOUT_DELAY = 5000 + +// The purpose if this class is to replace Variable> +// with a Map type in order to track notification widgets +// by their id, while making it conviniently bindable as an array +class NotifiationMap implements Subscribable { + // the underlying map to keep track of id widget pairs + private map: Map = new Map() + + // it makes sense to use a Variable under the hood and use its + // reactivity implementation instead of keeping track of subscribers ourselves + private var: Variable> = Variable([]) + + // notify subscribers to rerender when state changes + private notifiy() { + this.var.set([...this.map.values()].reverse()) + } + + constructor() { + const notifd = Notifd.get_default() + + /** + * uncomment this if you want to + * ignore timeout by senders and enforce our own timeout + * note that if the notification has any actions + * they might not work, since the sender already treats them as resolved + */ + // notifd.ignoreTimeout = true + + notifd.connect("notified", (_, id) => { + this.set(id, Notification({ + notification: notifd.get_notification(id)!, + + // once hovering over the notification is done + // destroy the widget without calling notification.dismiss() + // so that it acts as a "popup" and we can still display it + // in a notification center like widget + // but clicking on the close button will close it + onHoverLost: () => this.delete(id), + + // notifd by default does not close notifications + // until user input or the timeout specified by sender + // which we set to ignore above + setup: () => timeout(TIMEOUT_DELAY, () => { + /** + * uncomment this if you want to "hide" the notifications + * after TIMEOUT_DELAY + */ + // this.delete(id) + }) + })) + }) + + // notifications can be closed by the outside before + // any user input, which have to be handled too + notifd.connect("resolved", (_, id) => { + this.delete(id) + }) + } + + private set(key: number, value: Gtk.Widget) { + // in case of replacecment destroy previous widget + this.map.get(key)?.destroy() + this.map.set(key, value) + this.notifiy() + } + + private delete(key: number) { + this.map.get(key)?.destroy() + this.map.delete(key) + this.notifiy() + } + + // needed by the Subscribable interface + get() { + return this.var.get() + } + + // needed by the Subscribable interface + subscribe(callback: (list: Array) => void) { + return this.var.subscribe(callback) + } +} + +export default function NotificationPopups(gdkmonitor: Gdk.Monitor) { + const { TOP, RIGHT } = Astal.WindowAnchor + const notifs = new NotifiationMap() + + return + + {bind(notifs)} + + +} diff --git a/config/general/ags/notifications/handler.ts b/config/general/ags/notifications/handler.ts new file mode 100644 index 0000000..46ac4f5 --- /dev/null +++ b/config/general/ags/notifications/handler.ts @@ -0,0 +1,20 @@ +/* +* dotfiles - handler.ts +* +* Created by Janis Hutz 03/21/2025, Licensed under the GPL V3 License +* https://janishutz.com, development@janishutz.com +* +* +*/ + +// Handle incoming notifications and keep a list that can be consumed by +// other parts of the astal setup + +import Notifd from "gi://AstalNotifd"; +const notifd = Notifd.get_default(); +const notifications: Notifd.Notification[] = []; + +notifd.connect( 'notified', ( _, id ) => { + +} ); + diff --git a/config/general/ags/notifications/notifications.scss b/config/general/ags/notifications/notifications.scss new file mode 100644 index 0000000..e69de29 diff --git a/config/general/ags/notifications/notifications.tsx b/config/general/ags/notifications/notifications.tsx new file mode 100644 index 0000000..e69de29 diff --git a/config/general/ags/osd/OSD.scss b/config/general/ags/osd/OSD.scss new file mode 100644 index 0000000..d0fe4d1 --- /dev/null +++ b/config/general/ags/osd/OSD.scss @@ -0,0 +1,30 @@ +$fg-color: #{"@theme_fg_color"}; +$bg-color: #{"@theme_bg_color"}; + +window.OSD { + box.OSD { + border-radius: 100px; + background-color: $bg-color; + padding: 13px 16px; + margin: 13px; + box-shadow: 3px 3px 7px 0 rgba(0,0,0,.4); + } + + icon { + font-size: 4rem; + } + + label { + font-size: 2.4rem; + } + + levelbar { + trough { + margin: 1 .6rem; + } + + block { + min-height: 2rem; + } + } +} diff --git a/config/general/ags/osd/OSD.tsx b/config/general/ags/osd/OSD.tsx new file mode 100644 index 0000000..fbbd9ee --- /dev/null +++ b/config/general/ags/osd/OSD.tsx @@ -0,0 +1,71 @@ +// From examples on astal github page, since it looks good by default + +import { App, Astal, Gdk, Gtk } from "astal/gtk3" +import { timeout } from "astal/time" +import Variable from "astal/variable" +import Brightness from "./brightness" +import Wp from "gi://AstalWp" + +function OnScreenProgress({ visible }: { visible: Variable }) { + const brightness = Brightness.get_default() + const speaker = Wp.get_default()!.get_default_speaker() + + const iconName = Variable("") + const value = Variable(0) + + let count = 0 + function show(v: number, icon: string) { + visible.set(true) + value.set(v) + iconName.set(icon) + count++ + timeout(2000, () => { + count-- + if (count === 0) visible.set(false) + }) + } + + return ( + { + self.hook(brightness, "notify::screen", () => + show(brightness.screen, "display-brightness-symbolic"), + ) + + if (speaker) { + self.hook(speaker, "notify::volume", () => + show(speaker.volume, speaker.volumeIcon), + ) + } + }} + revealChild={visible()} + transitionType={Gtk.RevealerTransitionType.SLIDE_UP} + > + + + + + + ) +} + +export default function OSD(monitor: Gdk.Monitor) { + const visible = Variable(false) + + return ( + + visible.set(false)}> + + + + ) +} diff --git a/config/general/ags/osd/brightness.ts b/config/general/ags/osd/brightness.ts new file mode 100644 index 0000000..6e37c7e --- /dev/null +++ b/config/general/ags/osd/brightness.ts @@ -0,0 +1,47 @@ +// From examples on Astal GitHub + +import GObject, { register, property } from "astal/gobject" +import { monitorFile, readFileAsync } from "astal/file" +import { exec, execAsync } from "astal/process" + +const get = (args: string) => Number(exec(`brightnessctl ${args}`)) +const screen = exec(`bash -c "ls -w1 /sys/class/backlight | head -1"`) + +@register({ GTypeName: "Brightness" }) +export default class Brightness extends GObject.Object { + static instance: Brightness + static get_default() { + if (!this.instance) + this.instance = new Brightness() + + return this.instance + } + + #screenMax = get("max") + #screen = get("get") / (get("max") || 1) + + @property(Number) + get screen() { return this.#screen } + + set screen(percent) { + if (percent < 0) + percent = 0 + + if (percent > 1) + percent = 1 + + execAsync(`brightnessctl set ${Math.floor(percent * 100)}% -q`).then(() => { + this.#screen = percent + this.notify("screen") + }) + } + + constructor() { + super() + monitorFile(`/sys/class/backlight/${screen}/brightness`, async f => { + const v = await readFileAsync(f) + this.#screen = Number(v) / this.#screenMax + this.notify("screen") + }) + } +} diff --git a/config/general/ags/package.json b/config/general/ags/package.json new file mode 100644 index 0000000..44226f2 --- /dev/null +++ b/config/general/ags/package.json @@ -0,0 +1,6 @@ +{ + "name": "astal-shell", + "dependencies": { + "astal": "/usr/share/astal/gjs" + } +} diff --git a/config/general/ags/quickActions/handler.ts b/config/general/ags/quickActions/handler.ts new file mode 100644 index 0000000..e69de29 diff --git a/config/general/ags/quickActions/quickActions.scss b/config/general/ags/quickActions/quickActions.scss new file mode 100644 index 0000000..e69de29 diff --git a/config/general/ags/quickActions/quickActions.tsx b/config/general/ags/quickActions/quickActions.tsx new file mode 100644 index 0000000..e69de29 diff --git a/config/general/ags/style.scss b/config/general/ags/style.scss new file mode 100644 index 0000000..0b4c58f --- /dev/null +++ b/config/general/ags/style.scss @@ -0,0 +1,23 @@ +// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss +$fg-color: #{"@theme_fg_color"}; +$bg-color: #{"@theme_bg_color"}; + +// Load stylesheet for notifications +@use "./osd/OSD.scss"; + +window.Bar { + background: transparent; + color: $fg-color; + font-weight: bold; + + >centerbox { + background: $bg-color; + border-radius: 10px; + margin: 8px; + } + + button { + border-radius: 8px; + margin: 2px; + } +} diff --git a/config/general/ags/tsconfig.json b/config/general/ags/tsconfig.json new file mode 100644 index 0000000..9471e35 --- /dev/null +++ b/config/general/ags/tsconfig.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "experimentalDecorators": true, + "strict": true, + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "Bundler", + // "checkJs": true, + // "allowJs": true, + "jsx": "react-jsx", + "jsxImportSource": "astal/gtk3", + } +} diff --git a/config/general/ags/widgets/button.tsx b/config/general/ags/widgets/button.tsx new file mode 100644 index 0000000..e69de29 diff --git a/install b/install index 4895251..f277d62 100755 --- a/install +++ b/install @@ -2,3 +2,5 @@ # Read platform to install on (only if no platform file present in ~/.config/) read -p "Choose the configs to install, Laptop or Desktop (l/D): " platform + +# Packages to be added to install: aylurs-gtk-shell-git, brightnessctl diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..e69de29 diff --git a/setup b/setup index 13f4793..993d789 100755 --- a/setup +++ b/setup @@ -1,2 +1,5 @@ #!/bin/sh +# Read platform to install on (only if no platform file present in ~/.config/) +read -p "Choose the configs to install, Laptop or Desktop (l/D): " platform +