@@ -1,6 +1,6 @@
|
||||
import { App } from "astal/gtk3";
|
||||
import { monitorFile } from "astal/file";
|
||||
import { exec } from "astal/process";
|
||||
import app from "ags/gtk3/app";
|
||||
import { exec } from "ags/process";
|
||||
import { monitorFile } from "ags/file";
|
||||
import GLib from "gi://GLib";
|
||||
import Left from "./widget/Left";
|
||||
import Center from "./widget/Center";
|
||||
@@ -12,15 +12,15 @@ const scss = `${HOME}/.config/astal/theme.sass`;
|
||||
|
||||
monitorFile(scss, () => {
|
||||
exec(`sassc ${scss} ${css}`);
|
||||
App.apply_css(css, true);
|
||||
app.apply_css(css, true);
|
||||
});
|
||||
|
||||
exec(`sassc ${scss} ${css}`);
|
||||
|
||||
App.start({
|
||||
app.start({
|
||||
css,
|
||||
main() {
|
||||
App.get_monitors().map((monitor) => {
|
||||
app.get_monitors().map((monitor) => {
|
||||
Left(monitor);
|
||||
Center(monitor);
|
||||
Right(monitor);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { Gdk } from "astal/gtk3";
|
||||
import { Gdk } from "ags/gtk3";
|
||||
import Hyprland from "gi://AstalHyprland";
|
||||
|
||||
export const range = (length: number, start = 1) => {
|
||||
return Array.from({ length }, (n, i) => i + start);
|
||||
return Array.from({ length }, (_, i) => i + start);
|
||||
};
|
||||
|
||||
export const getHyprlandMonitor = (gdkmonitor: Gdk.Monitor) => {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"name": "astal-shell",
|
||||
"dependencies": {
|
||||
"astal": "~/.local/share/ags"
|
||||
"ags": "*"
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"strict": true,
|
||||
"target": "ES2022",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "ags/gtk3",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "Bundler",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "astal/gtk3"
|
||||
"strict": true,
|
||||
"target": "ES2020"
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,19 @@
|
||||
import { App, Astal, Gtk, Gdk } from "astal/gtk3";
|
||||
import { Astal, Gtk, Gdk } from "ags/gtk3";
|
||||
import Date from "./components/Date";
|
||||
import Hidden from "./components/Hidden";
|
||||
import app from "ags/gtk3/app";
|
||||
|
||||
export default (monitor: Gdk.Monitor) => (
|
||||
<window
|
||||
className="root"
|
||||
visible
|
||||
class="root"
|
||||
gdkmonitor={monitor}
|
||||
exclusivity={Astal.Exclusivity.IGNORE}
|
||||
anchor={Astal.WindowAnchor.TOP}
|
||||
application={App}
|
||||
application={app}
|
||||
>
|
||||
<Hidden>
|
||||
<box className="widgets" hexpand halign={Gtk.Align.CENTER}>
|
||||
<box class="widgets" hexpand halign={Gtk.Align.CENTER}>
|
||||
<Date />
|
||||
</box>
|
||||
</Hidden>
|
||||
|
@@ -1,21 +1,22 @@
|
||||
import { App, Astal, Gtk, Gdk } from "astal/gtk3";
|
||||
import { Astal, Gtk, Gdk } from "ags/gtk3";
|
||||
import app from "ags/gtk3/app";
|
||||
import Launcher from "./components/Launcher";
|
||||
import Workspace from "./components/Workspaces";
|
||||
import Hidden from "./components/Hidden";
|
||||
import { getHyprlandMonitor } from "../lib";
|
||||
|
||||
export default (monitor: Gdk.Monitor) => (
|
||||
<window
|
||||
className="root"
|
||||
visible
|
||||
class="root"
|
||||
gdkmonitor={monitor}
|
||||
exclusivity={Astal.Exclusivity.IGNORE}
|
||||
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT}
|
||||
application={App}
|
||||
application={app}
|
||||
>
|
||||
<Hidden>
|
||||
<box className="widgets" hexpand halign={Gtk.Align.START}>
|
||||
<box class="widgets" hexpand halign={Gtk.Align.START}>
|
||||
<Launcher />
|
||||
<Workspace monitor={getHyprlandMonitor(monitor)!} />
|
||||
<Workspace gdkmonitor={monitor} />
|
||||
</box>
|
||||
</Hidden>
|
||||
</window>
|
||||
|
@@ -1,18 +1,21 @@
|
||||
import { App, Astal, Gtk, Gdk } from "astal/gtk3";
|
||||
import { Astal, Gtk } from "ags/gtk3";
|
||||
import app from "ags/gtk3/app";
|
||||
import Gdk from "gi://Gdk";
|
||||
import Systray from "./components/Tray";
|
||||
import Hidden from "./components/Hidden";
|
||||
import Battery from "./components/Battery";
|
||||
|
||||
export default (monitor: Gdk.Monitor) => (
|
||||
<window
|
||||
className="root"
|
||||
visible
|
||||
class="root"
|
||||
gdkmonitor={monitor}
|
||||
exclusivity={Astal.Exclusivity.IGNORE}
|
||||
anchor={Astal.WindowAnchor.TOP | Astal.WindowAnchor.RIGHT}
|
||||
application={App}
|
||||
application={app}
|
||||
>
|
||||
<Hidden>
|
||||
<box className="widgets" hexpand halign={Gtk.Align.END}>
|
||||
<box class="widgets" hexpand halign={Gtk.Align.END}>
|
||||
<Systray />
|
||||
<Battery />
|
||||
</box>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { bind, Variable } from "astal";
|
||||
import { createBinding, createComputed } from "ags";
|
||||
import AstalBattery from "gi://AstalBattery";
|
||||
|
||||
const battery = AstalBattery.get_default();
|
||||
@@ -9,19 +9,19 @@ const formatTime = (seconds: number) =>
|
||||
: "--:--";
|
||||
|
||||
export default () => {
|
||||
const percentage = bind(battery, "percentage").as(
|
||||
const percentage = createBinding(battery, "percentage").as(
|
||||
(p) => Math.round(p * 100) + "%",
|
||||
);
|
||||
const charging = bind(battery, "charging");
|
||||
const timeToFull = bind(battery, "timeToFull");
|
||||
const timeToEmpty = bind(battery, "timeToEmpty");
|
||||
const charging = createBinding(battery, "charging");
|
||||
const timeToFull = createBinding(battery, "timeToFull");
|
||||
const timeToEmpty = createBinding(battery, "timeToEmpty");
|
||||
|
||||
const time = Variable.derive(
|
||||
const time = createComputed(
|
||||
[charging, timeToFull, timeToEmpty],
|
||||
(charging, full, empty) => formatTime(charging ? full : empty),
|
||||
);
|
||||
|
||||
const label = Variable.derive(
|
||||
const label = createComputed(
|
||||
[percentage, charging, time],
|
||||
(percentage, charging, time) => {
|
||||
const arrow = charging ? "▲" : "▼";
|
||||
@@ -30,8 +30,8 @@ export default () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<button className="battery">
|
||||
<label className="label" label={bind(label)} />
|
||||
<button class="battery">
|
||||
<label class="label" label={label} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
@@ -1,21 +1,16 @@
|
||||
import { bind, Variable } from "astal";
|
||||
import { GLib } from "astal";
|
||||
import { createPoll } from "ags/time";
|
||||
import GLib from "gi://GLib?version=2.0";
|
||||
|
||||
export default () => {
|
||||
const time = Variable(
|
||||
const time = createPoll(
|
||||
GLib.DateTime.new_now_local().format("%H:%M - %A, %d %B %Y")!,
|
||||
).poll(
|
||||
1000,
|
||||
() => GLib.DateTime.new_now_local().format("%H:%M - %A, %d %B %Y")!,
|
||||
);
|
||||
|
||||
return (
|
||||
<button className="date">
|
||||
<label
|
||||
className="label"
|
||||
onDestroy={() => time.drop()}
|
||||
label={bind(time)}
|
||||
/>
|
||||
<button class="date">
|
||||
<label class="label" label={time} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Gtk } from "astal/gtk3";
|
||||
import { Variable, bind, timeout } from "astal";
|
||||
import { createState } from "ags";
|
||||
import { Gtk } from "ags/gtk3";
|
||||
import { timeout } from "ags/time";
|
||||
|
||||
export default function Hidden({
|
||||
child,
|
||||
@@ -12,24 +13,20 @@ export default function Hidden({
|
||||
orientation?: Gtk.Orientation;
|
||||
transitionType?: Gtk.RevealerTransitionType;
|
||||
}) {
|
||||
const show = Variable(true);
|
||||
const [show, setShow] = createState(true);
|
||||
const contents = child ?? children;
|
||||
|
||||
return (
|
||||
<eventbox
|
||||
clickThrough
|
||||
onHover={() => show.set(true)}
|
||||
onHoverLost={() => show.set(false)}
|
||||
>
|
||||
<eventbox onHover={() => setShow(true)} onHoverLost={() => setShow(false)}>
|
||||
<box orientation={orientation}>
|
||||
<revealer
|
||||
setup={(self) => timeout(2000, () => (self.revealChild = false))}
|
||||
revealChild={bind(show)}
|
||||
onRealize={() => timeout(2000, () => setShow(false))}
|
||||
revealChild={show}
|
||||
transitionType={transitionType}
|
||||
>
|
||||
{Array.isArray(contents) ? <>{contents}</> : contents}
|
||||
</revealer>
|
||||
<box clickThrough className="trigger-guard" />
|
||||
<box class="trigger-guard" />
|
||||
</box>
|
||||
</eventbox>
|
||||
);
|
||||
|
@@ -1,14 +1,14 @@
|
||||
import { execAsync } from "astal/process";
|
||||
import { execAsync } from "ags/process";
|
||||
|
||||
export default () => (
|
||||
<button
|
||||
className="launcher"
|
||||
onClickRelease={() =>
|
||||
class="launcher"
|
||||
onClicked={() =>
|
||||
execAsync(
|
||||
'rofi -modes drun -show drun -run-command \"uwsm app -- {cmd}\"',
|
||||
)
|
||||
}
|
||||
>
|
||||
<icon className="icon" icon="nix-snowflake-symbolic" />;
|
||||
<icon class="icon" icon="nix-snowflake-symbolic" />;
|
||||
</button>
|
||||
);
|
||||
|
@@ -1,28 +1,31 @@
|
||||
import { App } from "astal/gtk3";
|
||||
import { bind } from "astal";
|
||||
import { createBinding, For } from "ags";
|
||||
import app from "ags/gtk3/app";
|
||||
import Tray from "gi://AstalTray";
|
||||
|
||||
const tray = Tray.get_default();
|
||||
|
||||
const TrayButton = ({ item }: { item: Tray.TrayItem }) => (
|
||||
<menubutton
|
||||
className="item"
|
||||
tooltipMarkup={bind(item, "tooltipMarkup")}
|
||||
class="item"
|
||||
tooltipMarkup={createBinding(item, "tooltipMarkup")}
|
||||
usePopover={false}
|
||||
menuModel={bind(item, "menuModel")}
|
||||
actionGroup={bind(item, "actionGroup").as((ag) => ["dbusmenu", ag])}
|
||||
menuModel={createBinding(item, "menuModel")}
|
||||
>
|
||||
<icon gicon={bind(item, "gicon")} />
|
||||
<icon gicon={createBinding(item, "gicon")} />
|
||||
</menubutton>
|
||||
);
|
||||
|
||||
export default () => (
|
||||
<box className="systray">
|
||||
{bind(tray, "items").as((items) =>
|
||||
items.map((item) => {
|
||||
if (item.iconThemePath) App.add_icons(item.iconThemePath);
|
||||
return <TrayButton item={item} />;
|
||||
}),
|
||||
)}
|
||||
</box>
|
||||
);
|
||||
export default () => {
|
||||
let items = createBinding(tray, "items");
|
||||
|
||||
return (
|
||||
<box class="systray">
|
||||
<For each={items}>
|
||||
{(item, _) => {
|
||||
if (item.iconThemePath) app.add_icons(item.iconThemePath);
|
||||
return <TrayButton item={item} />;
|
||||
}}
|
||||
</For>
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
@@ -1,74 +1,83 @@
|
||||
import { bind, Variable } from "astal";
|
||||
import Hyprland from "gi://AstalHyprland";
|
||||
import { range } from "../../lib";
|
||||
import { getHyprlandMonitor, range } from "../../lib";
|
||||
import {
|
||||
Accessor,
|
||||
createBinding,
|
||||
createComputed,
|
||||
createState,
|
||||
Setter,
|
||||
} from "ags";
|
||||
import { Gdk, Gtk } from "ags/gtk3";
|
||||
|
||||
const hyprland = Hyprland.get_default();
|
||||
const BLOCK_SIZE = 10;
|
||||
|
||||
const Workspace = ({ id }: { id: number }) => {
|
||||
let clients: Variable<string[]>;
|
||||
let clients: Accessor<string[]>;
|
||||
let setClients: Setter<string[]>;
|
||||
|
||||
try {
|
||||
const workspace = hyprland.get_workspace(id);
|
||||
clients = Variable(workspace.clients.map((client) => client.address));
|
||||
[clients, setClients] = createState(
|
||||
workspace.clients.map((client) => client.address),
|
||||
);
|
||||
} catch (_) {
|
||||
clients = Variable([]);
|
||||
[clients, setClients] = createState<string[]>([]);
|
||||
}
|
||||
|
||||
const active = Variable.derive(
|
||||
[bind(hyprland, "focusedWorkspace")],
|
||||
const active = createComputed(
|
||||
[createBinding(hyprland, "focusedWorkspace")],
|
||||
(focused) => focused.id == id,
|
||||
);
|
||||
|
||||
hyprland.connect("workspace-added", (_, workspace) => {
|
||||
if (workspace.id != id) return;
|
||||
clients.set(workspace.clients.map((client) => client.address));
|
||||
setClients(workspace.clients.map((client) => client.address));
|
||||
});
|
||||
|
||||
hyprland.connect("workspace-removed", (_, workspaceId) => {
|
||||
if (workspaceId != id) return;
|
||||
clients.set([]);
|
||||
setClients([]);
|
||||
});
|
||||
|
||||
hyprland.connect("client-added", (_hyprland, client) => {
|
||||
if (client.workspace.id != id) return;
|
||||
clients.set([...clients.get(), client.address]);
|
||||
setClients([...clients.get(), client.address]);
|
||||
});
|
||||
|
||||
// Explicit separate event handling instead of Variable.derive(workspaces, clients)
|
||||
// because client-moved events appear to be broken if done that way.
|
||||
hyprland.connect("client-moved", (_hyprland, client, workspace) => {
|
||||
if (workspace.id == id) {
|
||||
clients.set([...clients.get(), client.address]);
|
||||
setClients([...clients.get(), client.address]);
|
||||
} else {
|
||||
clients.set(
|
||||
setClients(
|
||||
clients.get().filter((oldClient) => oldClient != client.address),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
hyprland.connect("client-removed", (_hyprland, address) => {
|
||||
clients.set(clients.get().filter((oldClient) => oldClient != address));
|
||||
setClients(clients.get().filter((oldClient) => oldClient != address));
|
||||
});
|
||||
|
||||
const className = Variable.derive([active, clients], (active, clients) => {
|
||||
const className = createComputed([active, clients], (active, clients) => {
|
||||
if (active) return "button active";
|
||||
if (clients.length > 0) return "button occupied";
|
||||
return "button";
|
||||
});
|
||||
|
||||
return (
|
||||
<box vertical>
|
||||
<box orientation={Gtk.Orientation.VERTICAL}>
|
||||
<box vexpand />
|
||||
<eventbox onClickRelease={() => hyprland.dispatch("workspace", `${id}`)}>
|
||||
<label className={className()} />
|
||||
<label class={className} />
|
||||
</eventbox>
|
||||
<box vexpand />
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ({ monitor }: { monitor: Hyprland.Monitor }) => {
|
||||
export default ({ gdkmonitor }: { gdkmonitor: Gdk.Monitor }) => {
|
||||
const monitor = getHyprlandMonitor(gdkmonitor)!;
|
||||
const workspaces = hyprland.get_workspaces();
|
||||
const displayWorkspaces = workspaces.filter(
|
||||
(w) => w.monitor.id === monitor.id,
|
||||
@@ -78,7 +87,7 @@ export default ({ monitor }: { monitor: Hyprland.Monitor }) => {
|
||||
|
||||
return (
|
||||
<eventbox
|
||||
className="workspaces"
|
||||
class="workspaces"
|
||||
onScroll={(_, e) => {
|
||||
hyprland.dispatch("workspace", e.delta_y > 0 ? "m+1" : "m-1");
|
||||
}}
|
||||
|
Reference in New Issue
Block a user