add(ags/bar): network section + tweak styles
This commit is contained in:
parent
1dbd2d1f0d
commit
5b073fa9d6
@ -60,4 +60,22 @@ export const skip = <T>(list: T[], items: number) => list.filter((_, i) => {
|
|||||||
if (items > (list.length - 1)) items = list.length;
|
if (items > (list.length - 1)) items = list.length;
|
||||||
|
|
||||||
return i > (items - 1);
|
return i > (items - 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const toHumanReadable = (bytes: number, unit: string, fractionDigits = 1) => {
|
||||||
|
const div = 1024;
|
||||||
|
if (Math.abs(bytes) < div) {
|
||||||
|
return bytes.toFixed(fractionDigits) + unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
const units = ["ki", "Mi", "Gi", "Ti"];
|
||||||
|
let u = -1;
|
||||||
|
const r = 10 ** fractionDigits;
|
||||||
|
|
||||||
|
while (Math.round(Math.abs(bytes) * r) / r >= div && u < units.length) {
|
||||||
|
bytes /= div;
|
||||||
|
u++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.toFixed(fractionDigits) + units[u] + unit;
|
||||||
|
}
|
8
ags/lib/widgets/separator.tsx
Normal file
8
ags/lib/widgets/separator.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Gio } from "astal";
|
||||||
|
|
||||||
|
export default function Separator() {
|
||||||
|
return <image
|
||||||
|
cssClasses={["separator"]}
|
||||||
|
gicon={Gio.Icon.new_for_string("switch-on-symbolic")}
|
||||||
|
/>
|
||||||
|
}
|
@ -1,6 +1,11 @@
|
|||||||
|
@use './theme_colors.scss' as *;
|
||||||
@use '_variables' as *;
|
@use '_variables' as *;
|
||||||
@use 'mixins';
|
@use 'mixins';
|
||||||
|
|
||||||
.material-icon {
|
.material-icon {
|
||||||
@include mixins.material-icon;
|
@include mixins.material-icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
color: $base05;
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
window.bar {
|
window.bar {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
>box {
|
>box {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
@ -11,10 +11,10 @@ window.bar {
|
|||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
background: $base00;
|
background: $base00;
|
||||||
padding: 0.25rem 1rem;
|
padding: 0rem 1rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
* { color: $base0B; }
|
*:not(.separator) { color: $base0B; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.tray .item {
|
.tray .item {
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { App, Astal, Gdk, Gtk } from "astal/gtk4"
|
import { App, Astal, Gdk, Gtk } from "astal/gtk4";
|
||||||
import { Binding } from "astal"
|
|
||||||
|
|
||||||
import Battery from "gi://AstalBattery";
|
import Battery from "gi://AstalBattery";
|
||||||
|
|
||||||
|
import NetworkInfo from "./components/network_info";
|
||||||
import { BatteryInfo } from "./components/battery";
|
import { BatteryInfo } from "./components/battery";
|
||||||
|
import Workspace from "./components/workspace";
|
||||||
import DateTime from "@lib/widgets/datetime";
|
import DateTime from "@lib/widgets/datetime";
|
||||||
import Workspace from "./workspace";
|
import TrayView from "./components/tray";
|
||||||
import TrayView from "./tray";
|
import Separator from "@/lib/widgets/separator";
|
||||||
|
|
||||||
export default function Bar(monitor_id: number) {
|
export default function Bar(monitor_id: number) {
|
||||||
const { BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor
|
const { BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor
|
||||||
@ -21,8 +22,8 @@ export default function Bar(monitor_id: number) {
|
|||||||
|
|
||||||
return <window
|
return <window
|
||||||
visible
|
visible
|
||||||
name={`bar_${monitor_id}`}
|
name={`bar_${monitor_id}`}
|
||||||
monitor={monitor_id}
|
monitor={monitor_id}
|
||||||
cssClasses={["bar"]}
|
cssClasses={["bar"]}
|
||||||
exclusivity={Astal.Exclusivity.IGNORE}
|
exclusivity={Astal.Exclusivity.IGNORE}
|
||||||
anchor={BOTTOM | LEFT | RIGHT}
|
anchor={BOTTOM | LEFT | RIGHT}
|
||||||
@ -38,8 +39,13 @@ export default function Bar(monitor_id: number) {
|
|||||||
</menubutton>,
|
</menubutton>,
|
||||||
BatteryInfo({ battery }),
|
BatteryInfo({ battery }),
|
||||||
<button hasFrame={false} iconName={"applications-system-symbolic"} onButtonPressed={openQuickSettings} />,
|
<button hasFrame={false} iconName={"applications-system-symbolic"} onButtonPressed={openQuickSettings} />,
|
||||||
|
NetworkInfo(),
|
||||||
TrayView()
|
TrayView()
|
||||||
]}
|
].reduce((acc, it) => <>
|
||||||
|
{acc}
|
||||||
|
{Separator()}
|
||||||
|
{it}
|
||||||
|
</>)}
|
||||||
</box>
|
</box>
|
||||||
</window>
|
</window>
|
||||||
}
|
}
|
||||||
|
77
ags/widget/bar/components/network_info.tsx
Normal file
77
ags/widget/bar/components/network_info.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import Network from "gi://AstalNetwork";
|
||||||
|
import { bind, Variable } from "astal";
|
||||||
|
import { toHumanReadable } from "@/lib/utils";
|
||||||
|
|
||||||
|
type IpOutput = Array<{
|
||||||
|
ifname: string;
|
||||||
|
stats64: {
|
||||||
|
rx: { bytes: number };
|
||||||
|
tx: { bytes: number };
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
type Usage = {
|
||||||
|
initialRx: number | null;
|
||||||
|
initialTx: number | null;
|
||||||
|
totalRx: number;
|
||||||
|
totalTx: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getConnection = (instance?: Network.Wifi | Network.Wired) => {
|
||||||
|
if (!instance) return null;
|
||||||
|
|
||||||
|
const addresses = (instance instanceof Network.Wifi
|
||||||
|
? instance?.activeConnection
|
||||||
|
: instance?.connection)
|
||||||
|
?.ip4Config?.get_addresses();
|
||||||
|
return addresses.map(addr => addr.get_address()).join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NetworkInfo() {
|
||||||
|
const net = Network.get_default();
|
||||||
|
const wiredConn = bind(net, "wired");
|
||||||
|
const wifiConn = bind(net, "wifi");
|
||||||
|
|
||||||
|
const currentConn = Variable.derive([
|
||||||
|
wiredConn,
|
||||||
|
wifiConn
|
||||||
|
], (wired, wifi) => {
|
||||||
|
switch (net.primary) {
|
||||||
|
case Network.Primary.WIFI: return wifi;
|
||||||
|
case Network.Primary.WIRED: return wired;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const networkInterface = net.primary == 1 ? "enp0s31f6" : "wlp4s0";
|
||||||
|
const networkUsage = Variable<Usage>({
|
||||||
|
initialRx: null,
|
||||||
|
initialTx: null,
|
||||||
|
totalRx: 0,
|
||||||
|
totalTx: 0,
|
||||||
|
}).poll(
|
||||||
|
5000,
|
||||||
|
['ip', '-s', '-j', 'link', 'show', networkInterface],
|
||||||
|
(out, prev) => {
|
||||||
|
const ipOutput = JSON.parse(out) as IpOutput;
|
||||||
|
const totalRx = ipOutput?.reduce((prev, item) => prev + item.stats64.rx.bytes, 0);
|
||||||
|
const totalTx = ipOutput?.reduce((prev, item) => prev + item.stats64.tx.bytes, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initialRx: prev.initialRx ?? totalRx,
|
||||||
|
initialTx: prev.initialTx ?? totalTx,
|
||||||
|
totalTx,
|
||||||
|
totalRx,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataBinding = bind(networkUsage);
|
||||||
|
const binding = bind(currentConn);
|
||||||
|
|
||||||
|
return binding.as(Boolean) ? <box spacing={3}>
|
||||||
|
{binding.as(getConnection)}
|
||||||
|
TX: {dataBinding.as(data => toHumanReadable(data.totalTx / 4, "B/s"))}
|
||||||
|
RX: {dataBinding.as(data => toHumanReadable(data.totalRx / 4, "B/s"))}
|
||||||
|
</box> : <label>No network</label>
|
||||||
|
}
|
@ -35,7 +35,8 @@ async function getClipboardHistory(history: Variable<Entry[]>) {
|
|||||||
history.set(ids
|
history.set(ids
|
||||||
.map((id, index) => ({ id, content: contents[index] }))
|
.map((id, index) => ({ id, content: contents[index] }))
|
||||||
.filter(({ content }) => content && content.length > 0));
|
.filter(({ content }) => content && content.length > 0));
|
||||||
} catch {
|
} catch (rer){
|
||||||
|
console.error(rer)
|
||||||
history.set([]);
|
history.set([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user