Format and import changes
This commit is contained in:
		
							parent
							
								
									e4646fc2d8
								
							
						
					
					
						commit
						a22aeed71a
					
				| 
						 | 
					@ -1,30 +1,31 @@
 | 
				
			||||||
import { Astal, Gdk } from "ags/gtk4";
 | 
					import { Astal, Gdk, Gtk } from "ags/gtk4";
 | 
				
			||||||
import app from "ags/gtk4/app"
 | 
					import app from "ags/gtk4/app";
 | 
				
			||||||
import Gtk from "gi://Gtk?version=4.0"
 | 
					import { createBinding, createState, For, With, Accessor } from "ags";
 | 
				
			||||||
import { createBinding, createState, For, With, Accessor } from "ags"
 | 
					import { createPoll } from "ags/time";
 | 
				
			||||||
import { createPoll } from "ags/time"
 | 
					 | 
				
			||||||
import Tray from "gi://AstalTray";
 | 
					import Tray from "gi://AstalTray";
 | 
				
			||||||
import { execAsync } from "ags/process"
 | 
					import { execAsync } from "ags/process";
 | 
				
			||||||
import Hyprland from "gi://AstalHyprland";
 | 
					import Hyprland from "gi://AstalHyprland";
 | 
				
			||||||
import { getIconName } from "./utils";
 | 
					import { getIconName } from "./utils";
 | 
				
			||||||
import Wp from "gi://AstalWp"
 | 
					import Wp from "gi://AstalWp";
 | 
				
			||||||
import Battery from "gi://AstalBattery"
 | 
					import Battery from "gi://AstalBattery";
 | 
				
			||||||
import GLib from "gi://GLib";
 | 
					import GLib from "gi://GLib";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const battery = Battery.get_default()
 | 
					const battery = Battery.get_default();
 | 
				
			||||||
const sensorsAvailable = await execAsync(['sensors']).then(() => true).catch(() => false);
 | 
					const sensorsAvailable = await execAsync(["sensors"])
 | 
				
			||||||
 | 
					    .then(() => true)
 | 
				
			||||||
 | 
					    .catch(() => false);
 | 
				
			||||||
const wirePlumber = Wp.get_default();
 | 
					const wirePlumber = Wp.get_default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function SysTray(): JSX.Element {
 | 
					function SysTray(): JSX.Element {
 | 
				
			||||||
    const tray = Tray.get_default();
 | 
					    const tray = Tray.get_default();
 | 
				
			||||||
    let items = createBinding(tray, "items");
 | 
					    let items = createBinding(tray, "items");
 | 
				
			||||||
    const init = (btn: Gtk.MenuButton, item: Tray.TrayItem) => {
 | 
					    const init = (btn: Gtk.MenuButton, item: Tray.TrayItem) => {
 | 
				
			||||||
        btn.menuModel = item.menuModel
 | 
					        btn.menuModel = item.menuModel;
 | 
				
			||||||
        btn.insert_action_group("dbusmenu", item.actionGroup)
 | 
					        btn.insert_action_group("dbusmenu", item.actionGroup);
 | 
				
			||||||
        item.connect("notify::action-group", () => {
 | 
					        item.connect("notify::action-group", () => {
 | 
				
			||||||
            btn.insert_action_group("dbusmenu", item.actionGroup)
 | 
					            btn.insert_action_group("dbusmenu", item.actionGroup);
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <box>
 | 
					        <box>
 | 
				
			||||||
            <For each={items}>
 | 
					            <For each={items}>
 | 
				
			||||||
| 
						 | 
					@ -35,7 +36,8 @@ function SysTray(): JSX.Element {
 | 
				
			||||||
                            $={(self) => init(self, item)}
 | 
					                            $={(self) => init(self, item)}
 | 
				
			||||||
                            class="systray"
 | 
					                            class="systray"
 | 
				
			||||||
                            tooltipMarkup={createBinding(item, "tooltipMarkup")}
 | 
					                            tooltipMarkup={createBinding(item, "tooltipMarkup")}
 | 
				
			||||||
                            menuModel={createBinding(item, "menuModel")}>
 | 
					                            menuModel={createBinding(item, "menuModel")}
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
                            <image gicon={item.gicon} class="systray-item" />
 | 
					                            <image gicon={item.gicon} class="systray-item" />
 | 
				
			||||||
                        </menubutton>
 | 
					                        </menubutton>
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
| 
						 | 
					@ -62,74 +64,74 @@ function Center(): JSX.Element {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Date({ format = "%Y-%m-%d" }): JSX.Element {
 | 
					function Date({ format = "%Y-%m-%d" }): JSX.Element {
 | 
				
			||||||
    const time = createPoll<string>("", 60000, () => GLib.DateTime.new_now_local().format(format)!)
 | 
					    const time = createPoll<string>(
 | 
				
			||||||
    return <button
 | 
					        "",
 | 
				
			||||||
        class="item"
 | 
					        60000,
 | 
				
			||||||
        label={time}
 | 
					        () => GLib.DateTime.new_now_local().format(format)!,
 | 
				
			||||||
        onClicked={() => execAsync(['gnome-calendar'])}
 | 
					    );
 | 
				
			||||||
    />
 | 
					    return (
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					            class="item"
 | 
				
			||||||
 | 
					            label={time}
 | 
				
			||||||
 | 
					            onClicked={() => execAsync(["gnome-calendar"])}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Time({ format = "%H:%M:%S" }): JSX.Element {
 | 
					function Time({ format = "%H:%M:%S" }): JSX.Element {
 | 
				
			||||||
    const time = createPoll<string>("", 1000, () => GLib.DateTime.new_now_local().format(format)!)
 | 
					    const time = createPoll<string>(
 | 
				
			||||||
    return <label
 | 
					        "",
 | 
				
			||||||
        class="item blue"
 | 
					        1000,
 | 
				
			||||||
        label={time}
 | 
					        () => GLib.DateTime.new_now_local().format(format)!,
 | 
				
			||||||
    />
 | 
					    );
 | 
				
			||||||
 | 
					    return <label class="item blue" label={time} />;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Temp(): JSX.Element {
 | 
					function Temp(): JSX.Element {
 | 
				
			||||||
    let [label, _setlabel] = createState<string>("N/A");
 | 
					    let [label, _setlabel] = createState<string>("N/A");
 | 
				
			||||||
    if (sensorsAvailable) {
 | 
					    if (sensorsAvailable) {
 | 
				
			||||||
        label = createPoll<string>("", 3000, 'sensors', out => {
 | 
					        label = createPoll<string>("", 3000, "sensors", (out) => {
 | 
				
			||||||
            const match = out.split('\n').find(line => line.includes('Tctl') || line.includes('Package'))?.match(/[0-9.]*°C/);
 | 
					            const match = out
 | 
				
			||||||
 | 
					                .split("\n")
 | 
				
			||||||
 | 
					                .find((line) => line.includes("Tctl") || line.includes("Package"))
 | 
				
			||||||
 | 
					                ?.match(/[0-9.]*°C/);
 | 
				
			||||||
            return match ? match[0] : "N/A";
 | 
					            return match ? match[0] : "N/A";
 | 
				
			||||||
        })
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return <label
 | 
					    return <label class="item blue" label={label} />;
 | 
				
			||||||
        class="item blue"
 | 
					 | 
				
			||||||
        label={label}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Memory(): JSX.Element {
 | 
					function Memory(): JSX.Element {
 | 
				
			||||||
    const memory = createPoll<string>("", 2000, "free", out => {
 | 
					    const memory = createPoll<string>("", 2000, "free", (out) => {
 | 
				
			||||||
        const line = out.split('\n').find(line => line.includes('Mem:'));
 | 
					        const line = out.split("\n").find((line) => line.includes("Mem:"));
 | 
				
			||||||
        if (!line) return "N/A";
 | 
					        if (!line) return "N/A";
 | 
				
			||||||
        const split = line.split(/\s+/).map(Number);
 | 
					        const split = line.split(/\s+/).map(Number);
 | 
				
			||||||
        return (split[2] / 1000000).toFixed(2) + "GB";
 | 
					        return (split[2] / 1000000).toFixed(2) + "GB";
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return <label
 | 
					    return <label class="item blue" label={memory} />;
 | 
				
			||||||
        class="item blue"
 | 
					 | 
				
			||||||
        label={memory}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function ClockSpeed(): JSX.Element {
 | 
					function ClockSpeed(): JSX.Element {
 | 
				
			||||||
    const command = 'bash -c "cat /proc/cpuinfo | grep \\"MHz\\" | awk \'{print \\$4}\' | sort -n | tail -1 | awk \'{printf \\"%.2fGHz\\", \\$1/1000}\'"';
 | 
					    const command =
 | 
				
			||||||
    const speed = createPoll<string>("", 1000, command, out => out)
 | 
					        'bash -c "cat /proc/cpuinfo | grep \\"MHz\\" | awk \'{print \\$4}\' | sort -n | tail -1 | awk \'{printf \\"%.2fGHz\\", \\$1/1000}\'"';
 | 
				
			||||||
    return <label
 | 
					    const speed = createPoll<string>("", 1000, command, (out) => out);
 | 
				
			||||||
        class="item"
 | 
					    return <label class="item" label={speed} />;
 | 
				
			||||||
        label={speed}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function CPU(): JSX.Element {
 | 
					function CPU(): JSX.Element {
 | 
				
			||||||
    const usage = createPoll<string>("", 2000, "top -b -n 1", out => {
 | 
					    const usage = createPoll<string>("", 2000, "top -b -n 1", (out) => {
 | 
				
			||||||
        const line = out.split("\n").find(line => line.includes('Cpu(s)'));
 | 
					        const line = out.split("\n").find((line) => line.includes("Cpu(s)"));
 | 
				
			||||||
        if (!line) return "N/A";
 | 
					        if (!line) return "N/A";
 | 
				
			||||||
        return line.split(/\s+/)[1].replace(',', '.').toString() + "%";
 | 
					        return line.split(/\s+/)[1].replace(",", ".").toString() + "%";
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    return <box class="item">
 | 
					    return (
 | 
				
			||||||
        <image iconName="speedometer" css="margin-right: 0.7em;" />
 | 
					        <box class="item">
 | 
				
			||||||
        <label
 | 
					            <image iconName="speedometer" css="margin-right: 0.7em;" />
 | 
				
			||||||
            label={usage}
 | 
					            <label label={usage} />
 | 
				
			||||||
        />
 | 
					        </box>
 | 
				
			||||||
    </box>
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function Right() {
 | 
					function Right() {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <box class="right" hexpand halign={Gtk.Align.END} spacing={6}>
 | 
					        <box class="right" hexpand halign={Gtk.Align.END} spacing={6}>
 | 
				
			||||||
| 
						 | 
					@ -148,20 +150,34 @@ function Right() {
 | 
				
			||||||
function BatteryIcon(): JSX.Element {
 | 
					function BatteryIcon(): JSX.Element {
 | 
				
			||||||
    if (battery.get_state() == 0) return <box />;
 | 
					    if (battery.get_state() == 0) return <box />;
 | 
				
			||||||
    let batteryPercentage = createBinding(battery, "percentage");
 | 
					    let batteryPercentage = createBinding(battery, "percentage");
 | 
				
			||||||
    return <button class="battery-item" onClicked={() => execAsync(['gnome-power-statistics'])}>
 | 
					    return (
 | 
				
			||||||
        <box>
 | 
					        <button
 | 
				
			||||||
            <With value={batteryPercentage}>
 | 
					            class="battery-item"
 | 
				
			||||||
                {(percentage) => {
 | 
					            onClicked={() => execAsync(["gnome-power-statistics"])}
 | 
				
			||||||
                    const thresholds = [...Array(11).keys()].map(i => i * 10);
 | 
					        >
 | 
				
			||||||
                    const icon = thresholds.find(threshold => threshold >= percentage * 100)
 | 
					            <box>
 | 
				
			||||||
                    const charging_name = battery.percentage >= 0.99 ? "charged" : "charging"
 | 
					                <With value={batteryPercentage}>
 | 
				
			||||||
                    return <image
 | 
					                    {(percentage) => {
 | 
				
			||||||
                        iconName={battery.charging ? `battery-level-${icon}-${charging_name}-symbolic` : `battery-level-${icon}-symbolic`}
 | 
					                        const thresholds = [...Array(11).keys()].map((i) => i * 10);
 | 
				
			||||||
                    />
 | 
					                        const icon = thresholds.find(
 | 
				
			||||||
                }}
 | 
					                            (threshold) => threshold >= percentage * 100,
 | 
				
			||||||
            </With>
 | 
					                        );
 | 
				
			||||||
        </box>
 | 
					                        const charging_name =
 | 
				
			||||||
    </button>
 | 
					                            battery.percentage >= 0.99 ? "charged" : "charging";
 | 
				
			||||||
 | 
					                        return (
 | 
				
			||||||
 | 
					                            <image
 | 
				
			||||||
 | 
					                                iconName={
 | 
				
			||||||
 | 
					                                    battery.charging
 | 
				
			||||||
 | 
					                                        ? `battery-level-${icon}-${charging_name}-symbolic`
 | 
				
			||||||
 | 
					                                        : `battery-level-${icon}-symbolic`
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            />
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                </With>
 | 
				
			||||||
 | 
					            </box>
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Icons() {
 | 
					function Icons() {
 | 
				
			||||||
| 
						 | 
					@ -170,7 +186,7 @@ function Icons() {
 | 
				
			||||||
            <SysTray />
 | 
					            <SysTray />
 | 
				
			||||||
            <BatteryIcon />
 | 
					            <BatteryIcon />
 | 
				
			||||||
        </box>
 | 
					        </box>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Volume(): JSX.Element {
 | 
					function Volume(): JSX.Element {
 | 
				
			||||||
| 
						 | 
					@ -178,42 +194,51 @@ function Volume(): JSX.Element {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const audio = wirePlumber.audio;
 | 
					    const audio = wirePlumber.audio;
 | 
				
			||||||
    const icon = createBinding(audio.default_speaker, "volume").as((volume) => {
 | 
					    const icon = createBinding(audio.default_speaker, "volume").as((volume) => {
 | 
				
			||||||
        const vol = volume * 100
 | 
					        const vol = volume * 100;
 | 
				
			||||||
        const icon = [
 | 
					        const icon = [
 | 
				
			||||||
            [101, 'overamplified'],
 | 
					            [101, "overamplified"],
 | 
				
			||||||
            [67, 'high'],
 | 
					            [67, "high"],
 | 
				
			||||||
            [34, 'medium'],
 | 
					            [34, "medium"],
 | 
				
			||||||
            [1, 'low'],
 | 
					            [1, "low"],
 | 
				
			||||||
            [0, 'muted'],
 | 
					            [0, "muted"],
 | 
				
			||||||
        ].find(([threshold]) => Number(threshold) <= vol)?.[1]
 | 
					        ].find(([threshold]) => Number(threshold) <= vol)?.[1];
 | 
				
			||||||
        return `audio-volume-${icon}-symbolic`
 | 
					        return `audio-volume-${icon}-symbolic`;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    const css = createBinding(audio.default_speaker, "mute").as((mute) => {
 | 
					    const css = createBinding(audio.default_speaker, "mute").as((mute) => {
 | 
				
			||||||
        return mute ? "margin-left:0;" : "margin-left: 0.7em;"
 | 
					        return mute ? "margin-left:0;" : "margin-left: 0.7em;";
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    let volume = createBinding(audio.default_speaker, "volume");
 | 
					    let volume = createBinding(audio.default_speaker, "volume");
 | 
				
			||||||
    let mute = createBinding(audio.default_speaker, "mute");
 | 
					    let mute = createBinding(audio.default_speaker, "mute");
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <button class="item blue" onClicked={() => audio.default_speaker.mute = !audio.default_speaker.mute}>
 | 
					        <button
 | 
				
			||||||
 | 
					            class="item blue"
 | 
				
			||||||
 | 
					            onClicked={() =>
 | 
				
			||||||
 | 
					                (audio.default_speaker.mute = !audio.default_speaker.mute)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
            <box>
 | 
					            <box>
 | 
				
			||||||
                <image iconName={icon} />
 | 
					                <image iconName={icon} />
 | 
				
			||||||
                <With value={volume}>
 | 
					                <With value={volume}>
 | 
				
			||||||
                    {(vol) => <box>
 | 
					                    {(vol) => (
 | 
				
			||||||
                        <With value={mute}>
 | 
					                        <box>
 | 
				
			||||||
                            {(muted) => {
 | 
					                            <With value={mute}>
 | 
				
			||||||
                                return (
 | 
					                                {(muted) => {
 | 
				
			||||||
                                    <label label={muted ? "" : `${Math.floor(vol * 100)}%`} css={css} />
 | 
					                                    return (
 | 
				
			||||||
                                )
 | 
					                                        <label
 | 
				
			||||||
                            }}
 | 
					                                            label={muted ? "" : `${Math.floor(vol * 100)}%`}
 | 
				
			||||||
                        </With>
 | 
					                                            css={css}
 | 
				
			||||||
                    </box>}
 | 
					                                        />
 | 
				
			||||||
 | 
					                                    );
 | 
				
			||||||
 | 
					                                }}
 | 
				
			||||||
 | 
					                            </With>
 | 
				
			||||||
 | 
					                        </box>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
                </With>
 | 
					                </With>
 | 
				
			||||||
            </box>
 | 
					            </box>
 | 
				
			||||||
        </button>
 | 
					        </button>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
function Workspaces(): JSX.Element {
 | 
					function Workspaces(): JSX.Element {
 | 
				
			||||||
    const hyprland = Hyprland.get_default();
 | 
					    const hyprland = Hyprland.get_default();
 | 
				
			||||||
    let workspaces = createBinding(hyprland, "workspaces");
 | 
					    let workspaces = createBinding(hyprland, "workspaces");
 | 
				
			||||||
| 
						 | 
					@ -224,14 +249,24 @@ function Workspaces(): JSX.Element {
 | 
				
			||||||
                    <box>
 | 
					                    <box>
 | 
				
			||||||
                        <With value={createBinding(hyprland, "focusedMonitor")}>
 | 
					                        <With value={createBinding(hyprland, "focusedMonitor")}>
 | 
				
			||||||
                            {(fm: Hyprland.Monitor) => {
 | 
					                            {(fm: Hyprland.Monitor) => {
 | 
				
			||||||
                                let filtered_wss = new Accessor(() => wss.sort((a, b) => a.id - b.id)
 | 
					                                let filtered_wss = new Accessor(() =>
 | 
				
			||||||
                                    .filter(ws => ws && ws.get_monitor() && ws.get_monitor().get_id() === fm.get_id()))
 | 
					                                    wss
 | 
				
			||||||
 | 
					                                        .sort((a, b) => a.id - b.id)
 | 
				
			||||||
 | 
					                                        .filter(
 | 
				
			||||||
 | 
					                                            (ws) =>
 | 
				
			||||||
 | 
					                                                ws &&
 | 
				
			||||||
 | 
					                                                ws.get_monitor() &&
 | 
				
			||||||
 | 
					                                                ws.get_monitor().get_id() === fm.get_id(),
 | 
				
			||||||
 | 
					                                        ),
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
                                return (
 | 
					                                return (
 | 
				
			||||||
                                    <box>
 | 
					                                    <box>
 | 
				
			||||||
                                        <For each={filtered_wss}>
 | 
					                                        <For each={filtered_wss}>
 | 
				
			||||||
                                            {(ws: Hyprland.Workspace, _index) => (
 | 
					                                            {(ws: Hyprland.Workspace, _index) => (
 | 
				
			||||||
                                                <button
 | 
					                                                <button
 | 
				
			||||||
                                                    class={createBinding(hyprland, "focusedWorkspace").as((fw) => ws === fw ? "focused" : "",)}
 | 
					                                                    class={createBinding(hyprland, "focusedWorkspace").as(
 | 
				
			||||||
 | 
					                                                        (fw) => (ws === fw ? "focused" : ""),
 | 
				
			||||||
 | 
					                                                    )}
 | 
				
			||||||
                                                    onClicked={() => ws.focus()}
 | 
					                                                    onClicked={() => ws.focus()}
 | 
				
			||||||
                                                >
 | 
					                                                >
 | 
				
			||||||
                                                    {`${ws.id}`.slice(-1)}
 | 
					                                                    {`${ws.id}`.slice(-1)}
 | 
				
			||||||
| 
						 | 
					@ -239,7 +274,7 @@ function Workspaces(): JSX.Element {
 | 
				
			||||||
                                            )}
 | 
					                                            )}
 | 
				
			||||||
                                        </For>
 | 
					                                        </For>
 | 
				
			||||||
                                    </box>
 | 
					                                    </box>
 | 
				
			||||||
                                )
 | 
					                                );
 | 
				
			||||||
                            }}
 | 
					                            }}
 | 
				
			||||||
                        </With>
 | 
					                        </With>
 | 
				
			||||||
                    </box>
 | 
					                    </box>
 | 
				
			||||||
| 
						 | 
					@ -250,12 +285,12 @@ function Workspaces(): JSX.Element {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function shorten(title: string) {
 | 
					function shorten(title: string) {
 | 
				
			||||||
    return title.length > 40 ? title.slice(0, 20) + "..." : title
 | 
					    return title.length > 40 ? title.slice(0, 20) + "..." : title;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Clients(): JSX.Element {
 | 
					function Clients(): JSX.Element {
 | 
				
			||||||
    const hyprland = Hyprland.get_default();
 | 
					    const hyprland = Hyprland.get_default();
 | 
				
			||||||
    let clients = createBinding(hyprland, "clients")
 | 
					    let clients = createBinding(hyprland, "clients");
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <box>
 | 
					        <box>
 | 
				
			||||||
            <With value={createBinding(hyprland, "focusedWorkspace")}>
 | 
					            <With value={createBinding(hyprland, "focusedWorkspace")}>
 | 
				
			||||||
| 
						 | 
					@ -263,36 +298,44 @@ function Clients(): JSX.Element {
 | 
				
			||||||
                    <box class="clients">
 | 
					                    <box class="clients">
 | 
				
			||||||
                        <With value={clients}>
 | 
					                        <With value={clients}>
 | 
				
			||||||
                            {(cls: Array<Hyprland.Client>) => {
 | 
					                            {(cls: Array<Hyprland.Client>) => {
 | 
				
			||||||
                                let filtered_clients = new Accessor(() => cls
 | 
					                                let filtered_clients = new Accessor(() =>
 | 
				
			||||||
                                    .sort((a, b) => a.pid - b.pid)
 | 
					                                    cls
 | 
				
			||||||
                                    .filter(cl => !cl.title.includes("rofi"))
 | 
					                                        .sort((a, b) => a.pid - b.pid)
 | 
				
			||||||
                                    .filter(cl => fw && cl.get_workspace().get_id() === fw.get_id()))
 | 
					                                        .filter((cl) => !cl.title.includes("rofi"))
 | 
				
			||||||
 | 
					                                        .filter(
 | 
				
			||||||
 | 
					                                            (cl) => fw && cl.get_workspace().get_id() === fw.get_id(),
 | 
				
			||||||
 | 
					                                        ),
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                return (
 | 
					                                return (
 | 
				
			||||||
                                    <box>
 | 
					                                    <box>
 | 
				
			||||||
                                        <For each={filtered_clients}>
 | 
					                                        <For each={filtered_clients}>
 | 
				
			||||||
                                            {(cl: Hyprland.Client, _index) => (
 | 
					                                            {(cl: Hyprland.Client, _index) => (
 | 
				
			||||||
                                                <box
 | 
					                                                <box
 | 
				
			||||||
                                                    class={createBinding(hyprland, "focusedClient").as(a => a && a.address === cl.address ? "focused" : "unfocused")}
 | 
					                                                    class={createBinding(hyprland, "focusedClient").as(
 | 
				
			||||||
 | 
					                                                        (a) =>
 | 
				
			||||||
 | 
					                                                            a && a.address === cl.address
 | 
				
			||||||
 | 
					                                                                ? "focused"
 | 
				
			||||||
 | 
					                                                                : "unfocused",
 | 
				
			||||||
 | 
					                                                    )}
 | 
				
			||||||
                                                >
 | 
					                                                >
 | 
				
			||||||
                                                    <image
 | 
					                                                    <image iconName={getIconName(cl)} class="app-icon" />
 | 
				
			||||||
                                                        iconName={getIconName(cl)}
 | 
					                                                    <label
 | 
				
			||||||
                                                        class="app-icon"
 | 
					                                                        label={createBinding(cl, "title").as((title) =>
 | 
				
			||||||
 | 
					                                                            shorten(title),
 | 
				
			||||||
 | 
					                                                        )}
 | 
				
			||||||
                                                    />
 | 
					                                                    />
 | 
				
			||||||
                                                    <label label={createBinding(cl, 'title').as(title => shorten(title))} />
 | 
					 | 
				
			||||||
                                                </box>
 | 
					                                                </box>
 | 
				
			||||||
                                            )}
 | 
					                                            )}
 | 
				
			||||||
                                        </For>
 | 
					                                        </For>
 | 
				
			||||||
                                    </box>
 | 
					                                    </box>
 | 
				
			||||||
                                )
 | 
					                                );
 | 
				
			||||||
                            }
 | 
					                            }}
 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        </With>
 | 
					                        </With>
 | 
				
			||||||
                    </box>
 | 
					                    </box>
 | 
				
			||||||
                )
 | 
					                )}
 | 
				
			||||||
                }
 | 
					            </With>
 | 
				
			||||||
            </With >
 | 
					        </box>
 | 
				
			||||||
        </box >
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -304,15 +347,20 @@ export default function Bar(gdkmonitor: Gdk.Monitor, scaleFactor: number = 1) {
 | 
				
			||||||
            gdkmonitor={gdkmonitor}
 | 
					            gdkmonitor={gdkmonitor}
 | 
				
			||||||
            css={"font-size: " + scaleFactor + "em;"}
 | 
					            css={"font-size: " + scaleFactor + "em;"}
 | 
				
			||||||
            exclusivity={Astal.Exclusivity.EXCLUSIVE}
 | 
					            exclusivity={Astal.Exclusivity.EXCLUSIVE}
 | 
				
			||||||
            anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.RIGHT}
 | 
					            anchor={
 | 
				
			||||||
 | 
					                Astal.WindowAnchor.TOP |
 | 
				
			||||||
 | 
					                Astal.WindowAnchor.LEFT |
 | 
				
			||||||
 | 
					                Astal.WindowAnchor.RIGHT
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            application={app}
 | 
					            application={app}
 | 
				
			||||||
            class="Bar"
 | 
					            class="Bar"
 | 
				
			||||||
            name="top-bar" >
 | 
					            name="top-bar"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
            <centerbox class="window-box">
 | 
					            <centerbox class="window-box">
 | 
				
			||||||
                <Left $type="start" />
 | 
					                <Left $type="start" />
 | 
				
			||||||
                <Center $type="center" />
 | 
					                <Center $type="center" />
 | 
				
			||||||
                <Right $type="end" />
 | 
					                <Right $type="end" />
 | 
				
			||||||
            </centerbox>
 | 
					            </centerbox>
 | 
				
			||||||
        </window >
 | 
					        </window>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,35 +1,40 @@
 | 
				
			||||||
import app from "ags/gtk4/app"
 | 
					import app from "ags/gtk4/app";
 | 
				
			||||||
import style from "./style.scss"
 | 
					import style from "./style.scss";
 | 
				
			||||||
import Bar from "./Bar"
 | 
					import Bar from "./Bar";
 | 
				
			||||||
import Hyprland from "gi://AstalHyprland";
 | 
					import Hyprland from "gi://AstalHyprland";
 | 
				
			||||||
import NotificationPopups from "./notifications/NotificationPopups"
 | 
					import NotificationPopups from "./notifications/NotificationPopups";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const hyprland = Hyprland.get_default();
 | 
					const hyprland = Hyprland.get_default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function find_main_monitor(): Hyprland.Monitor {
 | 
					function find_main_monitor(): Hyprland.Monitor {
 | 
				
			||||||
    let monitors = hyprland.get_monitors()
 | 
					    let monitors = hyprland.get_monitors();
 | 
				
			||||||
    for (let j = 0; j < monitors.length; j++) {
 | 
					    for (let j = 0; j < monitors.length; j++) {
 | 
				
			||||||
        for (const monitor of ["eDP", "DP", "HDMI-A"]) {
 | 
					        for (const monitor of ["eDP", "DP", "HDMI-A"]) {
 | 
				
			||||||
            for (let i = 0; i < monitors.length; i++) {
 | 
					            for (let i = 0; i < monitors.length; i++) {
 | 
				
			||||||
                console.log("Checking monitor:", monitors[i].get_name(), "against", monitor + "-" + j)
 | 
					                console.log(
 | 
				
			||||||
 | 
					                    "Checking monitor:",
 | 
				
			||||||
 | 
					                    monitors[i].get_name(),
 | 
				
			||||||
 | 
					                    "against",
 | 
				
			||||||
 | 
					                    monitor + "-" + j,
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                if (monitors[i].get_name() == monitor + "-" + j) {
 | 
					                if (monitors[i].get_name() == monitor + "-" + j) {
 | 
				
			||||||
                    return monitors[i]
 | 
					                    return monitors[i];
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return monitors[0]
 | 
					    return monitors[0];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function register_windows(monitor: Hyprland.Monitor) {
 | 
					function register_windows(monitor: Hyprland.Monitor) {
 | 
				
			||||||
    let gtkMonitor = app.get_monitors()[0]
 | 
					    let gtkMonitor = app.get_monitors()[0];
 | 
				
			||||||
    let scale = (monitor.get_width() >= 3000) ? 1.2 : 1
 | 
					    let scale = monitor.get_width() >= 3000 ? 1.2 : 1;
 | 
				
			||||||
    Bar(gtkMonitor, scale)
 | 
					    Bar(gtkMonitor, scale);
 | 
				
			||||||
    NotificationPopups()
 | 
					    NotificationPopups();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function switch_to_best_monitor() {
 | 
					function switch_to_best_monitor() {
 | 
				
			||||||
    let mainMonitor = find_main_monitor()
 | 
					    let mainMonitor = find_main_monitor();
 | 
				
			||||||
    for (var wd of app.get_windows()) {
 | 
					    for (var wd of app.get_windows()) {
 | 
				
			||||||
        wd.destroy();
 | 
					        wd.destroy();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -37,17 +42,19 @@ function switch_to_best_monitor() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
hyprland.connect("monitor-added", (_, _monitor: Hyprland.Monitor) => {
 | 
					hyprland.connect("monitor-added", (_, _monitor: Hyprland.Monitor) => {
 | 
				
			||||||
    switch_to_best_monitor()
 | 
					    switch_to_best_monitor();
 | 
				
			||||||
})
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
hyprland.connect("monitor-removed", () => {
 | 
					hyprland.connect("monitor-removed", () => {
 | 
				
			||||||
    switch_to_best_monitor()
 | 
					    switch_to_best_monitor();
 | 
				
			||||||
})
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log("Trying to find the best monitor");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.start({
 | 
					app.start({
 | 
				
			||||||
    css: style,
 | 
					    css: style,
 | 
				
			||||||
    iconTheme: "Papirus",
 | 
					    iconTheme: "Papirus",
 | 
				
			||||||
    main() {
 | 
					    main() {
 | 
				
			||||||
        switch_to_best_monitor()
 | 
					        switch_to_best_monitor();
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
})
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,21 +1,21 @@
 | 
				
			||||||
declare const SRC: string
 | 
					declare const SRC: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare module "inline:*" {
 | 
					declare module "inline:*" {
 | 
				
			||||||
  const content: string
 | 
					    const content: string
 | 
				
			||||||
  export default content
 | 
					    export default content
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare module "*.scss" {
 | 
					declare module "*.scss" {
 | 
				
			||||||
  const content: string
 | 
					    const content: string
 | 
				
			||||||
  export default content
 | 
					    export default content
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare module "*.blp" {
 | 
					declare module "*.blp" {
 | 
				
			||||||
  const content: string
 | 
					    const content: string
 | 
				
			||||||
  export default content
 | 
					    export default content
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare module "*.css" {
 | 
					declare module "*.css" {
 | 
				
			||||||
  const content: string
 | 
					    const content: string
 | 
				
			||||||
  export default content
 | 
					    export default content
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,34 +1,33 @@
 | 
				
			||||||
import Gtk from "gi://Gtk?version=4.0"
 | 
					import { Gtk, Gdk } from "ags/gtk4";
 | 
				
			||||||
import Gdk from "gi://Gdk?version=4.0"
 | 
					import Adw from "gi://Adw";
 | 
				
			||||||
import Adw from "gi://Adw"
 | 
					import GLib from "gi://GLib";
 | 
				
			||||||
import GLib from "gi://GLib"
 | 
					import AstalNotifd from "gi://AstalNotifd";
 | 
				
			||||||
import AstalNotifd from "gi://AstalNotifd"
 | 
					import Pango from "gi://Pango";
 | 
				
			||||||
import Pango from "gi://Pango"
 | 
					import { timeout } from "ags/time";
 | 
				
			||||||
import { timeout } from "ags/time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function isIcon(icon?: string | null) {
 | 
					function isIcon(icon?: string | null) {
 | 
				
			||||||
    const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()!)
 | 
					    const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()!);
 | 
				
			||||||
    return icon && iconTheme.has_icon(icon)
 | 
					    return icon && iconTheme.has_icon(icon);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function fileExists(path: string) {
 | 
					function fileExists(path: string) {
 | 
				
			||||||
    return GLib.file_test(path, GLib.FileTest.EXISTS)
 | 
					    return GLib.file_test(path, GLib.FileTest.EXISTS);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function time(time: number, format = "%H:%M") {
 | 
					function time(time: number, format = "%H:%M") {
 | 
				
			||||||
    return GLib.DateTime.new_from_unix_local(time).format(format)!
 | 
					    return GLib.DateTime.new_from_unix_local(time).format(format)!;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function urgency(n: AstalNotifd.Notification) {
 | 
					function urgency(n: AstalNotifd.Notification) {
 | 
				
			||||||
    const { LOW, NORMAL, CRITICAL } = AstalNotifd.Urgency
 | 
					    const { LOW, NORMAL, CRITICAL } = AstalNotifd.Urgency;
 | 
				
			||||||
    switch (n.urgency) {
 | 
					    switch (n.urgency) {
 | 
				
			||||||
        case LOW:
 | 
					        case LOW:
 | 
				
			||||||
            return "low"
 | 
					            return "low";
 | 
				
			||||||
        case CRITICAL:
 | 
					        case CRITICAL:
 | 
				
			||||||
            return "critical"
 | 
					            return "critical";
 | 
				
			||||||
        case NORMAL:
 | 
					        case NORMAL:
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            return "normal"
 | 
					            return "normal";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,12 +35,12 @@ export default function Notification({
 | 
				
			||||||
    notification: n,
 | 
					    notification: n,
 | 
				
			||||||
    onHoverLost,
 | 
					    onHoverLost,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
    notification: AstalNotifd.Notification
 | 
					    notification: AstalNotifd.Notification;
 | 
				
			||||||
    onHoverLost: () => void
 | 
					    onHoverLost: () => void;
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
    const timer = timeout(3000, () => {
 | 
					    const timer = timeout(3000, () => {
 | 
				
			||||||
        onHoverLost()
 | 
					        onHoverLost();
 | 
				
			||||||
    })
 | 
					    });
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Adw.Clamp maximumSize={400}>
 | 
					        <Adw.Clamp maximumSize={400}>
 | 
				
			||||||
            <box
 | 
					            <box
 | 
				
			||||||
| 
						 | 
					@ -49,7 +48,10 @@ export default function Notification({
 | 
				
			||||||
                class={`Notification ${urgency(n)}`}
 | 
					                class={`Notification ${urgency(n)}`}
 | 
				
			||||||
                orientation={Gtk.Orientation.VERTICAL}
 | 
					                orientation={Gtk.Orientation.VERTICAL}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                <Gtk.EventControllerMotion onEnter={() => timer.cancel()} onLeave={onHoverLost} />
 | 
					                <Gtk.EventControllerMotion
 | 
				
			||||||
 | 
					                    onEnter={() => timer.cancel()}
 | 
				
			||||||
 | 
					                    onLeave={onHoverLost}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
                <box class="header">
 | 
					                <box class="header">
 | 
				
			||||||
                    {(n.appIcon || isIcon(n.desktopEntry)) && (
 | 
					                    {(n.appIcon || isIcon(n.desktopEntry)) && (
 | 
				
			||||||
                        <image
 | 
					                        <image
 | 
				
			||||||
| 
						 | 
					@ -120,5 +122,5 @@ export default function Notification({
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
            </box>
 | 
					            </box>
 | 
				
			||||||
        </Adw.Clamp>
 | 
					        </Adw.Clamp>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,39 +1,39 @@
 | 
				
			||||||
import app from "ags/gtk4/app"
 | 
					import app from "ags/gtk4/app";
 | 
				
			||||||
import { Astal, Gtk } from "ags/gtk4"
 | 
					import { Astal, Gtk } from "ags/gtk4";
 | 
				
			||||||
import AstalNotifd from "gi://AstalNotifd"
 | 
					import AstalNotifd from "gi://AstalNotifd";
 | 
				
			||||||
import Notification from "./Notification"
 | 
					import Notification from "./Notification";
 | 
				
			||||||
import { createBinding, For, createState, onCleanup } from "ags"
 | 
					import { createBinding, For, createState, onCleanup } from "ags";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function NotificationPopups() {
 | 
					export default function NotificationPopups() {
 | 
				
			||||||
    const monitors = createBinding(app, "monitors")
 | 
					    const monitors = createBinding(app, "monitors");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const notifd = AstalNotifd.get_default()
 | 
					    const notifd = AstalNotifd.get_default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [notifications, setNotifications] = createState(
 | 
					    const [notifications, setNotifications] = createState(
 | 
				
			||||||
        new Array<AstalNotifd.Notification>(),
 | 
					        new Array<AstalNotifd.Notification>(),
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const notifiedHandler = notifd.connect("notified", (_, id, replaced) => {
 | 
					    const notifiedHandler = notifd.connect("notified", (_, id, replaced) => {
 | 
				
			||||||
        const notification = notifd.get_notification(id)
 | 
					        const notification = notifd.get_notification(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (replaced && notifications.get().some(n => n.id === id)) {
 | 
					        if (replaced && notifications.get().some((n) => n.id === id)) {
 | 
				
			||||||
            setNotifications((ns) => ns.map((n) => (n.id === id ? notification : n)))
 | 
					            setNotifications((ns) => ns.map((n) => (n.id === id ? notification : n)));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            setNotifications((ns) => [notification, ...ns])
 | 
					            setNotifications((ns) => [notification, ...ns]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    })
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const resolvedHandler = notifd.connect("resolved", (_, id) => {
 | 
					    const resolvedHandler = notifd.connect("resolved", (_, id) => {
 | 
				
			||||||
        setNotifications((ns) => ns.filter((n) => n.id !== id))
 | 
					        setNotifications((ns) => ns.filter((n) => n.id !== id));
 | 
				
			||||||
    })
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // technically, we don't need to cleanup because in this example this is a root component
 | 
					    // technically, we don't need to cleanup because in this example this is a root component
 | 
				
			||||||
    // and this cleanup function is only called when the program exits, but exiting will cleanup either way
 | 
					    // and this cleanup function is only called when the program exits, but exiting will cleanup either way
 | 
				
			||||||
    // but it's here to remind you that you should not forget to cleanup signal connections
 | 
					    // but it's here to remind you that you should not forget to cleanup signal connections
 | 
				
			||||||
    onCleanup(() => {
 | 
					    onCleanup(() => {
 | 
				
			||||||
        notifd.disconnect(notifiedHandler)
 | 
					        notifd.disconnect(notifiedHandler);
 | 
				
			||||||
        notifd.disconnect(resolvedHandler)
 | 
					        notifd.disconnect(resolvedHandler);
 | 
				
			||||||
    })
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <For each={monitors} cleanup={(win) => (win as Gtk.Window).destroy()}>
 | 
					        <For each={monitors} cleanup={(win) => (win as Gtk.Window).destroy()}>
 | 
				
			||||||
| 
						 | 
					@ -61,5 +61,5 @@ export default function NotificationPopups() {
 | 
				
			||||||
                </window>
 | 
					                </window>
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
        </For>
 | 
					        </For>
 | 
				
			||||||
    )
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
    "ags": "*"
 | 
					        "ags": "*"
 | 
				
			||||||
  }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue