diff --git a/ags/app.ts b/ags/app.ts index 1e0c672..1c1cb43 100644 --- a/ags/app.ts +++ b/ags/app.ts @@ -1,14 +1,22 @@ import { App } from "astal/gtk3" -import { Bar } from "./window/Bar"; import { runStyleHandler } from "./scripts/style-handler"; -import "./scripts/reload-handler"; +//import { monitorPaths } from "./scripts/reload-handler"; // Only for debugging purposes(testing new widgets and stuff) +import { handleArguments } from "./scripts/arg-handler"; -runStyleHandler(); +export const astalInstanceName = "astal" App.start({ - instanceName: "astal", + instanceName: astalInstanceName || "astal", + requestHandler(request: string, res: (result: any) => void) { + console.log(`[LOG] Arguments received: ${request}`) + res(handleArguments(request)); + }, main() { - Bar(0); + console.log(`[LOG] Initialized astal instance as: ${ astalInstanceName || "astal" }`); + console.log(`[LOG] Running Stylesheet handler`); + runStyleHandler(); + //console.log(`[LOG] Starting to monitor scripts to automatically reload instance`); + //monitorPaths(); } }); diff --git a/ags/i18n/intl.ts b/ags/i18n/intl.ts new file mode 100644 index 0000000..0520444 --- /dev/null +++ b/ags/i18n/intl.ts @@ -0,0 +1,53 @@ +//TODO use I18n system >.< + +import { GLib } from "astal"; + +export const i18nKeys = { + "en_US": () => import("./lang/en_US"), + "pt_BR": () => import("./lang/pt_BR") +} + +export const languages: Array = (() => + Object.keys(i18nKeys))() + +const defaultLanguage: string = languages[0]; +let language: string = defaultLanguage; + +export function getSystemLanguage(): string { + const sysLanguage: (string|null|undefined) = GLib.getenv("LANG") || GLib.getenv("LANGUAGE"); + + if(!sysLanguage) { + console.log(`[WARNING] Couldn't get system language, fallback to default ${defaultLanguage || "en_US"}`); + console.log("[TIP] Please set the LANG or LANGUAGE environment variable"); + + return defaultLanguage || "en_US"; + } + + return sysLanguage.split('.')[0]; +} + +export function setLanguage(lang: keyof typeof i18nKeys): (string|Error) { + languages.map((cur: string) => { + if(cur === lang) { + language = lang; + return lang; + } + }); + + throw new Error(`[i18n/intl] Couldn't set language: ${lang}`, { + cause: `Language ${lang} not found in languages of type ${typeof languages}` + }); +} + +export function tr(key: string): (string|undefined) { + let langKeys: Object = i18nKeys[language as keyof typeof i18nKeys]; + let result = i18nKeys[language as keyof typeof i18nKeys], + defResult = i18nKeys[defaultLanguage as keyof typeof i18nKeys]; + + key.split('.').map((keyString: string) => { + result = result[keyString as keyof typeof result]; + defResult = defResult[keyString as keyof typeof defResult]; + }); + + return (result as never) || (defResult as never) || undefined; +} diff --git a/ags/i18n/lang/en_US.ts b/ags/i18n/lang/en_US.ts new file mode 100644 index 0000000..0042fc3 --- /dev/null +++ b/ags/i18n/lang/en_US.ts @@ -0,0 +1,3 @@ +export default { + "language": "English (United States)", +} diff --git a/ags/i18n/lang/pt_BR.ts b/ags/i18n/lang/pt_BR.ts new file mode 100644 index 0000000..6edb605 --- /dev/null +++ b/ags/i18n/lang/pt_BR.ts @@ -0,0 +1,3 @@ +export default { + "language": "Português (Brasil)" +} diff --git a/ags/scripts/apps.ts b/ags/scripts/apps.ts new file mode 100644 index 0000000..8b8d930 --- /dev/null +++ b/ags/scripts/apps.ts @@ -0,0 +1,30 @@ +import AstalApps from "gi://AstalApps"; + +const astalApps: AstalApps.Apps = new AstalApps.Apps(); +let appsList: Array = astalApps.get_list(); + +export function getApps(): Array { + return appsList; +} + +export function updateApps(): void { + astalApps.reload(); + appsList = astalApps.get_list(); +} + +export function getAppsByName(appName: string): (Array|undefined) { + let found: Array = []; + + getApps().map((app: AstalApps.Application) => { + if(app.get_name().trim().toLowerCase() === appName.trim().toLowerCase() + || (app?.wmClass && app.wmClass.trim().toLowerCase() === appName.trim().toLowerCase())) + found.push(app); + }); + + return (found.length > 0 ? found : undefined); +} + +export function getAppIcon(appName: string): (string|undefined) { + const found: (Array|undefined) = getAppsByName(appName); + return found ? found[0]?.iconName : undefined; +} diff --git a/ags/scripts/arg-handler.ts b/ags/scripts/arg-handler.ts new file mode 100644 index 0000000..e7370bb --- /dev/null +++ b/ags/scripts/arg-handler.ts @@ -0,0 +1,79 @@ +import { Windows } from "./windows"; +import { restartInstance } from "./reload-handler"; + +export function handleArguments(request: string): any { + const args: Array = request.split(" "); + switch(args[0]) { + case "open": + case "close": + case "toggle": + return handleWindowArgs(args); + + case "help": + case "h": + return getHelp(); // stop it, get some help + + case "reload": + restartInstance({ log: true, instanceName: "astal" }); + return "Reloading instance..." + + default: + return "command not found! try checking help"; + } +} + +// Didn't want to bloat the switch statement, so I just separated it into functions +export function handleWindowArgs(args: Array): string { + const windows = Windows.getDefault().getWindows(); + const window = windows[args[1] as never]; + + if(args[1] == undefined || args[1] == "") + return "Window argument not specified!"; + + if(!Object.hasOwn(windows, args[1]!)) + return `Window "${args[1]}" not found windows list!` + + switch(args[0]) { + case "open": + if(!Windows.getDefault().isVisible(window)) { + Windows.getDefault().open(window); + return `Setting visibility of window "${args[1]}" to true`; + } + + return `Window is already open, ignored`; + + case "close": + if(Windows.getDefault().isVisible(window)) { + Windows.getDefault().close(window); + return `Setting visibility of window "${args[1]}" to false` + } + + return `Window is already closed, ignored` + + case "toggle": + if(!Windows.getDefault().isVisible(window)) { + Windows.getDefault().open(window); + return `Toggle opening window "${args[1]}"`; + } + + Windows.getDefault().close(window); + return `Toggle closing window "${args[1]}"` + } + + return "Couldn't handle window management arguments" +} + +export function getHelp(): string { + return `Manage Astal Windows and do more stuff. From +retrozinndev's Hyprland Dots, using Astal and AGS by Aylur. + +Options: + open [window_name]: sets specified window's visibility to true. + close [window_name]: sets specified window's visibility to false. + toggle [window_name]: toggles visibility of specified window. + reload: creates a new astal instance and removes this one. + help, -h, --help: shows this help message. + +2024 (c) retrozinndev's Hyprland-Dots, licensed under the MIT License. +https://github.com/retrozinndev/Hyprland-Dots` +} diff --git a/ags/scripts/notification-handler.ts b/ags/scripts/notification-handler.ts new file mode 100644 index 0000000..1348495 --- /dev/null +++ b/ags/scripts/notification-handler.ts @@ -0,0 +1,67 @@ +import AstalNotifd from "gi://AstalNotifd"; +import { Windows } from "./windows"; +import { timeout } from "astal/time"; + +const notifd: AstalNotifd.Notifd = new AstalNotifd.Notifd({ + ignoreTimeout: false, + dontDisturb: false +}); + +const windows = Windows.getDefault(); + +export let notifications: Array = []; +export let notificationHistory: Array = []; + +notifd.connect("notified", (_source: AstalNotifd.Notifd, id: number, _replaced: boolean) => { + windows.isVisible(windows.getWindows().floating_notifications) && windows.open(windows.getWindows().floating_notifications); + addNotification(getNotifd().get_notification(id)); +}); + +function addNotification(notification: AstalNotifd.Notification) { + prependArray(notifications, getNotifd().get_notification(notification.id)); + + // default timeout if undefined + let notificationTimeout = 4000; + + switch(notification.urgency) { + case AstalNotifd.Urgency.LOW: + notificationTimeout = 2000; + break; + case AstalNotifd.Urgency.NORMAL: + notificationTimeout = 4000; + break; + } + + notification.urgency !== AstalNotifd.Urgency.CRITICAL && timeout(notificationTimeout, () => { + notificationTimeout--; + if(notificationTimeout === 0) { + removeNotification(notification.id); + addToNotificationHistory(notification); + }; + }); +} + +export function removeNotification(notificationId: number) { + notifications = notifications.filter((notification: AstalNotifd.Notification) => + notification.id !== notificationId); +} + +function addToNotificationHistory(notification: AstalNotifd.Notification) { + prependArray(notificationHistory, notification); +} + +export function removeFromNotificationHistory(notificationId: number) { + notifications = notifications.filter((curNotification: AstalNotifd.Notification) => + curNotification.id !== notificationId); +} + +function prependArray(array: Array, item: any) { + let tmpArray = array; + tmpArray.reverse(); + tmpArray.push(item); + array = tmpArray.reverse(); +} + +export function getNotifd(): AstalNotifd.Notifd { + return notifd; +} diff --git a/ags/scripts/reload-handler.ts b/ags/scripts/reload-handler.ts index 6c4fef6..1754223 100644 --- a/ags/scripts/reload-handler.ts +++ b/ags/scripts/reload-handler.ts @@ -1,17 +1,30 @@ import { monitorFile, Process } from "astal"; +import { astalInstanceName } from "../app"; +import { getUserDirs } from "./user"; -const monitoringPaths = [ "./scripts", "./widget", "./app.ts", "env.d.ts" ]; +const monitoringPaths = [ "./scripts", "./window", "./app.ts", "env.d.ts" ]; -function restartInstance(instanceName?: string) { - Process.exec_async(`ags run`, () => {}); - Process.exec_async(`astal -q ${ instanceName ? instanceName : "astal" }`, () => {}); +interface InstanceProps { + instanceName?: string; + log?: boolean; } -monitoringPaths.map((path: string) => { - monitorFile( - path, - () => { - restartInstance("astal"); - } +export function restartInstance(props: InstanceProps = { instanceName: "astal", log: false }): void { + Process.exec_async(`astal -q ${props.instanceName}`, () => {}); + Process.exec_async(`ags run ${ props.log && `--log-file + ${ getUserDirs().cache}/ags-${ astalInstanceName || "astal" }.log` }`.replaceAll('\n', ' ').trim(), + () => {} ) -}) +} + +export function monitorPaths(): void { + monitoringPaths.map((path: string) => { + monitorFile( + path, + () => restartInstance({ + instanceName: astalInstanceName || "astal", + log: true + }) + ) + }); +} diff --git a/ags/scripts/style-handler.ts b/ags/scripts/style-handler.ts index b7505bf..0f376ce 100644 --- a/ags/scripts/style-handler.ts +++ b/ags/scripts/style-handler.ts @@ -1,6 +1,6 @@ // handles reloading stylesheet and pywal colors -import { readFile, monitorFile, Process, Gio } from "astal"; +import { readFile, monitorFile, Process } from "astal"; import { App } from "astal/gtk3"; import { getUserDirs } from "./user"; diff --git a/ags/scripts/time.ts b/ags/scripts/time.ts new file mode 100644 index 0000000..9ca846b --- /dev/null +++ b/ags/scripts/time.ts @@ -0,0 +1,6 @@ +import { GLib, Variable } from "astal"; + +const time = new Variable(GLib.DateTime.new_now_local()).poll(600, () => + GLib.DateTime.new_now_local())(); + +export const getDateTime = () => time; diff --git a/ags/scripts/volume.ts b/ags/scripts/volume.ts new file mode 100644 index 0000000..411dca9 --- /dev/null +++ b/ags/scripts/volume.ts @@ -0,0 +1,68 @@ +import AstalWp from "gi://AstalWp"; + +export class Wireplumber { + private astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default(); + private defaultSink: AstalWp.Endpoint = this.astalWireplumber!.get_default_speaker()!; + private defaultSource: AstalWp.Endpoint = this.astalWireplumber!.get_default_microphone()!; + private static inst: Wireplumber = new Wireplumber(); + + private maxSinkVolume: number = 100; + private maxSourceVolume: number = 100; + + constructor() { + if(!this.astalWireplumber) + throw new Error("Audio features will not work correctly! Please install wireplumber first", { + cause: "Wireplumber library not found" + }); + } + + public static getDefault(): Wireplumber { + return Wireplumber.inst; + } + + public getDefaultSink(): AstalWp.Endpoint { + return this.defaultSink; + } + + public getDefaultSource(): AstalWp.Endpoint { + return this.defaultSource; + } + + public getSinkVolume(): number { + return this.getDefaultSink().get_volume() * 100; + } + + public getSourceVolume(): number { + return this.getDefaultSource().get_volume() * 100; + } + + public setSinkVolume(newSinkVolume: number) { + this.defaultSink.set_volume( + (newSinkVolume > this.maxSinkVolume ? this.maxSinkVolume : newSinkVolume) / 100 + ); + } + + public setSourceVolume(newSourceVolume: number) { + this.defaultSource.set_volume( + newSourceVolume > this.maxSourceVolume ? this.maxSourceVolume : newSourceVolume / 100 + ); + } + + public increaseSinkVolume(volumeIncrease: number) { + if(volumeIncrease > this.maxSinkVolume + || (this.maxSinkVolume + volumeIncrease) > this.maxSinkVolume) { + this.setSinkVolume(this.maxSinkVolume); + } + + this.setSinkVolume(this.getSinkVolume() + volumeIncrease); + } + + public increaseSourceVolume(volumeIncrease: number) { + if(volumeIncrease > this.maxSourceVolume //TODO + || (this.maxSinkVolume + volumeIncrease) > this.maxSinkVolume) { + this.setSinkVolume(this.maxSinkVolume); + } + + this.setSinkVolume(this.getSinkVolume() + volumeIncrease); + } +} diff --git a/ags/scripts/windows.ts b/ags/scripts/windows.ts new file mode 100644 index 0000000..f255e9b --- /dev/null +++ b/ags/scripts/windows.ts @@ -0,0 +1,40 @@ +// get open windows / interact with windows(e.g.: close, open or toggle) + +import { Widget } from "astal/gtk3"; +import { Bar } from "../window/Bar"; +import { OSD } from "../window/OSD"; +import { ControlCenter } from "../window/ControlCenter"; +//import { FloatingNotifications } from "../window/FloatingNotifications"; + +export class Windows { + private static inst: Windows = new Windows(); + + /* Windows List(js object): + * add all windows here */ + private readonly windows = { + "bar": Bar, + "osd": OSD, + "control-center": ControlCenter + //"floating-notifications": FloatingNotifications + }; + + public static getDefault(): Windows { + return Windows.inst; + } + + public getWindows(): typeof this.windows { + return this.windows; + } + + public open(window: Widget.Window): void { + window.show(); + } + + public isVisible(window: Widget.Window): boolean { + return window.get_visible(); + } + + public close(window: Widget.Window): void { + window.hide(); + } +} diff --git a/ags/style.scss b/ags/style.scss index acb4348..5ad53db 100644 --- a/ags/style.scss +++ b/ags/style.scss @@ -1,2 +1,4 @@ @use "./style/bar"; @use "./style/wal"; +@use "./style/osd"; +@use "./style/control-center" diff --git a/ags/style/_bar.scss b/ags/style/_bar.scss index 3c791ba..7f248d7 100644 --- a/ags/style/_bar.scss +++ b/ags/style/_bar.scss @@ -41,11 +41,15 @@ } } + // Style widget groups & > .bar-centerbox > * { background: rgba($color: wal.$background, $alpha: .6); - padding: 6px; + padding: 5px; border-radius: 18px; + // Style widgets + & > *, + & > * > button & > * { margin: 0 2px; @@ -68,17 +72,18 @@ .workspaces { padding: 2px 2px; + & button { all: unset; border-radius: 16px; transition: 80ms linear; - padding: 0 12px; + padding: 12px 12px; background: wal.$color1; margin: 1px 2px; &.focus { background: wal.$foreground; - padding: 0 20px; + padding: 12px 20px; } } } @@ -108,9 +113,8 @@ } .logo button { - $padding-inline: 12px; - padding-left: $padding-inline; - padding-right: calc($padding-inline + 3.9px); + padding: 0 11px; + padding-right: 16px; & label { font-size: 14px; @@ -128,6 +132,36 @@ font-size: 14px; } } + + &.reveal { + & .media > box { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + & .media-controls { + padding-left: 3px; + border-top-right-radius: 12px; + border-bottom-right-radius: 12px; + background: scale-color($color: wal.$color3, $lightness: -12%); + + & > button { + margin: 0px 1px; + border-radius: 4px; + + &:first-child { + border-top-left-radius: 12px; + border-bottom-left-radius: 12px; + margin-left: 0; + } + + &:last-child { + border-top-right-radius: 12px; + border-bottom-right-radius: 12px; + margin-right: 0; + } + } + } + } } .tray { diff --git a/ags/style/_control-center.scss b/ags/style/_control-center.scss new file mode 100644 index 0000000..0517e67 --- /dev/null +++ b/ags/style/_control-center.scss @@ -0,0 +1,54 @@ +@use "./wal"; + +.control-center-container { + all: unset; + background: rgba(wal.$background, .6); + border-top-left-radius: 16px; + border-bottom-left-radius: 16px; + margin: 32px 0; + + & * { + all: unset; + transition: 120ms linear; + } + + & { + & button { + padding: 4px 6px; + background: scale-color($color: wal.$color1, $lightness: -20%); + border-radius: 12px; + } + + & .quickactions { + padding: 10px 16px; + & .button-row { + & > button { + margin: 5px 2px; + border-radius: 2px; + padding: 0 8px; + background: rgba($color: scale-color($color: wal.$color1, $lightness: -20%), $alpha: .7); + + & label { + font-size: 16px; + } + + &:hover { + background: rgba($color: scale-color($color: wal.$color1, $lightness: -20%), $alpha: 1); + } + + &:first-child { + margin-left: 0; + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; + } + + &:last-child { + margin-right: 0; + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + } + } + } + } + } +} diff --git a/ags/style/_osd.scss b/ags/style/_osd.scss new file mode 100644 index 0000000..50f61a0 --- /dev/null +++ b/ags/style/_osd.scss @@ -0,0 +1,37 @@ +@use "./wal"; + +.osd-window { + all: unset; + + .osd { + margin-bottom: 100px; + background: rgba($color: wal.$background, $alpha: .5); + padding: 14px 16px; + border-radius: 20px; + + .icon { + margin-right: 14px; + font-size: 24px; + } + + .volume { + margin-top: -6px; + + .value { + margin-bottom: 5px; + } + + levelbar { + trough block { + border-radius: 6px; + background: wal.$background; + + &.filled { + padding: 3px 0; + background: wal.$color1; + } + } + } + } + } +} diff --git a/ags/style/_wal.scss b/ags/style/_wal.scss index e2dc560..c9e4351 100644 --- a/ags/style/_wal.scss +++ b/ags/style/_wal.scss @@ -1,26 +1,26 @@ // SCSS Variables // Generated by 'wal' -$wallpaper: "/home/joaov/wallpapers/Garden Kita.png"; +$wallpaper: "/home/joaov/wallpapers/Miku, Rin and Luka Chibi.jpg"; // Special -$background: #101212; -$foreground: #c3c3c3; -$cursor: #c3c3c3; +$background: #3d2217; +$foreground: #cec7c5; +$cursor: #cec7c5; // Colors -$color0: #101212; -$color1: #59662a; -$color2: #517047; -$color3: #87863c; -$color4: #707b48; -$color5: #4b6266; -$color6: #84876e; -$color7: #8e9898; -$color8: #596d6d; -$color9: #778839; -$color10: #6C965F; -$color11: #B4B350; -$color12: #96A460; -$color13: #658388; -$color14: #B0B493; -$color15: #c3c3c3; +$color0: #3d2217; +$color1: #b38678; +$color2: #a4998a; +$color3: #b39e8a; +$color4: #a5a09b; +$color5: #aea299; +$color6: #b4aea2; +$color7: #a39c99; +$color8: #7f6f68; +$color9: #EFB3A1; +$color10: #DBCCB9; +$color11: #EFD3B9; +$color12: #DDD6CF; +$color13: #E8D8CD; +$color14: #F1E8D9; +$color15: #cec7c5; diff --git a/ags/widget/Separator.tsx b/ags/widget/Separator.tsx index 2db9b87..8f5cbab 100644 --- a/ags/widget/Separator.tsx +++ b/ags/widget/Separator.tsx @@ -12,15 +12,15 @@ export function Separator(props: SeparatorProps) { return new Widget.Box({ className: `separator separator-${ props.orientation == Gtk.Orientation.VERTICAL ? "vertical" : "horizontal" } ${ props.class && props.class }`, css: `.separator { - background: ${ props.cssColor ? props.cssColor : "lightgray" }; - opacity: ${ props.alpha ? props.alpha : 1 }; + background: ${ props.cssColor || "lightgray" }; + opacity: ${ props.alpha || 1 }; } .separator-horizontal { - padding-right: ${props.size ? props.size : 1 }px; + padding-right: ${props.size || 1 }px; margin: 7px 4px; } .separator-vertical { - padding-bottom: ${props.size ? props.size : 1 }px; + padding-bottom: ${props.size || 1 }px; margin: 4px 7px; }`, } as Widget.BoxProps); diff --git a/ags/widget/bar/Audio.tsx b/ags/widget/bar/Audio.tsx index dd48074..e2934d6 100644 --- a/ags/widget/bar/Audio.tsx +++ b/ags/widget/bar/Audio.tsx @@ -19,7 +19,8 @@ export function Audio() { } as Widget.LabelProps), new Widget.Label({ className: "icon nf", - label: bind(wp!.defaultSpeaker, "volume").as((volume: number) => Math.round(volume * 100).toString() + "%") + label: bind(wp!.defaultSpeaker, "volume").as((volume: number) => + Math.round(volume * 100).toString() + "%") } as Widget.LabelProps) ] }) @@ -33,7 +34,8 @@ export function Audio() { label: "󰍬" } as Widget.LabelProps), new Widget.Label({ - label: bind(wp!.defaultMicrophone, "volume").as((volume: number) => Math.round(volume * 100).toString() + "%") + label: bind(wp!.defaultMicrophone, "volume").as((volume: number) => + Math.round(volume * 100).toString() + "%") } as Widget.LabelProps) ] }) diff --git a/ags/widget/bar/Clock.tsx b/ags/widget/bar/Clock.tsx index 5a8011d..18ae79d 100644 --- a/ags/widget/bar/Clock.tsx +++ b/ags/widget/bar/Clock.tsx @@ -1,16 +1,14 @@ -import { Box, Button } from "astal/gtk3/widget"; -import { GLib, Variable } from "astal"; import { Widget } from "astal/gtk3"; - -const dateTimeFormat = "%A %d, %H:%M" -const time = new Variable("").poll(600, () => - GLib.DateTime.new_now_local().format(dateTimeFormat)!); +import { getDateTime } from "../../scripts/time"; +import { GLib } from "astal"; export function Clock(): JSX.Element { return new Widget.Box({ className: "clock", child: new Widget.Button({ - label: time() + label: getDateTime().as((dateTime: GLib.DateTime) => { + return dateTime.format("%A %d, %H:%M") + }) } as Widget.ButtonProps) } as Widget.BoxProps); } diff --git a/ags/widget/bar/FocusedWindow.tsx b/ags/widget/bar/FocusedWindow.tsx index a72e67f..b6413e8 100644 --- a/ags/widget/bar/FocusedWindow.tsx +++ b/ags/widget/bar/FocusedWindow.tsx @@ -1,6 +1,7 @@ import { bind } from "astal"; import { Gtk, Widget } from "astal/gtk3"; import AstalHyprland from "gi://AstalHyprland"; +import { getAppIcon } from "../../scripts/apps"; const hyprland = AstalHyprland.get_default(); @@ -11,14 +12,8 @@ export function FocusedWindow() { children: [ new Widget.Icon({ className: "icon", - icon: bind(hyprland, "focusedClient").as(Boolean) && bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => { - switch(client.initialClass) { - case "zen": - return "zen-browser"; - - default: - return client.initialClass; - }}), + icon: bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => + getAppIcon(client.initialClass) || "image-missing"), iconSize: Gtk.IconSize.SMALL_TOOLBAR }), new Widget.Box({ @@ -29,12 +24,14 @@ export function FocusedWindow() { new Widget.Label({ className: "class", xalign: 0, - label: bind(hyprland, "focusedClient").as(Boolean) && bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => client.get_class()) + label: bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => + client?.["class"]) } as Widget.LabelProps), new Widget.Label({ className: "title", xalign: 0, - label: bind(hyprland, "focusedClient").as(Boolean) && bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => client.get_title()) + label: bind(hyprland, "focusedClient").as((client: AstalHyprland.Client) => + client?.["title"]) } as Widget.LabelProps) ] }) diff --git a/ags/widget/bar/Logo.tsx b/ags/widget/bar/Logo.tsx index f563620..83c67c7 100644 --- a/ags/widget/bar/Logo.tsx +++ b/ags/widget/bar/Logo.tsx @@ -1,10 +1,10 @@ import { Box, Button } from "astal/gtk3/widget"; -import { Process } from "astal"; +import AstalHyprland from "gi://AstalHyprland"; export function Logo() { return ( -