import app from "ags/gtk3/app" import { Astal, Gdk, Gtk } from "ags/gtk3" import { createState } from "ags" import { createPoll, interval } from "ags/time" import { sh, shell } from "../lib/utils" import { ClaudeUsageCard } from "./ClaudeUsage" import { CardHeader, StatRow } from "./WidgetCard" export const QUICKSETTINGS_WINDOW = "quick-settings" const closePanel = () => app.toggle_window(QUICKSETTINGS_WINDOW) function pollBool(command: string, intervalMs: number) { const [value, setValue] = createState(false) const refresh = () => sh(command).then((out) => setValue(out.trim() === "on")) refresh() interval(intervalMs, refresh) return [value, setValue] as const } const volume = createPoll( 0, 1000, shell("pamixer --get-volume 2>/dev/null || echo 0"), (stdout) => Number(stdout.trim()) || 0, ) const [muted, setMuted] = pollBool( "pamixer --get-mute 2>/dev/null | grep -qx true && echo on || echo off", 1000, ) const brightness = createPoll( 0, 2000, shell("brightnessctl -m 2>/dev/null | cut -d, -f4 | tr -d '%' || echo 0"), (stdout) => Number(stdout.trim()) || 0, ) const [bluetoothOn] = pollBool( "bluetoothctl show 2>/dev/null | grep -q 'Powered: yes' && echo on || echo off", 2000, ) const [doNotDisturb, setDoNotDisturb] = pollBool( "omarchy-shell notifications dndState 2>/dev/null", 2000, ) const [nightLight, setNightLight] = pollBool( "omarchy-toggle-nightlight --status 2>/dev/null | grep -q '\"enabled\":true' && echo on || echo off", 2000, ) const [stayAwake, setStayAwake] = pollBool( "omarchy-toggle-idle status 2>/dev/null | grep -q '\"enabled\":true' && echo on || echo off", 2000, ) const [recording] = pollBool( "pgrep -f '^gpu-screen-recorder' >/dev/null && echo on || echo off", 2000, ) const clock = createPoll("", 1000, shell("date '+%H:%M'"), (stdout) => stdout.trim()) const today = createPoll("", 10000, shell("date '+%A, %B %-d'"), (stdout) => stdout.trim()) type Media = { title: string artist: string artUrl: string status: string length: number position: number } const mediaCommand = "playerctl metadata -f '{{title}}|||{{artist}}|||{{mpris:artUrl}}|||{{status}}|||{{mpris:length}}|||{{position}}' " + "2>/dev/null || echo 'No Media||||||Stopped|||0|||0'" const media = createPoll( { title: "No Media", artist: "", artUrl: "", status: "Stopped", length: 0, position: 0 }, 1000, shell(mediaCommand), (stdout) => { const parts = stdout.split("|||") return { title: parts[0]?.trim() || "No Media", artist: parts[1]?.trim() || "", artUrl: (parts[2]?.trim() || "").replace(/^file:\/\//, ""), status: parts[3]?.trim() || "Stopped", length: Number(parts[4]) || 0, position: Number(parts[5]) || 0, } }, ) function Header() { return ( ) } function Tile({ icon, label, active, tooltip, onClicked, }: { icon: string label: string active?: any tooltip?: any onClicked: () => void }) { return ( ) } function Toggles() { return ( { closePanel(); sh("omarchy-shell shell toggle omarchy.network") }} /> { closePanel() sh("omarchy-shell shell toggle omarchy.bluetooth") }} /> { closePanel() sh("blob_wallpaper") }} /> { setDoNotDisturb(!doNotDisturb.get()) sh("omarchy-shell notifications toggleDnd") }} /> { setNightLight(!nightLight.get()) sh("omarchy-toggle-nightlight") }} /> (on ? "Stop screen recording" : "Start a screen recording"))} onClicked={() => { closePanel() sh(recording.get() ? "omarchy-capture-screenrecording --stop-recording" : "omarchy-menu toggle trigger.capture.screenrecord") }} /> (on ? "Allow idle lock and screensaver" : "Keep the screen awake"))} onClicked={() => { setStayAwake(!stayAwake.get()) sh("omarchy-toggle-idle toggle") }} /> { closePanel(); sh("hyprpicker -a") }} /> { closePanel() sh("blob_theme") }} /> ) } function VolumeSlider() { return ( `Volume ${Math.round(level)}%`)} hexpand min={0} max={100} step={1} value={volume} $={(self) => { self.connect("value-changed", () => { if (self.dragging) { sh(`pamixer --set-volume ${Math.round(self.value)}`) } }) }} /> ) } function BrightnessSlider() { return ( ) } function MediaPlayer() { return ( m.title !== "No Media")}> m.artUrl ? `background-image: url('${m.artUrl}');` : "background-color: alpha(@color0, 0.5);", )} /> ) } const [monthOffset, setMonthOffset] = createState(0) const [monthName, setMonthName] = createState("") const [monthGrid, setMonthGrid] = createState("") const loadMonth = (offset: number) => { const firstOfMonth = `$(date +%Y-%m-01) ${offset} months` const command = `date -d "${firstOfMonth}" '+%B %Y'; ` + `cal $(date -d "${firstOfMonth}" '+%m %Y') | sed '1d'` sh(command).then((stdout) => { const lines = String(stdout).replace(/\s+$/, "").split("\n") setMonthName(lines[0]?.trim() ?? "") setMonthGrid(lines.slice(1).join("\n")) }) } const shiftMonth = (delta: number) => { const next = monthOffset.get() + delta setMonthOffset(next) loadMonth(next) } const resetMonth = () => { setMonthOffset(0) loadMonth(0) } loadMonth(0) interval(3600000, () => { if (monthOffset.get() === 0) loadMonth(0) }) function CalendarSection() { return ( { const direction = event?.direction const deltaY = Number(event?.delta_y ?? 0) if (direction === Gdk.ScrollDirection.UP || deltaY < 0) shiftMonth(-1) else if (direction === Gdk.ScrollDirection.DOWN || deltaY > 0) shiftMonth(1) }} > ) } type Weather = { ok: boolean; icon: string; place: string; temp: string; wind: string } function parseWeather(raw: string): Weather { const parts = raw.split(" · ") if (parts.length < 3) return { ok: false, icon: "", place: raw, temp: "", wind: "" } const match = parts[0].match(/^(\S+)\s*(.*)$/) return { ok: true, icon: match ? match[1] : "", place: match ? match[2].trim() : parts[0].trim(), temp: parts[1].replace(/^Temp\s*/, ""), wind: parts[2].replace(/^Wind\s*/, ""), } } const weather = createPoll( { ok: false, icon: "", place: "Loading...", temp: "", wind: "" }, 600000, shell("bash $HOME/.config/ags/lib/weather.sh"), (stdout) => parseWeather(stdout.trim()), ) function WeatherCard() { return ( w.icon)} title={weather.as((w) => w.place)} /> ) } function LeftPanel() { return ( ) } function RightPanel() { return ( ) } export default function QuickSettings() { const { TOP } = Astal.WindowAnchor return (
) }