ags added
This commit is contained in:
45
flake.lock
generated
45
flake.lock
generated
@@ -1,5 +1,48 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"ags": {
|
||||||
|
"inputs": {
|
||||||
|
"astal": [
|
||||||
|
"astal"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775689345,
|
||||||
|
"narHash": "sha256-tM3s7CX+tgxlYW0Sk3nzVThg2MHn08foIuMxABupxIs=",
|
||||||
|
"owner": "aylur",
|
||||||
|
"repo": "ags",
|
||||||
|
"rev": "bbee2f18939f1ec7ff720e717cf305e73635628f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "aylur",
|
||||||
|
"repo": "ags",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"astal": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780295699,
|
||||||
|
"narHash": "sha256-gt9jeb/HOoiUSOTnE5I9K/B9LEbjJW5k37Xq99HOf/Q=",
|
||||||
|
"owner": "aylur",
|
||||||
|
"repo": "astal",
|
||||||
|
"rev": "271851bbc07748100382ae7caf6ef71c70c01bfc",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "aylur",
|
||||||
|
"repo": "astal",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"base16-schemes": {
|
"base16-schemes": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -254,6 +297,8 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"ags": "ags",
|
||||||
|
"astal": "astal",
|
||||||
"darwin": "darwin",
|
"darwin": "darwin",
|
||||||
"hardware": "hardware",
|
"hardware": "hardware",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
|
|||||||
10
flake.nix
10
flake.nix
@@ -35,6 +35,16 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
astal = {
|
||||||
|
url = "github:aylur/astal";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
ags = {
|
||||||
|
url = "github:aylur/ags";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.astal.follows = "astal";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs:
|
outputs = inputs:
|
||||||
|
|||||||
57
modules/home/wayland/apps/ags/default.nix
Normal file
57
modules/home/wayland/apps/ags/default.nix
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{ inputs, lib, config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
colorsScss = ''
|
||||||
|
$base00: #${config.colorScheme.palette.base00};
|
||||||
|
$base01: #${config.colorScheme.palette.base01};
|
||||||
|
$base02: #${config.colorScheme.palette.base02};
|
||||||
|
$base03: #${config.colorScheme.palette.base03};
|
||||||
|
$base04: #${config.colorScheme.palette.base04};
|
||||||
|
$base05: #${config.colorScheme.palette.base05};
|
||||||
|
$base06: #${config.colorScheme.palette.base06};
|
||||||
|
$base07: #${config.colorScheme.palette.base07};
|
||||||
|
$base08: #${config.colorScheme.palette.base08};
|
||||||
|
$base09: #${config.colorScheme.palette.base09};
|
||||||
|
$base0A: #${config.colorScheme.palette.base0A};
|
||||||
|
$base0B: #${config.colorScheme.palette.base0B};
|
||||||
|
$base0C: #${config.colorScheme.palette.base0C};
|
||||||
|
$base0D: #${config.colorScheme.palette.base0D};
|
||||||
|
$base0E: #${config.colorScheme.palette.base0E};
|
||||||
|
$base0F: #${config.colorScheme.palette.base0F};
|
||||||
|
|
||||||
|
|
||||||
|
$fg: $base07;
|
||||||
|
$bg0: $base00;
|
||||||
|
$bg1: $base01;
|
||||||
|
|
||||||
|
$border-color: $base03;
|
||||||
|
$border-color-focus: $base04;
|
||||||
|
$border-radius: ${config.colorScheme.palette.border-radius}px;
|
||||||
|
$border-width: ${config.colorScheme.palette.border-width}px;
|
||||||
|
|
||||||
|
$gaps-screen: ${config.colorScheme.palette.gaps-screen}px;
|
||||||
|
$gaps-window: ${config.colorScheme.palette.gaps-window}px;
|
||||||
|
'';
|
||||||
|
configDir = pkgs.runCommandLocal "ags-config" {} ''
|
||||||
|
mkdir -p "$out"
|
||||||
|
cp -r ${lib.cleanSource ./src}/. "$out/"
|
||||||
|
mkdir -p "$out/css"
|
||||||
|
cat > "$out/css/_colors.scss" <<'EOF'
|
||||||
|
${colorsScss}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
|
||||||
|
imports = [ inputs.ags.homeManagerModules.default ];
|
||||||
|
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
||||||
|
programs.ags = {
|
||||||
|
enable = true;
|
||||||
|
configDir = configDir;
|
||||||
|
extraPackages = with pkgs; [
|
||||||
|
inputs.astal.packages.${pkgs.system}.battery
|
||||||
|
fzf
|
||||||
|
bluez
|
||||||
|
custom.amdgpu_top
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
768
modules/home/wayland/apps/ags/src/app.tsx
Normal file
768
modules/home/wayland/apps/ags/src/app.tsx
Normal file
@@ -0,0 +1,768 @@
|
|||||||
|
import app from "ags/gtk4/app"
|
||||||
|
import { Astal } from "ags/gtk4"
|
||||||
|
import { createState, onCleanup } from "ags"
|
||||||
|
import { readFileAsync } from "ags/file"
|
||||||
|
import { execAsync } from "ags/process"
|
||||||
|
import { createPoll } from "ags/time"
|
||||||
|
import Gdk from "gi://Gdk?version=4.0"
|
||||||
|
import GLib from "gi://GLib"
|
||||||
|
import Gtk from "gi://Gtk?version=4.0"
|
||||||
|
|
||||||
|
import style from "./style.scss"
|
||||||
|
|
||||||
|
type Workspace = {
|
||||||
|
id: number
|
||||||
|
occupied: boolean
|
||||||
|
focused: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type BatteryState = {
|
||||||
|
available: boolean
|
||||||
|
percent: number
|
||||||
|
status: string
|
||||||
|
remaining: string
|
||||||
|
icon: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SystemState = {
|
||||||
|
cpu: number
|
||||||
|
gpu: number
|
||||||
|
memory: number
|
||||||
|
memoryText: string
|
||||||
|
battery: BatteryState
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectivityState = {
|
||||||
|
wifi: {
|
||||||
|
label: string
|
||||||
|
detail: string
|
||||||
|
icon: string
|
||||||
|
}
|
||||||
|
ethernet: {
|
||||||
|
label: string
|
||||||
|
detail: string
|
||||||
|
icon: string
|
||||||
|
}
|
||||||
|
bluetooth: {
|
||||||
|
label: string
|
||||||
|
detail: string
|
||||||
|
icon: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClockState = {
|
||||||
|
hour: string
|
||||||
|
minute: string
|
||||||
|
month: string
|
||||||
|
year: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const SYSTEM_INIT: SystemState = {
|
||||||
|
cpu: 0,
|
||||||
|
gpu: 0,
|
||||||
|
memory: 0,
|
||||||
|
memoryText: "0 / 0 GiB",
|
||||||
|
battery: {
|
||||||
|
available: false,
|
||||||
|
percent: 0,
|
||||||
|
status: "No battery",
|
||||||
|
remaining: "",
|
||||||
|
icon: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONNECTIVITY_INIT: ConnectivityState = {
|
||||||
|
wifi: {
|
||||||
|
label: "Wi-Fi",
|
||||||
|
detail: "Unavailable",
|
||||||
|
icon: "",
|
||||||
|
},
|
||||||
|
ethernet: {
|
||||||
|
label: "Ethernet",
|
||||||
|
detail: "Disconnected",
|
||||||
|
icon: "",
|
||||||
|
},
|
||||||
|
bluetooth: {
|
||||||
|
label: "Bluetooth",
|
||||||
|
detail: "Off",
|
||||||
|
icon: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLOCK_INIT: ClockState = {
|
||||||
|
hour: "--",
|
||||||
|
minute: "--",
|
||||||
|
month: "--",
|
||||||
|
year: "--",
|
||||||
|
}
|
||||||
|
|
||||||
|
let previousCpuSample: { total: number; idle: number } | null = null
|
||||||
|
const [powerMenuVisible, setPowerMenuVisible] = createState(false)
|
||||||
|
|
||||||
|
function attachHover(widget: Gtk.Widget, onEnter: () => void, onLeave: () => void) {
|
||||||
|
const controller = new Gtk.EventControllerMotion()
|
||||||
|
controller.connect("enter", onEnter)
|
||||||
|
controller.connect("leave", onLeave)
|
||||||
|
widget.add_controller(controller)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampPercent(value: number) {
|
||||||
|
if (!Number.isFinite(value)) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(0, Math.min(100, Math.round(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function commandOrEmpty(command: string | string[]) {
|
||||||
|
try {
|
||||||
|
return (await execAsync(command)).trim()
|
||||||
|
} catch {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readText(path: string) {
|
||||||
|
try {
|
||||||
|
return (await readFileAsync(path)).trim()
|
||||||
|
} catch {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readNumber(path: string) {
|
||||||
|
const value = Number(await readText(path))
|
||||||
|
return Number.isFinite(value) ? value : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(command: string) {
|
||||||
|
execAsync(["bash", "-lc", command]).catch((error) => console.error(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusWorkspace(id: number) {
|
||||||
|
run(`hyprctl dispatch workspace ${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mediaAction(action: string) {
|
||||||
|
run(`playerctl ${action}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readWeather() {
|
||||||
|
const line = await commandOrEmpty([
|
||||||
|
"bash",
|
||||||
|
"-lc",
|
||||||
|
"curl -fsS 'https://wttr.in/?format=%l:+%C+%t' 2>/dev/null | head -n1",
|
||||||
|
])
|
||||||
|
|
||||||
|
return line || "Weather unavailable"
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCalendar(now: Date) {
|
||||||
|
const monthLabel = now.toLocaleString("en-US", { month: "short" }).toUpperCase()
|
||||||
|
const year = now.getFullYear()
|
||||||
|
const header = `${monthLabel} ${year}`
|
||||||
|
const weekdays = "MO TU WE TH FR SA SU"
|
||||||
|
const first = new Date(year, now.getMonth(), 1)
|
||||||
|
const lastDay = new Date(year, now.getMonth() + 1, 0).getDate()
|
||||||
|
const offset = (first.getDay() + 6) % 7
|
||||||
|
const slots = Array.from({ length: offset + lastDay }, (_, index) =>
|
||||||
|
index < offset ? " " : String(index - offset + 1).padStart(2, " "),
|
||||||
|
)
|
||||||
|
|
||||||
|
const rows = []
|
||||||
|
for (let index = 0; index < slots.length; index += 7) {
|
||||||
|
rows.push(slots.slice(index, index + 7).join(" "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return [header, weekdays, ...rows].join("\n").replace(/ /g, "\u2007")
|
||||||
|
}
|
||||||
|
|
||||||
|
function workspaceIcon(workspace: Workspace) {
|
||||||
|
if (workspace.focused) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspace.occupied) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function wifiIcon(detail: string) {
|
||||||
|
if (detail === "Off") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const strength = Number(detail.split("%", 1)[0])
|
||||||
|
|
||||||
|
if (!Number.isFinite(strength) || strength <= 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strength < 25) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strength < 50) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strength < 75) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function batteryIcon(percent: number, status: string) {
|
||||||
|
if (status === "Charging") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 10) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 20) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 30) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 40) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 50) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 60) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 70) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 80) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent <= 90) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatHours(hours: number) {
|
||||||
|
const totalMinutes = Math.max(0, Math.round(hours * 60))
|
||||||
|
const hh = String(Math.floor(totalMinutes / 60)).padStart(2, "0")
|
||||||
|
const mm = String(totalMinutes % 60).padStart(2, "0")
|
||||||
|
return `${hh}:${mm}`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readWorkspaces() {
|
||||||
|
const [workspacesRaw, monitorsRaw] = await Promise.all([
|
||||||
|
commandOrEmpty(["hyprctl", "-j", "workspaces"]),
|
||||||
|
commandOrEmpty(["hyprctl", "-j", "monitors"]),
|
||||||
|
])
|
||||||
|
|
||||||
|
const workspaces = workspacesRaw ? JSON.parse(workspacesRaw) : []
|
||||||
|
const monitors = monitorsRaw ? JSON.parse(monitorsRaw) : []
|
||||||
|
const focused = monitors.find((monitor: any) => monitor.focused)?.activeWorkspace?.id ?? 1
|
||||||
|
const occupied = new Set(
|
||||||
|
workspaces
|
||||||
|
.map((workspace: any) => Number(workspace.id))
|
||||||
|
.filter((id: number) => Number.isFinite(id) && id > 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Array.from({ length: 10 }, (_, index) => {
|
||||||
|
const id = index + 1
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
occupied: occupied.has(id),
|
||||||
|
focused: focused === id,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readSystem() {
|
||||||
|
const [statRaw, meminfoRaw, gpuRaw] = await Promise.all([
|
||||||
|
readText("/proc/stat"),
|
||||||
|
readText("/proc/meminfo"),
|
||||||
|
commandOrEmpty(["amdgpu_top", "-J", "-n", "1"]),
|
||||||
|
])
|
||||||
|
|
||||||
|
let cpu = 0
|
||||||
|
const statLine = statRaw.split("\n")[0] ?? ""
|
||||||
|
const cpuValues = statLine
|
||||||
|
.split(/\s+/)
|
||||||
|
.slice(1)
|
||||||
|
.map((value) => Number(value))
|
||||||
|
.filter((value) => Number.isFinite(value))
|
||||||
|
|
||||||
|
if (cpuValues.length >= 4) {
|
||||||
|
const total = cpuValues.reduce((sum, value) => sum + value, 0)
|
||||||
|
const idle = (cpuValues[3] ?? 0) + (cpuValues[4] ?? 0)
|
||||||
|
|
||||||
|
if (previousCpuSample) {
|
||||||
|
const totalDelta = total - previousCpuSample.total
|
||||||
|
const idleDelta = idle - previousCpuSample.idle
|
||||||
|
|
||||||
|
if (totalDelta > 0) {
|
||||||
|
cpu = clampPercent(((totalDelta - idleDelta) / totalDelta) * 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousCpuSample = { total, idle }
|
||||||
|
}
|
||||||
|
|
||||||
|
const memTotal = Number(meminfoRaw.match(/^MemTotal:\s+(\d+)/m)?.[1] ?? 0)
|
||||||
|
const memAvailable = Number(meminfoRaw.match(/^MemAvailable:\s+(\d+)/m)?.[1] ?? 0)
|
||||||
|
const memUsed = Math.max(0, memTotal - memAvailable)
|
||||||
|
const memory = memTotal > 0 ? clampPercent((memUsed / memTotal) * 100) : 0
|
||||||
|
const memoryText = `${(memUsed / 1024 / 1024).toFixed(1)} / ${(memTotal / 1024 / 1024).toFixed(1)} GiB`
|
||||||
|
|
||||||
|
let gpu = 0
|
||||||
|
|
||||||
|
if (gpuRaw) {
|
||||||
|
try {
|
||||||
|
const gpuData = JSON.parse(gpuRaw)
|
||||||
|
const device = gpuData.devices?.[0] ?? {}
|
||||||
|
|
||||||
|
gpu =
|
||||||
|
clampPercent(
|
||||||
|
Number(
|
||||||
|
device.gpu_activity?.GFX?.value ??
|
||||||
|
device.GRBM2?.["Command Processor - Graphics"]?.value ??
|
||||||
|
device.GRBM2?.["CommandProcessor-Graphics"]?.value ??
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
) || 0
|
||||||
|
} catch {
|
||||||
|
gpu = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const batteryPresent = Boolean(await readText("/sys/class/power_supply/BAT0/status"))
|
||||||
|
let battery = SYSTEM_INIT.battery
|
||||||
|
|
||||||
|
if (batteryPresent) {
|
||||||
|
const [status, percent, powerNow, energyNow, energyFull] = await Promise.all([
|
||||||
|
readText("/sys/class/power_supply/BAT0/status"),
|
||||||
|
readNumber("/sys/class/power_supply/BAT0/capacity"),
|
||||||
|
readNumber("/sys/class/power_supply/BAT0/power_now"),
|
||||||
|
readNumber("/sys/class/power_supply/BAT0/energy_now"),
|
||||||
|
readNumber("/sys/class/power_supply/BAT0/energy_full"),
|
||||||
|
])
|
||||||
|
|
||||||
|
let remaining = status
|
||||||
|
|
||||||
|
if (powerNow > 0 && energyNow > 0 && energyFull > 0) {
|
||||||
|
const hours =
|
||||||
|
status === "Charging"
|
||||||
|
? (energyFull - energyNow) / powerNow
|
||||||
|
: status === "Discharging"
|
||||||
|
? energyNow / powerNow
|
||||||
|
: 0
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
remaining = `${formatHours(hours)} ${status === "Charging" ? "to full" : "left"}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
battery = {
|
||||||
|
available: true,
|
||||||
|
percent: clampPercent(percent),
|
||||||
|
status,
|
||||||
|
remaining,
|
||||||
|
icon: batteryIcon(percent, status),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cpu,
|
||||||
|
gpu,
|
||||||
|
memory,
|
||||||
|
memoryText,
|
||||||
|
battery,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readConnectivity() {
|
||||||
|
const deviceStatus = await commandOrEmpty(["nmcli", "-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"])
|
||||||
|
const devices = deviceStatus
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((line) => {
|
||||||
|
const [device, type, state, connection] = line.split(":")
|
||||||
|
return { device, type, state, connection }
|
||||||
|
})
|
||||||
|
|
||||||
|
const wifiDevice = devices.find((device) => device.type === "wifi")
|
||||||
|
const ethernetDevice = devices.find((device) => device.type === "ethernet")
|
||||||
|
|
||||||
|
let wifi = CONNECTIVITY_INIT.wifi
|
||||||
|
|
||||||
|
if (wifiDevice) {
|
||||||
|
if (wifiDevice.state === "connected") {
|
||||||
|
const wifiList = await commandOrEmpty([
|
||||||
|
"nmcli",
|
||||||
|
"-t",
|
||||||
|
"-f",
|
||||||
|
"IN-USE,SIGNAL,SSID",
|
||||||
|
"device",
|
||||||
|
"wifi",
|
||||||
|
"list",
|
||||||
|
"--rescan",
|
||||||
|
"no",
|
||||||
|
])
|
||||||
|
const activeLine =
|
||||||
|
wifiList
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.find((line) => line.startsWith("*:")) ?? ""
|
||||||
|
const [, signalRaw = "0", ssid = wifiDevice.connection] = activeLine.split(":")
|
||||||
|
const signal = clampPercent(Number(signalRaw))
|
||||||
|
|
||||||
|
wifi = {
|
||||||
|
label: "Wi-Fi",
|
||||||
|
detail: `${signal}% ${ssid || wifiDevice.connection}`,
|
||||||
|
icon: wifiIcon(`${signal}%`),
|
||||||
|
}
|
||||||
|
} else if (wifiDevice.state === "disconnected") {
|
||||||
|
wifi = {
|
||||||
|
label: "Wi-Fi",
|
||||||
|
detail: "Idle",
|
||||||
|
icon: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ethernet =
|
||||||
|
ethernetDevice && ethernetDevice.state === "connected"
|
||||||
|
? {
|
||||||
|
label: "Ethernet",
|
||||||
|
detail: ethernetDevice.connection || ethernetDevice.device,
|
||||||
|
icon: "",
|
||||||
|
}
|
||||||
|
: CONNECTIVITY_INIT.ethernet
|
||||||
|
|
||||||
|
const bluetoothShow = await commandOrEmpty(["bash", "-lc", "bluetoothctl show 2>/dev/null || true"])
|
||||||
|
const bluetoothDevices = await commandOrEmpty([
|
||||||
|
"bash",
|
||||||
|
"-lc",
|
||||||
|
"bluetoothctl devices Connected 2>/dev/null | cut -d' ' -f3- || true",
|
||||||
|
])
|
||||||
|
const bluetoothPowered = /Powered:\s+yes/.test(bluetoothShow)
|
||||||
|
const connectedDevices = bluetoothDevices.split("\n").map((line) => line.trim()).filter(Boolean)
|
||||||
|
|
||||||
|
const bluetooth = bluetoothPowered
|
||||||
|
? {
|
||||||
|
label: "Bluetooth",
|
||||||
|
detail: connectedDevices[0] ?? "Ready",
|
||||||
|
icon: connectedDevices.length > 0 ? "" : "",
|
||||||
|
}
|
||||||
|
: CONNECTIVITY_INIT.bluetooth
|
||||||
|
|
||||||
|
return {
|
||||||
|
wifi,
|
||||||
|
ethernet,
|
||||||
|
bluetooth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Section(props: { title: string; className?: string; children?: JSX.Element | Array<JSX.Element> }) {
|
||||||
|
return (
|
||||||
|
<box class={`section ${props.className ?? ""}`} orientation={Gtk.Orientation.VERTICAL}>
|
||||||
|
<label class="section-title" xalign={0} label={props.title} />
|
||||||
|
<box class="section-body" orientation={Gtk.Orientation.VERTICAL} spacing={0}>
|
||||||
|
{props.children}
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatRow(props: {
|
||||||
|
icon: any
|
||||||
|
label: any
|
||||||
|
value: any
|
||||||
|
fraction?: any
|
||||||
|
visible?: any
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<box class="stat-row" orientation={Gtk.Orientation.VERTICAL} visible={props.visible ?? true}>
|
||||||
|
<box class="stat-head" orientation={Gtk.Orientation.VERTICAL} spacing={4}>
|
||||||
|
<label class="stat-icon" label={props.icon} />
|
||||||
|
</box>
|
||||||
|
<levelbar class="stat-bar" minValue={0} maxValue={1} value={props.fraction ?? 0} />
|
||||||
|
</box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusRow(props: { icon: any; label: any; detail: any }) {
|
||||||
|
return (
|
||||||
|
<box class="status-row">
|
||||||
|
<label class="status-icon" label={props.icon} />
|
||||||
|
</box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActionButton(props: { icon: string; tooltip: string; command: string }) {
|
||||||
|
return (
|
||||||
|
<button class="action-button" tooltipText={props.tooltip} onClicked={() => run(props.command)}>
|
||||||
|
<label class="action-icon" label={props.icon} />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function WorkspacesSection() {
|
||||||
|
const workspaces = createPoll<Workspace[]>([], 1500, readWorkspaces)
|
||||||
|
const workspaceIds = Array.from({ length: 10 }, (_, index) => index + 1)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="Desktops" className="desktops-section">
|
||||||
|
<box class="workspace-stack" orientation={Gtk.Orientation.VERTICAL} spacing={2}>
|
||||||
|
{workspaceIds.map((id) => {
|
||||||
|
const workspace = workspaces(
|
||||||
|
(items) => items.find((item) => item.id === id) ?? { id, occupied: false, focused: false },
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
class={workspace((item) =>
|
||||||
|
item.focused
|
||||||
|
? "workspace-button focused"
|
||||||
|
: item.occupied
|
||||||
|
? "workspace-button occupied"
|
||||||
|
: "workspace-button",
|
||||||
|
)}
|
||||||
|
tooltipText={`Workspace ${id}`}
|
||||||
|
onClicked={() => focusWorkspace(id)}
|
||||||
|
>
|
||||||
|
<label class="workspace-icon" label={workspace((item) => workspaceIcon(item))} />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</box>
|
||||||
|
</Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MediaSection() {
|
||||||
|
return (
|
||||||
|
<Section title="Media">
|
||||||
|
<box class="media-controls" orientation={Gtk.Orientation.VERTICAL} spacing={4}>
|
||||||
|
<button class="mini-button" tooltipText="Previous" onClicked={() => mediaAction("previous")}>
|
||||||
|
<label label="" />
|
||||||
|
</button>
|
||||||
|
<button class="mini-button" tooltipText="Play / Pause" onClicked={() => mediaAction("play-pause")}>
|
||||||
|
<label label="" />
|
||||||
|
</button>
|
||||||
|
<button class="mini-button" tooltipText="Next" onClicked={() => mediaAction("next")}>
|
||||||
|
<label label="" />
|
||||||
|
</button>
|
||||||
|
</box>
|
||||||
|
</Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SystemSection() {
|
||||||
|
const system = createPoll<SystemState>(SYSTEM_INIT, 2500, readSystem)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="System">
|
||||||
|
<StatRow
|
||||||
|
icon=""
|
||||||
|
label="CPU"
|
||||||
|
value={system((value) => `${value.cpu}%`)}
|
||||||
|
fraction={system((value) => value.cpu / 100)}
|
||||||
|
/>
|
||||||
|
<StatRow
|
||||||
|
icon=""
|
||||||
|
label="GPU"
|
||||||
|
value={system((value) => `${value.gpu}%`)}
|
||||||
|
fraction={system((value) => value.gpu / 100)}
|
||||||
|
/>
|
||||||
|
<StatRow
|
||||||
|
icon=""
|
||||||
|
label="RAM"
|
||||||
|
value={system((value) => `${value.memory}%`)}
|
||||||
|
fraction={system((value) => value.memory / 100)}
|
||||||
|
/>
|
||||||
|
<StatRow
|
||||||
|
icon={system((value) => value.battery.icon)}
|
||||||
|
label={system((value) => value.battery.status)}
|
||||||
|
value={system((value) => `${value.battery.percent}%`)}
|
||||||
|
fraction={system((value) => value.battery.percent / 100)}
|
||||||
|
visible={system((value) => value.battery.available)}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ConnectivitySection() {
|
||||||
|
const connectivity = createPoll<ConnectivityState>(CONNECTIVITY_INIT, 5000, readConnectivity)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="Network">
|
||||||
|
<StatusRow
|
||||||
|
icon={connectivity((value) => value.wifi.icon)}
|
||||||
|
label={connectivity((value) => value.wifi.label)}
|
||||||
|
detail={connectivity((value) => value.wifi.detail)}
|
||||||
|
/>
|
||||||
|
<StatusRow
|
||||||
|
icon={connectivity((value) => value.bluetooth.icon)}
|
||||||
|
label={connectivity((value) => value.bluetooth.label)}
|
||||||
|
detail={connectivity((value) => value.bluetooth.detail)}
|
||||||
|
/>
|
||||||
|
<StatusRow
|
||||||
|
icon={connectivity((value) => value.ethernet.icon)}
|
||||||
|
label={connectivity((value) => value.ethernet.label)}
|
||||||
|
detail={connectivity((value) => value.ethernet.detail)}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function WidgetsSection() {
|
||||||
|
return (
|
||||||
|
<Section title="Widgets">
|
||||||
|
<box class="action-row" orientation={Gtk.Orientation.VERTICAL} spacing={4}>
|
||||||
|
<ActionButton icon="" tooltip="Launcher" command="wofi --show drun" />
|
||||||
|
<ActionButton icon="" tooltip="Terminal" command="kitty" />
|
||||||
|
<ActionButton icon="" tooltip="Mute speakers" command="wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" />
|
||||||
|
<ActionButton icon="" tooltip="Mute microphone" command="wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle" />
|
||||||
|
</box>
|
||||||
|
</Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ClockPowerSection() {
|
||||||
|
const clock = createPoll<ClockState>(CLOCK_INIT, 1000, () => {
|
||||||
|
const now = GLib.DateTime.new_now_local()
|
||||||
|
|
||||||
|
return {
|
||||||
|
hour: now.format("%H") ?? "--",
|
||||||
|
minute: now.format("%M") ?? "--",
|
||||||
|
month: now.format("%m") ?? "--",
|
||||||
|
year: now.format("%y") ?? "--",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const [clockHovered, setClockHovered] = createState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="Time / Power" className="clock-section">
|
||||||
|
<button
|
||||||
|
class="clock-trigger"
|
||||||
|
tooltipText="Open power menu"
|
||||||
|
onClicked={() => setPowerMenuVisible(true)}
|
||||||
|
$={(self) => attachHover(self, () => setClockHovered(true), () => setClockHovered(false))}
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="clock-time"
|
||||||
|
justify={Gtk.Justification.CENTER}
|
||||||
|
label={clock((value) =>
|
||||||
|
clockHovered() ? `${value.month}\n${value.year}` : `${value.hour}\n${value.minute}`,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</Section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function PowerMenu({ gdkmonitor }: { gdkmonitor: Gdk.Monitor }) {
|
||||||
|
const weather = createPoll("Weather unavailable", 900000, readWeather)
|
||||||
|
const calendar = createPoll(renderCalendar(new Date()), 60000, () => renderCalendar(new Date()))
|
||||||
|
const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor
|
||||||
|
|
||||||
|
return (
|
||||||
|
<window
|
||||||
|
visible={powerMenuVisible}
|
||||||
|
name="ags-power-menu"
|
||||||
|
namespace="ags-power-menu"
|
||||||
|
gdkmonitor={gdkmonitor}
|
||||||
|
application={app}
|
||||||
|
layer={Astal.Layer.OVERLAY}
|
||||||
|
keymode={Astal.Keymode.ON_DEMAND}
|
||||||
|
exclusivity={Astal.Exclusivity.IGNORE}
|
||||||
|
anchor={TOP | BOTTOM | LEFT | RIGHT}
|
||||||
|
margin={0}
|
||||||
|
>
|
||||||
|
<overlay hexpand vexpand>
|
||||||
|
<button class="power-backdrop" hexpand vexpand onClicked={() => setPowerMenuVisible(false)} />
|
||||||
|
<box type="overlay" class="power-menu" orientation={Gtk.Orientation.VERTICAL} halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
|
||||||
|
<label class="power-weather" wrap justify={Gtk.Justification.CENTER} label={weather((value) => value)} />
|
||||||
|
<label class="power-calendar" justify={Gtk.Justification.LEFT} xalign={0} label={calendar((value) => value)} />
|
||||||
|
<box class="power-actions" spacing={8}>
|
||||||
|
<ActionButton icon="" tooltip="Lock" command="swaylock" />
|
||||||
|
<ActionButton icon="" tooltip="Suspend" command="systemctl suspend" />
|
||||||
|
<ActionButton icon="" tooltip="Reboot" command="systemctl reboot" />
|
||||||
|
<ActionButton icon="" tooltip="Power off" command="systemctl poweroff" />
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
|
</overlay>
|
||||||
|
</window>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function RightBar({ gdkmonitor }: { gdkmonitor: Gdk.Monitor }) {
|
||||||
|
let window: Astal.Window
|
||||||
|
const { TOP, BOTTOM, RIGHT } = Astal.WindowAnchor
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
window.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<window
|
||||||
|
$={(self) => (window = self)}
|
||||||
|
visible
|
||||||
|
name={`ags-right-bar-${gdkmonitor.connector}`}
|
||||||
|
namespace="ags-right-bar"
|
||||||
|
gdkmonitor={gdkmonitor}
|
||||||
|
application={app}
|
||||||
|
anchor={TOP | BOTTOM | RIGHT}
|
||||||
|
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||||
|
marginRight={0}
|
||||||
|
marginTop={0}
|
||||||
|
marginBottom={0}
|
||||||
|
>
|
||||||
|
<box class="bar-shell" orientation={Gtk.Orientation.VERTICAL}>
|
||||||
|
<WorkspacesSection />
|
||||||
|
<MediaSection />
|
||||||
|
<SystemSection />
|
||||||
|
<ConnectivitySection />
|
||||||
|
<WidgetsSection />
|
||||||
|
<box vexpand />
|
||||||
|
<ClockPowerSection />
|
||||||
|
</box>
|
||||||
|
</window>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.start({
|
||||||
|
css: style,
|
||||||
|
gtkTheme: "Adwaita",
|
||||||
|
main() {
|
||||||
|
const [monitor] = app.get_monitors()
|
||||||
|
|
||||||
|
if (!monitor) {
|
||||||
|
throw new Error("No monitor available for AGS bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RightBar gdkmonitor={monitor} />
|
||||||
|
<PowerMenu gdkmonitor={monitor} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
176
modules/home/wayland/apps/ags/src/style.scss
Normal file
176
modules/home/wayland/apps/ags/src/style.scss
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
@use "sass:color";
|
||||||
|
@use "./css/_colors.scss" as *;
|
||||||
|
|
||||||
|
* {
|
||||||
|
color: $fg;
|
||||||
|
font-family: "IBM Plex Sans", "Symbols Nerd Font Mono";
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
window {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-shell {
|
||||||
|
min-width: 42px;
|
||||||
|
padding: 4px 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background: rgba(color.channel($bg1, "red", $space: rgb), color.channel($bg1, "green", $space: rgb), color.channel($bg1, "blue", $space: rgb), 0.92);
|
||||||
|
border: $border-width solid $border-color;
|
||||||
|
border-right: 0;
|
||||||
|
border-radius: $border-radius 0 0 $border-radius;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding: 6px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
min-height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-body {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-stack {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-button,
|
||||||
|
.mini-button,
|
||||||
|
.action-button,
|
||||||
|
.clock-trigger {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
border-radius: calc($border-radius - 2px);
|
||||||
|
box-shadow: none;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-button:hover,
|
||||||
|
.mini-button:hover,
|
||||||
|
.action-button:hover,
|
||||||
|
.clock-trigger:hover {
|
||||||
|
background: rgba(color.channel($base03, "red", $space: rgb), color.channel($base03, "green", $space: rgb), color.channel($base03, "blue", $space: rgb), 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-button.focused {
|
||||||
|
background: rgba(color.channel($base0D, "red", $space: rgb), color.channel($base0D, "green", $space: rgb), color.channel($base0D, "blue", $space: rgb), 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-button.occupied .workspace-icon {
|
||||||
|
color: $base05;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-button.focused .workspace-icon {
|
||||||
|
color: $base0D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-icon,
|
||||||
|
.action-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-controls,
|
||||||
|
.action-row {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-row {
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-row:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon,
|
||||||
|
.status-icon {
|
||||||
|
color: $base0A;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-head,
|
||||||
|
.status-row {
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-bar {
|
||||||
|
margin-top: 3px;
|
||||||
|
min-height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-bar trough {
|
||||||
|
background: rgba(red($base02), green($base02), blue($base02), 0.85);
|
||||||
|
border-radius: 99px;
|
||||||
|
min-height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-bar block.filled {
|
||||||
|
background: $base0D;
|
||||||
|
border-radius: 99px;
|
||||||
|
min-height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-section {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-trigger {
|
||||||
|
padding: 6px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-time {
|
||||||
|
color: $base0E;
|
||||||
|
font-family: "IBM Plex Mono", "Symbols Nerd Font Mono";
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
min-width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-backdrop {
|
||||||
|
background: rgba(color.channel($base00, "red", $space: rgb), color.channel($base00, "green", $space: rgb), color.channel($base00, "blue", $space: rgb), 0.42);
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-menu {
|
||||||
|
background: rgba(color.channel($bg1, "red", $space: rgb), color.channel($bg1, "green", $space: rgb), color.channel($bg1, "blue", $space: rgb), 0.94);
|
||||||
|
border: $border-width solid $border-color-focus;
|
||||||
|
border-radius: 18px;
|
||||||
|
min-width: 320px;
|
||||||
|
padding: 20px 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-weather {
|
||||||
|
color: $base0C;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-calendar {
|
||||||
|
font-family: "IBM Plex Mono", "Symbols Nerd Font Mono";
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-actions {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-actions .action-button {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
{ ... }: { imports = [ ./dunst ./eww ./kanshi ./waylock ./wofi ]; }
|
{ ... }: { imports = [ ./dunst ./eww ./kanshi ./waylock ./wofi ./ags ]; }
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, ... }: {
|
||||||
let
|
|
||||||
restartEwwBar = monitor: pkgs.writeShellScript "restart-eww-bar-after-kanshi-${toString monitor}" ''
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
${lib.getExe pkgs.eww} close bar || true
|
|
||||||
${lib.getExe pkgs.eww} open bar --screen ${toString monitor}
|
|
||||||
'';
|
|
||||||
in {
|
|
||||||
|
|
||||||
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
||||||
services.kanshi = {
|
services.kanshi = {
|
||||||
@@ -15,7 +7,6 @@ in {
|
|||||||
settings = [
|
settings = [
|
||||||
{
|
{
|
||||||
profile.name = "tower_0";
|
profile.name = "tower_0";
|
||||||
profile.exec = [ "${restartEwwBar 1}" ];
|
|
||||||
profile.outputs = [
|
profile.outputs = [
|
||||||
{
|
{
|
||||||
criteria = "AOC 24E1W1 GNSKCHA086899";
|
criteria = "AOC 24E1W1 GNSKCHA086899";
|
||||||
@@ -37,7 +28,6 @@ in {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
profile.name = "tower_1";
|
profile.name = "tower_1";
|
||||||
profile.exec = [ "${restartEwwBar 1}" ];
|
|
||||||
profile.outputs = [
|
profile.outputs = [
|
||||||
{
|
{
|
||||||
criteria = "AOC 24E1W1 GNSKCHA086899";
|
criteria = "AOC 24E1W1 GNSKCHA086899";
|
||||||
@@ -67,7 +57,6 @@ in {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
profile.name = "laptop_0";
|
profile.name = "laptop_0";
|
||||||
profile.exec = [ "${restartEwwBar 0}" ];
|
|
||||||
profile.outputs = [{
|
profile.outputs = [{
|
||||||
criteria = "LG Display 0x060A Unknown";
|
criteria = "LG Display 0x060A Unknown";
|
||||||
mode = "1920x1080@60.020";
|
mode = "1920x1080@60.020";
|
||||||
@@ -78,7 +67,6 @@ in {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
profile.name = "laptop_1";
|
profile.name = "laptop_1";
|
||||||
profile.exec = [ "${restartEwwBar 1}" ];
|
|
||||||
profile.outputs = [
|
profile.outputs = [
|
||||||
{
|
{
|
||||||
criteria = "CEX CX133 0x00000001";
|
criteria = "CEX CX133 0x00000001";
|
||||||
@@ -98,7 +86,6 @@ in {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
profile.name = "laptop_2";
|
profile.name = "laptop_2";
|
||||||
profile.exec = [ "${restartEwwBar 1}" ];
|
|
||||||
profile.outputs = [
|
profile.outputs = [
|
||||||
{
|
{
|
||||||
criteria = "AOC 16G3 1DDP7HA000348";
|
criteria = "AOC 16G3 1DDP7HA000348";
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
|
|
||||||
startupScript = pkgs.writeShellScriptBin "hyprland-start" ''
|
startupScript = pkgs.writeShellScriptBin "hyprland-start" ''
|
||||||
eww-open-on-current-screen bar &
|
ags-run &
|
||||||
awww-daemon &
|
awww-daemon &
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|||||||
12
overlays/ags/default.nix
Normal file
12
overlays/ags/default.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{ final, prev, ... }:
|
||||||
|
prev.ags.overrideAttrs (old: rec {
|
||||||
|
version = "3.1.2";
|
||||||
|
src = prev.fetchFromGitHub {
|
||||||
|
owner = "Aylur";
|
||||||
|
repo = "ags";
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-tM3s7CX+tgxlYW0Sk3nzVThg2MHn08foIuMxABupxIs=";
|
||||||
|
};
|
||||||
|
modRoot = "cli";
|
||||||
|
vendorHash = "sha256-UHMHbUGqJeUTw0AHHyTdQ8ed5z+SFyPcdXs4shC+hoI=";
|
||||||
|
})
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{ final, prev, ... }:
|
|
||||||
prev.bambu-studio.overrideAttrs (oldAttrs: rec{
|
|
||||||
version = "02.00.01.50";
|
|
||||||
src = prev.fetchFromGitHub {
|
|
||||||
owner = "bambulab";
|
|
||||||
repo = "BambuStudio";
|
|
||||||
rev = "v${version}";
|
|
||||||
hash = "sha256-7mkrPl2CQSfc1lRjl1ilwxdYcK5iRU//QGKmdCicK30=";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
#openttd-jgrpp = import ./openttd-jgrpp { inherit final prev; };
|
#openttd-jgrpp = import ./openttd-jgrpp { inherit final prev; };
|
||||||
#yarn-berry = import ./yarn-berry { inherit final prev; };
|
#yarn-berry = import ./yarn-berry { inherit final prev; };
|
||||||
#eww = import ./eww { inherit final prev; };
|
#eww = import ./eww { inherit final prev; };
|
||||||
#bambu-studio = import ./bambu-studio { inherit final prev; };
|
# ags = import ./ags { inherit final prev; };
|
||||||
wine = final.unstable.wineWow64Packages.unstableFull;
|
wine = final.unstable.wineWow64Packages.unstableFull;
|
||||||
unstable = import inputs.nixUnstable {
|
unstable = import inputs.nixUnstable {
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
let old = prev.eww;
|
let old = prev.eww;
|
||||||
in final.rustPlatform.buildRustPackage rec {
|
in final.rustPlatform.buildRustPackage rec {
|
||||||
pname = "eww";
|
pname = "eww";
|
||||||
version = "98c220126d912b935987766f56650b55f3e226eb";
|
version = "865cf631d5bbb5f9fccc99b3f4cc80b9eeada18c";
|
||||||
|
|
||||||
src = prev.fetchFromGitHub {
|
src = prev.fetchFromGitHub {
|
||||||
owner = "elkowar";
|
owner = "elkowar";
|
||||||
repo = "eww";
|
repo = "eww";
|
||||||
rev = "${version}";
|
rev = "${version}";
|
||||||
hash = "sha256-zi+5G05aakh8GBdfHL1qcNo/15VEm5mXtHGgKMAyp1U=";
|
hash = "sha256-zi+5G05aakh8GBdfHL1qcNo/15aEm5mXtHGgKMAyp1U=";
|
||||||
};
|
};
|
||||||
|
|
||||||
cargoHash = "sha256-SEdr9nW5nBm1g6fjC5fZhqPbHQ7H6Kk0RL1V6OEQRdA=";
|
cargoHash = "sha256-SEdr9nW5nBm1gafjC5fZhqPbHQ7H6Kk0RL1V6OEQRdA=";
|
||||||
|
|
||||||
nativeBuildInputs = old.nativeBuildInputs;
|
nativeBuildInputs = old.nativeBuildInputs;
|
||||||
buildInputs = old.buildInputs ++ [ final.libdbusmenu-gtk3 ];
|
buildInputs = old.buildInputs ++ [ final.libdbusmenu-gtk3 ];
|
||||||
|
|||||||
Reference in New Issue
Block a user