This commit is contained in:
Nico
2024-10-31 10:33:46 +01:00
commit ad6d893cb0
237 changed files with 19793 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
import { ArrowToggleButton, Menu } from "../ToggleButton.js"
import icons from "../../../lib/icons.js"
import asusctl from "../../../services/asusctl.js"
const profile = asusctl.bind("profile")
export const ProfileToggle = () => ArrowToggleButton({
name: "asusctl-profile",
icon: profile.as(p => icons.asusctl.profile[p]),
label: profile,
connection: [asusctl, () => asusctl.profile !== "Balanced"],
activate: () => asusctl.setProfile("Quiet"),
deactivate: () => asusctl.setProfile("Balanced"),
activateOnArrow: false,
})
export const ProfileSelector = () => Menu({
name: "asusctl-profile",
icon: profile.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"),
],
}),
}),
],
})

View File

@@ -0,0 +1,62 @@
import { Menu, ArrowToggleButton } from "../ToggleButton.js"
import icons from "../../../lib/icons.js"
import { watch } from "../../../lib/experiments.js"
const bluetooth = await Service.import("bluetooth")
const title = 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`
})
export const BluetoothToggle = () => ArrowToggleButton({
name: "bluetooth",
icon: bluetooth.bind("enabled").as(p => icons.bluetooth[p ? "enabled" : "disabled"]),
label: title,
connection: [bluetooth, () => bluetooth.enabled],
deactivate: () => bluetooth.enabled = false,
activate: () => bluetooth.enabled = true,
})
const DeviceItem = (device) => 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.js"
import brightness from "../../../services/brightness.js"
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.js"
import icons from "../../../lib/icons.js"
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,10 @@
import { SimpleToggleButton } from "../ToggleButton.js"
import icons from "../../../lib/icons.js"
let scheme = "dark"
export const DarkModeToggle = () => SimpleToggleButton({
icon: icons.color[scheme],
label: scheme === "dark" ? "Dark" : "Light",
toggle: () => scheme = scheme === "dark" ? "light" : "dark",
})

View File

@@ -0,0 +1,60 @@
import icons from "../../../lib/icons.js"
import { uptime } from "../../../lib/variables.js"
import options from "../../../options.js"
import powermenu from "../../../services/powermenu.js"
const battery = await Service.import("battery")
const { image, size } = options.quicksettings.avatar
function up(up) {
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: `
min-width: ${size}px;
min-height: ${size}px;
background-image: url('${image}');
background-size: cover;
`,
})
const SysButton = (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([
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,148 @@
import icons from "../../../lib/icons.js"
import options from "../../../options.js"
import { icon } from "../../../lib/utils.js"
const mpris = await Service.import("mpris")
const players = mpris.bind("players")
const { media } = options.quicksettings
function lengthStr(length) {
const min = Math.floor(length / 60)
const sec = Math.floor(length % 60)
const sec0 = sec < 10 ? "0" : ""
return `${min}:${sec0}${sec}`
}
const Player = (player) => {
const cover = Widget.Box({
class_name: "cover",
vpack: "start",
css: player.bind("cover_path").as(path => `
min-width: ${media.coverSize}px;
min-height: ${media.coverSize}px;
background-image: url('${path}');
`),
})
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 = (_, time) => {
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: player.bind("entry").as(e => {
const name = `${e}${media.monochromeIcon ? "-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,26 @@
import { SimpleToggleButton } from "../ToggleButton.js"
import icons from "../../../lib/icons.js"
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"
// TODO: Variable watch option
const ico = Variable(icon())
microphone.connect("changed", () => ico.value = icon())
// TODO: Variable watch option
const lbl = Variable(label())
microphone.connect("changed", () => lbl.value = label())
export const MicMute = () => SimpleToggleButton({
icon: ico.bind(),
label: lbl.bind(),
toggle: () => microphone.is_muted = !microphone.is_muted,
connection: [microphone, () => microphone?.is_muted || false],
})

View File

@@ -0,0 +1,61 @@
import { Menu, ArrowToggleButton } from "../ToggleButton.js"
import icons from "../../../lib/icons.js"
import { dependencies, sh } from "../../../lib/utils.js"
import options from "../../../options.js"
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),
child: Widget.Box({
children: [
Widget.Icon(icons.ui.settings),
Widget.Label("Network"),
],
}),
}),
],
})

View File

@@ -0,0 +1,141 @@
import { Arrow, Menu } from "../ToggleButton.js"
import { dependencies, icon, sh } from "../../../lib/utils.js"
import icons from "../../../lib/icons.js"
const audio = await Service.import("audio")
const VolumeIndicator = (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 = "speaker") => Widget.Slider({
hexpand: true,
draw_value: false,
on_change: ({ value, dragging }) => dragging && (audio[type].volume = value),
value: audio[type].bind("volume"),
})
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) => 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) => 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(),
],
})