Added ags

TODO copy ags config
This commit is contained in:
2024-04-16 00:28:26 +02:00
parent be8fbb5d9f
commit 8aa6bcd5cb
125 changed files with 8130 additions and 113 deletions

View File

@@ -0,0 +1,61 @@
import { type BluetoothDevice } from "types/service/bluetooth"
import { Menu, ArrowToggleButton } from "../ToggleButton"
import icons from "lib/icons"
const bluetooth = await Service.import("bluetooth")
export const BluetoothToggle = () => ArrowToggleButton({
name: "bluetooth",
icon: bluetooth.bind("enabled").as(p => icons.bluetooth[p ? "enabled" : "disabled"]),
label: Utils.watch("Disabled", bluetooth, () => {
if (!bluetooth.enabled)
return "Disabled"
if (bluetooth.connected_devices.length === 1)
return bluetooth.connected_devices[0].alias
return `${bluetooth.connected_devices.length} Connected`
}),
connection: [bluetooth, () => bluetooth.enabled],
deactivate: () => bluetooth.enabled = false,
activate: () => bluetooth.enabled = true,
})
const DeviceItem = (device: BluetoothDevice) => Widget.Box({
children: [
Widget.Icon(device.icon_name + "-symbolic"),
Widget.Label(device.name),
Widget.Label({
label: `${device.battery_percentage}%`,
visible: device.bind("battery_percentage").as(p => p > 0),
}),
Widget.Box({ hexpand: true }),
Widget.Spinner({
active: device.bind("connecting"),
visible: device.bind("connecting"),
}),
Widget.Switch({
active: device.connected,
visible: device.bind("connecting").as(p => !p),
setup: self => self.on("notify::active", () => {
device.setConnection(self.active)
}),
}),
],
})
export const BluetoothDevices = () => Menu({
name: "bluetooth",
icon: icons.bluetooth.disabled,
title: "Bluetooth",
content: [
Widget.Box({
class_name: "bluetooth-devices",
hexpand: true,
vertical: true,
children: bluetooth.bind("devices").as(ds => ds
.filter(d => d.name)
.map(DeviceItem)),
}),
],
})

View File

@@ -0,0 +1,23 @@
import icons from "lib/icons"
import brightness from "service/brightness"
const BrightnessSlider = () => Widget.Slider({
draw_value: false,
hexpand: true,
value: brightness.bind("screen"),
on_change: ({ value }) => brightness.screen = value,
})
export const Brightness = () => Widget.Box({
class_name: "brightness",
children: [
Widget.Button({
vpack: "center",
child: Widget.Icon(icons.brightness.indicator),
on_clicked: () => brightness.screen = 0,
tooltip_text: brightness.bind("screen").as(v =>
`Screen Brightness: ${Math.floor(v * 100)}%`),
}),
BrightnessSlider(),
],
})

View File

@@ -0,0 +1,12 @@
import { SimpleToggleButton } from "../ToggleButton"
import icons from "lib/icons"
const n = await Service.import("notifications")
const dnd = n.bind("dnd")
export const DND = () => SimpleToggleButton({
icon: dnd.as(dnd => icons.notifications[dnd ? "silent" : "noisy"]),
label: dnd.as(dnd => dnd ? "Silent" : "Noisy"),
toggle: () => n.dnd = !n.dnd,
connection: [n, () => n.dnd],
})

View File

@@ -0,0 +1,12 @@
import { SimpleToggleButton } from "../ToggleButton"
import icons from "lib/icons"
import options from "options"
const { scheme } = options.theme
export const DarkModeToggle = () => SimpleToggleButton({
icon: scheme.bind().as(s => icons.color[s]),
label: scheme.bind().as(s => s === "dark" ? "Dark" : "Light"),
toggle: () => scheme.value = scheme.value === "dark" ? "light" : "dark",
connection: [scheme, () => scheme.value === "dark"],
})

View File

@@ -0,0 +1,63 @@
import icons from "lib/icons"
import { uptime } from "lib/variables"
import options from "options"
import powermenu, { Action } from "service/powermenu"
const battery = await Service.import("battery")
const { image, size } = options.quicksettings.avatar
function up(up: number) {
const h = Math.floor(up / 60)
const m = Math.floor(up % 60)
return `${h}h ${m < 10 ? "0" + m : m}m`
}
const Avatar = () => Widget.Box({
class_name: "avatar",
css: Utils.merge([image.bind(), size.bind()], (img, size) => `
min-width: ${size}px;
min-height: ${size}px;
background-image: url('${img}');
background-size: cover;
`),
})
const SysButton = (action: Action) => Widget.Button({
vpack: "center",
child: Widget.Icon(icons.powermenu[action]),
on_clicked: () => powermenu.action(action),
})
export const Header = () => Widget.Box(
{ class_name: "header horizontal" },
Avatar(),
Widget.Box({
vertical: true,
vpack: "center",
children: [
Widget.Box({
visible: battery.bind("available"),
children: [
Widget.Icon({ icon: battery.bind("icon_name") }),
Widget.Label({ label: battery.bind("percent").as(p => `${p}%`) }),
],
}),
Widget.Box([
Widget.Icon({ icon: icons.ui.time }),
Widget.Label({ label: uptime.bind().as(up) }),
]),
],
}),
Widget.Box({ hexpand: true }),
Widget.Button({
vpack: "center",
child: Widget.Icon(icons.ui.settings),
on_clicked: () => {
App.closeWindow("quicksettings")
App.closeWindow("settings-dialog")
App.openWindow("settings-dialog")
},
}),
SysButton("logout"),
SysButton("shutdown"),
)

View File

@@ -0,0 +1,153 @@
import { type MprisPlayer } from "types/service/mpris"
import icons from "lib/icons"
import options from "options"
import { icon } from "lib/utils"
const mpris = await Service.import("mpris")
const players = mpris.bind("players")
const { media } = options.quicksettings
function lengthStr(length: number) {
const min = Math.floor(length / 60)
const sec = Math.floor(length % 60)
const sec0 = sec < 10 ? "0" : ""
return `${min}:${sec0}${sec}`
}
const Player = (player: MprisPlayer) => {
const cover = Widget.Box({
class_name: "cover",
vpack: "start",
css: Utils.merge([
player.bind("cover_path"),
player.bind("track_cover_url"),
media.coverSize.bind(),
], (path, url, size) => `
min-width: ${size}px;
min-height: ${size}px;
background-image: url('${path || url}');
`),
})
const title = Widget.Label({
class_name: "title",
max_width_chars: 20,
truncate: "end",
hpack: "start",
label: player.bind("track_title"),
})
const artist = Widget.Label({
class_name: "artist",
max_width_chars: 20,
truncate: "end",
hpack: "start",
label: player.bind("track_artists").as(a => a.join(", ")),
})
const positionSlider = Widget.Slider({
class_name: "position",
draw_value: false,
on_change: ({ value }) => player.position = value * player.length,
setup: self => {
const update = () => {
const { length, position } = player
self.visible = length > 0
self.value = length > 0 ? position / length : 0
}
self.hook(player, update)
self.hook(player, update, "position")
self.poll(1000, update)
},
})
const positionLabel = Widget.Label({
class_name: "position",
hpack: "start",
setup: self => {
const update = (_: unknown, time?: number) => {
self.label = lengthStr(time || player.position)
self.visible = player.length > 0
}
self.hook(player, update, "position")
self.poll(1000, update)
},
})
const lengthLabel = Widget.Label({
class_name: "length",
hpack: "end",
visible: player.bind("length").as(l => l > 0),
label: player.bind("length").as(lengthStr),
})
const playericon = Widget.Icon({
class_name: "icon",
hexpand: true,
hpack: "end",
vpack: "start",
tooltip_text: player.identity || "",
icon: Utils.merge([player.bind("entry"), media.monochromeIcon.bind()], (e, s) => {
const name = `${e}${s ? "-symbolic" : ""}`
return icon(name, icons.fallback.audio)
}),
})
const playPause = Widget.Button({
class_name: "play-pause",
on_clicked: () => player.playPause(),
visible: player.bind("can_play"),
child: Widget.Icon({
icon: player.bind("play_back_status").as(s => {
switch (s) {
case "Playing": return icons.mpris.playing
case "Paused":
case "Stopped": return icons.mpris.stopped
}
}),
}),
})
const prev = Widget.Button({
on_clicked: () => player.previous(),
visible: player.bind("can_go_prev"),
child: Widget.Icon(icons.mpris.prev),
})
const next = Widget.Button({
on_clicked: () => player.next(),
visible: player.bind("can_go_next"),
child: Widget.Icon(icons.mpris.next),
})
return Widget.Box(
{ class_name: "player", vexpand: false },
cover,
Widget.Box(
{ vertical: true },
Widget.Box([
title,
playericon,
]),
artist,
Widget.Box({ vexpand: true }),
positionSlider,
Widget.CenterBox({
class_name: "footer horizontal",
start_widget: positionLabel,
center_widget: Widget.Box([
prev,
playPause,
next,
]),
end_widget: lengthLabel,
}),
),
)
}
export const Media = () => Widget.Box({
vertical: true,
class_name: "media vertical",
children: players.as(p => p.map(Player)),
})

View File

@@ -0,0 +1,18 @@
import { SimpleToggleButton } from "../ToggleButton"
import icons from "lib/icons"
const { microphone } = await Service.import("audio")
const icon = () => microphone.is_muted || microphone.stream?.is_muted
? icons.audio.mic.muted
: icons.audio.mic.high
const label = () => microphone.is_muted || microphone.stream?.is_muted
? "Muted"
: "Unmuted"
export const MicMute = () => SimpleToggleButton({
icon: Utils.watch(icon(), microphone, icon),
label: Utils.watch(label(), microphone, label),
toggle: () => microphone.is_muted = !microphone.is_muted,
connection: [microphone, () => microphone?.is_muted || false],
})

View File

@@ -0,0 +1,61 @@
import { Menu, ArrowToggleButton } from "../ToggleButton"
import icons from "lib/icons.js"
import { dependencies, sh } from "lib/utils"
import options from "options"
const { wifi } = await Service.import("network")
export const NetworkToggle = () => ArrowToggleButton({
name: "network",
icon: wifi.bind("icon_name"),
label: wifi.bind("ssid").as(ssid => ssid || "Not Connected"),
connection: [wifi, () => wifi.enabled],
deactivate: () => wifi.enabled = false,
activate: () => {
wifi.enabled = true
wifi.scan()
},
})
export const WifiSelection = () => Menu({
name: "network",
icon: wifi.bind("icon_name"),
title: "Wifi Selection",
content: [
Widget.Box({
vertical: true,
setup: self => self.hook(wifi, () => self.children =
wifi.access_points.map(ap => Widget.Button({
on_clicked: () => {
if (dependencies("nmcli"))
Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`)
},
child: Widget.Box({
children: [
Widget.Icon(ap.iconName),
Widget.Label(ap.ssid || ""),
Widget.Icon({
icon: icons.ui.tick,
hexpand: true,
hpack: "end",
setup: self => Utils.idle(() => {
if (!self.is_destroyed)
self.visible = ap.active
}),
}),
],
}),
})),
),
}),
Widget.Separator(),
Widget.Button({
on_clicked: () => sh(options.quicksettings.networkSettings.value),
child: Widget.Box({
children: [
Widget.Icon(icons.ui.settings),
Widget.Label("Network"),
],
}),
}),
],
})

View File

@@ -0,0 +1,99 @@
import { ArrowToggleButton, Menu } from "../ToggleButton"
import icons from "lib/icons"
import asusctl from "service/asusctl"
const asusprof = asusctl.bind("profile")
const AsusProfileToggle = () => ArrowToggleButton({
name: "asusctl-profile",
icon: asusprof.as(p => icons.asusctl.profile[p]),
label: asusprof,
connection: [asusctl, () => asusctl.profile !== "Balanced"],
activate: () => asusctl.setProfile("Quiet"),
deactivate: () => asusctl.setProfile("Balanced"),
activateOnArrow: false,
})
const AsusProfileSelector = () => Menu({
name: "asusctl-profile",
icon: asusprof.as(p => icons.asusctl.profile[p]),
title: "Profile Selector",
content: [
Widget.Box({
vertical: true,
hexpand: true,
children: [
Widget.Box({
vertical: true,
children: asusctl.profiles.map(prof => Widget.Button({
on_clicked: () => asusctl.setProfile(prof),
child: Widget.Box({
children: [
Widget.Icon(icons.asusctl.profile[prof]),
Widget.Label(prof),
],
}),
})),
}),
],
}),
Widget.Separator(),
Widget.Button({
on_clicked: () => Utils.execAsync("rog-control-center"),
child: Widget.Box({
children: [
Widget.Icon(icons.ui.settings),
Widget.Label("Rog Control Center"),
],
}),
}),
],
})
const pp = await Service.import("powerprofiles")
const profile = pp.bind("active_profile")
const profiles = pp.profiles.map(p => p.Profile)
const pretty = (str: string) => str
.split("-")
.map(str => `${str.at(0)?.toUpperCase()}${str.slice(1)}`)
.join(" ")
const PowerProfileToggle = () => ArrowToggleButton({
name: "asusctl-profile",
icon: profile.as(p => icons.powerprofile[p]),
label: profile.as(pretty),
connection: [pp, () => pp.active_profile !== profiles[1]],
activate: () => pp.active_profile = profiles[0],
deactivate: () => pp.active_profile = profiles[1],
activateOnArrow: false,
})
const PowerProfileSelector = () => Menu({
name: "asusctl-profile",
icon: profile.as(p => icons.powerprofile[p]),
title: "Profile Selector",
content: [Widget.Box({
vertical: true,
hexpand: true,
child: Widget.Box({
vertical: true,
children: profiles.map(prof => Widget.Button({
on_clicked: () => pp.active_profile = prof,
child: Widget.Box({
children: [
Widget.Icon(icons.powerprofile[prof]),
Widget.Label(pretty(prof)),
],
}),
})),
}),
})],
})
export const ProfileToggle = asusctl.available
? AsusProfileToggle : PowerProfileToggle
export const ProfileSelector = asusctl.available
? AsusProfileSelector : PowerProfileSelector

View File

@@ -0,0 +1,150 @@
import { type Stream } from "types/service/audio"
import { Arrow, Menu } from "../ToggleButton"
import { dependencies, icon, sh } from "lib/utils"
import icons from "lib/icons.js"
const audio = await Service.import("audio")
type Type = "microphone" | "speaker"
const VolumeIndicator = (type: Type = "speaker") => Widget.Button({
vpack: "center",
on_clicked: () => audio[type].is_muted = !audio[type].is_muted,
child: Widget.Icon({
icon: audio[type].bind("icon_name")
.as(i => icon(i || "", icons.audio.mic.high)),
tooltipText: audio[type].bind("volume")
.as(vol => `Volume: ${Math.floor(vol * 100)}%`),
}),
})
const VolumeSlider = (type: Type = "speaker") => Widget.Slider({
hexpand: true,
draw_value: false,
on_change: ({ value, dragging }) => {
if (dragging) {
audio[type].volume = value
audio[type].is_muted = false
}
},
value: audio[type].bind("volume"),
class_name: audio[type].bind("is_muted").as(m => m ? "muted" : ""),
})
export const Volume = () => Widget.Box({
class_name: "volume",
children: [
VolumeIndicator("speaker"),
VolumeSlider("speaker"),
Widget.Box({
vpack: "center",
child: Arrow("sink-selector"),
}),
Widget.Box({
vpack: "center",
child: Arrow("app-mixer"),
visible: audio.bind("apps").as(a => a.length > 0),
}),
],
})
export const Microhone = () => Widget.Box({
class_name: "slider horizontal",
visible: audio.bind("recorders").as(a => a.length > 0),
children: [
VolumeIndicator("microphone"),
VolumeSlider("microphone"),
],
})
const MixerItem = (stream: Stream) => Widget.Box(
{
hexpand: true,
class_name: "mixer-item horizontal",
},
Widget.Icon({
tooltip_text: stream.bind("name").as(n => n || ""),
icon: stream.bind("name").as(n => {
return Utils.lookUpIcon(n || "")
? (n || "")
: icons.fallback.audio
}),
}),
Widget.Box(
{ vertical: true },
Widget.Label({
xalign: 0,
truncate: "end",
max_width_chars: 28,
label: stream.bind("description").as(d => d || ""),
}),
Widget.Slider({
hexpand: true,
draw_value: false,
value: stream.bind("volume"),
on_change: ({ value }) => stream.volume = value,
}),
),
)
const SinkItem = (stream: Stream) => Widget.Button({
hexpand: true,
on_clicked: () => audio.speaker = stream,
child: Widget.Box({
children: [
Widget.Icon({
icon: icon(stream.icon_name || "", icons.fallback.audio),
tooltip_text: stream.icon_name || "",
}),
Widget.Label((stream.description || "").split(" ").slice(0, 4).join(" ")),
Widget.Icon({
icon: icons.ui.tick,
hexpand: true,
hpack: "end",
visible: audio.speaker.bind("stream").as(s => s === stream.stream),
}),
],
}),
})
const SettingsButton = () => Widget.Button({
on_clicked: () => {
if (dependencies("pavucontrol"))
sh("pavucontrol")
},
hexpand: true,
child: Widget.Box({
children: [
Widget.Icon(icons.ui.settings),
Widget.Label("Settings"),
],
}),
})
export const AppMixer = () => Menu({
name: "app-mixer",
icon: icons.audio.mixer,
title: "App Mixer",
content: [
Widget.Box({
vertical: true,
class_name: "vertical mixer-item-box",
children: audio.bind("apps").as(a => a.map(MixerItem)),
}),
Widget.Separator(),
SettingsButton(),
],
})
export const SinkSelector = () => Menu({
name: "sink-selector",
icon: icons.audio.type.headset,
title: "Sink Selector",
content: [
Widget.Box({
vertical: true,
children: audio.bind("speakers").as(a => a.map(SinkItem)),
}),
Widget.Separator(),
SettingsButton(),
],
})