diff --git a/app.ts b/app.ts index eef20f8..40c1804 100644 --- a/app.ts +++ b/app.ts @@ -1,22 +1,26 @@ import style from "./styles/style.scss" -import { SHELL } from "./settings.json"; import { GLib, monitorFile, exec } from "astal"; -import { App, Gtk } from "astal/gtk4" +import { App, Gtk } from "astal/gtk4"; import Hyprland from "gi://AstalHyprland"; import QuickSettings from "widget/quick_settings/quick_settings"; -import BluetoothWindow from "./widget/quick_settings/bluetooth"; - +import BluetoothWindow from "widget/quick_settings/bluetooth"; import Launcher from "widget/launcher/launcher"; +import BatteryInfo from "@/widget/bar/battery_info"; import Bar from "widget/bar/bar"; const hypr = Hyprland.get_default(); const windows = new Map(); +const components = [ + Bar, + Launcher, + QuickSettings, + BluetoothWindow, + BatteryInfo +]; const setupBars = async (monitor_id: number) => { - const components = [Bar, Launcher, QuickSettings, BluetoothWindow]; - const windows = await Promise.all( components.map(item => Promise.resolve(item(monitor_id)) as Promise) ); @@ -26,7 +30,7 @@ const setupBars = async (monitor_id: number) => { const STYLES = `${GLib.get_user_config_dir()}/ags/styles`; const monitorCSS = () => monitorFile( - STYLES + '/colors/index.scss', + STYLES + '/colors.scss', () => { exec(`sass ${STYLES}/style.scss /tmp/ags-style.css`); App.apply_css('/tmp/ags-style.css', true); diff --git a/lib/icons.ts b/lib/icons.ts new file mode 100644 index 0000000..3e30ea8 --- /dev/null +++ b/lib/icons.ts @@ -0,0 +1,17 @@ +import { Binding } from "astal"; + +type Substitution = { charging?: string, idle?: string } + +export const getBatteryIcon = (percentage: number, charging: Binding) => { + const levels = Array.from({ length: 10 }, (_, i) => (i + 1) * 10); + const level = levels.find((level) => percentage <= level)!; + + const substitutions: Record = { + 100: { charging: "battery-level-100-charged-symbolic" } + }; + + return charging.as(c => c + ? substitutions[level]?.charging || `battery-level-${level}-charging-symbolic` + : substitutions[level]?.idle || `battery-level-${level}-symbolic` + ); +}; \ No newline at end of file diff --git a/lib/icons.tsx b/lib/icons.tsx deleted file mode 100644 index 6366fbe..0000000 --- a/lib/icons.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export default { - battery: { - charging: "battery-symbolic", - discharging: "battery-caution-symbolic" - } -}; diff --git a/lib/utils.ts b/lib/utils.ts index 2196b22..1c58da2 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -3,10 +3,18 @@ import { Gtk, Gdk } from "astal/gtk4"; export type If = Condition extends true ? Then : Else; export type Belongs = { [K in keyof T]: T[K] extends U ? K : never; -}[keyof T]; +}[keyof T]; export const hideWindow = (self: Gtk.Window, keyval: number) => { - if (keyval === Gdk.KEY_Escape) self.hide(); + const keys = ["quick_settings"].includes(self.name) + ? [Gdk.KEY_Escape] + : [ + Gdk.KEY_Escape, + Gdk.KEY_Super_L, + Gdk.KEY_Super_R + ]; + + if (keys.some(key => key === keyval)) self.hide(); } export const openOnButton = (event: Gdk.ButtonEvent, keyval: number) => (action: () => void) => { diff --git a/styles/colors.scss b/styles/colors.scss new file mode 100644 index 0000000..01a49a1 --- /dev/null +++ b/styles/colors.scss @@ -0,0 +1,26 @@ +// SCSS Variables +// Generated by 'wal' +$wallpaper: "/home/sadorowo/images/wallpapers/youtube_Sheri_62.jpg"; + +// Special +$background: #f2eff5; +$foreground: #040404; +$cursor: #040404; + +// Colors +$color0: #f2eff5; +$color1: #463C8B; +$color2: #335092; +$color3: #504E90; +$color4: #695F9E; +$color5: #93689E; +$color6: #9D77B9; +$color7: #040404; +$color8: #655974; +$color9: #463C8B; +$color10: #335092; +$color11: #504E90; +$color12: #695F9E; +$color13: #93689E; +$color14: #9D77B9; +$color15: #040404; diff --git a/styles/colors/index.scss b/styles/colors/index.scss deleted file mode 100644 index a63f338..0000000 --- a/styles/colors/index.scss +++ /dev/null @@ -1,52 +0,0 @@ -// SCSS Variables -// Generated by 'wal' -$wallpaper: "/home/sadorowo/images/wallpapers/youtube_Sheri_36.jpg"; - -// Special -$background: #0D0809; -$foreground: #b5b4b9; -$cursor: #b5b4b9; - -// Colors -$color0: #0D0809; -$color1: #655250; -$color2: #975334; -$color3: #9A6854; -$color4: #C96F45; -$color5: #B18970; -$color6: #EC9863; -$color7: #b5b4b9; -$color8: #7e7d81; -$color9: #655250; -$color10: #975334; -$color11: #9A6854; -$color12: #C96F45; -$color13: #B18970; -$color14: #EC9863; -$color15: #b5b4b9; -$primary: lighten($color4, 20%); -$onPrimary: darken($color2, 20%); -$primaryContainer: darken($color2, 10%); -$onPrimaryContainer: lighten($color4, 10%); -$secondary: desaturate(lighten($color5, 20%), 20%); -$onSecondary: desaturate(darken($color3, 20%), 20%); -$secondaryContainer: desaturate(darken($color3, 20%), 20%); -$onSecondaryContainer: desaturate(lighten($color5, 20%), 20%); -$tertiary: adjust-hue(lighten($color4, 20%), 30deg); -$onTertiary: adjust-hue(darken($color2, 20%), 30deg); -$tertiaryContainer: adjust-hue(darken($color2, 10%), 30deg); -$tertiaryContainer: adjust-hue(lighten($color4, 10%), 30deg); -$error: #ffb4a9; -$onError: #680003; -$errorContainer: #930006; -$onErrorContainer: #ffb4a9; -$background: $color0; -$onBackground: $color7; -$surface: $color0; -$onSurface: $color7; -$surfaceVariant: $color1; -$onSurfaceVariant: $color7; -$outline: $color7; -$inverseSurface: invert($surface); -$inverseOnSurface: invert($onSurface); -$inversePrimary: invert($primary); diff --git a/styles/colors/material.scss b/styles/colors/material.scss deleted file mode 100644 index aedf100..0000000 --- a/styles/colors/material.scss +++ /dev/null @@ -1,26 +0,0 @@ -$primary: lighten($color4, 20%); -$onPrimary: darken($color2, 20%); -$primaryContainer: darken($color2, 10%); -$onPrimaryContainer: lighten($color4, 10%); -$secondary: desaturate(lighten($color5, 20%), 20%); -$onSecondary: desaturate(darken($color3, 20%), 20%); -$secondaryContainer: desaturate(darken($color3, 20%), 20%); -$onSecondaryContainer: desaturate(lighten($color5, 20%), 20%); -$tertiary: adjust-hue(lighten($color4, 20%), 30deg); -$onTertiary: adjust-hue(darken($color2, 20%), 30deg); -$tertiaryContainer: adjust-hue(darken($color2, 10%), 30deg); -$tertiaryContainer: adjust-hue(lighten($color4, 10%), 30deg); -$error: #ffb4a9; -$onError: #680003; -$errorContainer: #930006; -$onErrorContainer: #ffb4a9; -$background: $color0; -$onBackground: $color7; -$surface: $color0; -$onSurface: $color7; -$surfaceVariant: $color1; -$onSurfaceVariant: $color7; -$outline: $color7; -$inverseSurface: invert($surface); -$inverseOnSurface: invert($onSurface); -$inversePrimary: invert($primary); diff --git a/styles/components/_bar.scss b/styles/components/_bar.scss index 4156b8a..109de5a 100644 --- a/styles/components/_bar.scss +++ b/styles/components/_bar.scss @@ -1,7 +1,6 @@ @use '../colors' as *; window.bar { - color: $onPrimary; font-weight: bold; >box { @@ -11,21 +10,23 @@ window.bar { >.inner { border-bottom-left-radius: 0; border-bottom-right-radius: 0; - background: $primary; + background: $color5; padding: 0.25rem 1rem; margin: 0; - * { color: $onPrimary; } + * { color: $color0; } } .tray .item { padding: 0; margin: 0; - background: $primary; + background: $color5; image { padding: 0; margin: 0; } } + + calendar>header { border: none; } } diff --git a/styles/components/_battery_info.scss b/styles/components/_battery_info.scss new file mode 100644 index 0000000..51f1aec --- /dev/null +++ b/styles/components/_battery_info.scss @@ -0,0 +1,56 @@ +@use '../colors' as *; + +window.battery_info { + font-weight: bold; + + >box { + border: 1px solid $color0; + border-radius: 10px; + margin: 8px; + } + + >.inner { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + background: $color5; + padding: 0.75rem 3rem; + margin: 0; + + label.percentage { + font-size: 300%; + } + + * { + transition: all 0.2s ease-in-out; + color: $color0; + } + } + + button { + padding: 0.75rem 1rem; + } +} + +window.battery_info button { + transition: all 0.2s ease-in-out; + background: $color5; + border: 2px solid $color0; + margin: 5px; + + &:hover { + background: $color0; + + * { + color: $color5; + } + } + + &.active { + background: $color0; + border: 0.5px solid $color5; + + * { + color: $color5; + } + } +} \ No newline at end of file diff --git a/styles/components/_index.scss b/styles/components/_index.scss index 80d7258..8ef99aa 100644 --- a/styles/components/_index.scss +++ b/styles/components/_index.scss @@ -1,3 +1,4 @@ @forward '_bar'; @forward '_launcher'; -@forward '_quick_settings'; +@forward '_battery_info'; +@forward '_quick_settings'; \ No newline at end of file diff --git a/styles/components/_launcher.scss b/styles/components/_launcher.scss index c397815..d81eb4d 100644 --- a/styles/components/_launcher.scss +++ b/styles/components/_launcher.scss @@ -2,29 +2,29 @@ .launcher { entry { - background: $primary; - color: $onPrimary; + background: $color5; + color: $color0; padding: 8px 12px; border: none; margin-bottom: 6px; &:focus { - outline: 2px solid $onPrimary; + outline: 2px solid $color0; } } .application { padding: 10px; - background: $primary; + background: $color5; border-radius: 8px; transition: background 0.2s ease; &:hover { - background: $onPrimary; + background: $color0; box>.name, box>.description { - color: $primary; + color: $color5; } } @@ -34,21 +34,21 @@ box { .name { - color: $onPrimary; + color: $color0; font-size: 14px; font-weight: 600; } .description { - color: $onPrimary; + color: $color0; font-size: 12px; } } } .not-found { - background: $primary; - color: $onPrimary; + background: $color5; + color: $color0; font-size: 14px; padding: 10px; border-radius: 8px; diff --git a/styles/components/_quick_settings.scss b/styles/components/_quick_settings.scss index 85f2db3..7a877a9 100644 --- a/styles/components/_quick_settings.scss +++ b/styles/components/_quick_settings.scss @@ -4,6 +4,7 @@ window.quick_settings { font-weight: bold; >box { + border: 1px solid $color0; border-radius: 10px; margin: 8px; } @@ -11,13 +12,13 @@ window.quick_settings { >.inner { border-bottom-left-radius: 0; border-bottom-right-radius: 0; - background: $primary; + background: $color5; padding: 0.75rem 3rem; margin: 0; * { transition: all 0.2s ease-in-out; - color: $onPrimary; + color: $color0; } } @@ -28,11 +29,11 @@ window.quick_settings { window.qs_bluetooth { >.inner { - background: $primary; + background: $color5; padding: 0.75rem 3rem; border-radius: 10px; - * { color: $onPrimary; } + * { color: $color0; } } .device { @@ -46,24 +47,24 @@ window.qs_bluetooth { window.quick_settings button, window.qs_bluetooth button { transition: all 0.2s ease-in-out; - background: $primary; - border: 2px solid $onPrimary; + background: $color5; + border: 2px solid $color0; margin: 5px; &:hover { - background: $onPrimary; + background: $color0; * { - color: $primary; + color: $color5; } } &.active { - background: $onPrimary; - border: 0.5px solid $primary; + background: $color0; + border: 0.5px solid $color5; * { - color: $primary; + color: $color5; } } } diff --git a/styles/style.scss b/styles/style.scss index 4d78b92..01265c3 100644 --- a/styles/style.scss +++ b/styles/style.scss @@ -8,12 +8,16 @@ font-family: $font-family; } -popover>contents, calendar { - background: $onPrimaryContainer; +separator { + background: $color0; } -button:hover { - background: $primary; +popover>contents, calendar { + background: $color5; +} + +button { + background: transparent; } image { diff --git a/widget/bar/bar.tsx b/widget/bar/bar.tsx index 202adc9..49450d9 100644 --- a/widget/bar/bar.tsx +++ b/widget/bar/bar.tsx @@ -1,49 +1,13 @@ import { App, Astal, Gdk, Gtk } from "astal/gtk4" -import { bind, Binding } from "astal" +import { Binding } from "astal" -import PowerProfiles from "gi://AstalPowerProfiles"; import Battery from "gi://AstalBattery"; +import { BatteryInfo } from "./components/battery"; import DateTime from "@lib/widgets/datetime"; import Workspace from "./workspace"; import TrayView from "./tray"; -type Substitution = { charging?: string, idle?: string } - -const getBatteryIcon = (percentage: number, charging: Binding) => { - const levels = Array.from({ length: 10 }, (_, i) => (i + 1) * 10); - const level = levels.find((level) => percentage <= level)!; - - const substitutions: Record = { - 100: { charging: "battery-level-100-charged-symbolic" } - }; - - return charging.as(c => c - ? substitutions[level]?.charging || `battery-level-${level}-charging-symbolic` - : substitutions[level]?.idle || `battery-level-${level}-symbolic` - ); -}; - -const BatteryInfo = ({ battery }: { battery: Battery.Device }) => { - const percentage = bind(battery, "percentage").as(p => Math.floor(p * 100)); - const charging = bind(battery, "charging"); - const power = PowerProfiles.get_default(); - - return ( - - {percentage.as(p => )} - - ); -}; - export default function Bar(monitor_id: number) { const { BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor const CENTER = Gtk.Align.CENTER; diff --git a/widget/bar/battery_info.tsx b/widget/bar/battery_info.tsx new file mode 100644 index 0000000..eb5c3fe --- /dev/null +++ b/widget/bar/battery_info.tsx @@ -0,0 +1,53 @@ +import { App, Astal, Gtk } from "astal/gtk4"; +import { bind } from "astal"; + +import { getBatteryIcon } from "@/lib/icons"; +import { hideWindow } from "@lib/utils"; + +import PowerProfiles from "gi://AstalPowerProfiles?version=0.1"; +import Battery from "gi://AstalBattery?version=0.1"; +import Hyprland from "gi://AstalHyprland"; + +export default function BatteryInfo(_monitor_id: number) { + const { BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor + const CENTER = Gtk.Align.CENTER; + + const hypr = Hyprland.get_default(); + const power = PowerProfiles.get_default(); + const battery = Battery.get_default(); + + const percentage = bind(battery, "percentage").as(p => Math.floor(p * 100)); + const charging = bind(battery, "charging"); + + return monitor.id)} + cssClasses={["battery_info"]} + keymode={Astal.Keymode.EXCLUSIVE} + exclusivity={Astal.Exclusivity.IGNORE} + anchor={BOTTOM | LEFT | RIGHT} + onKeyPressed={hideWindow} + application={App}> + + {percentage.as(p => <> + + + + )} + + {power.get_profiles().map(profile => )} + + ); +}; \ No newline at end of file diff --git a/widget/bar/network/button.tsx b/widget/bar/network/button.tsx deleted file mode 100644 index 85c22fb..0000000 --- a/widget/bar/network/button.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { bind, Variable } from "astal"; - -import Network from "gi://AstalNetwork"; - -export default function NetworkButton() { - const network = Network.get_default(); - - const state = Variable.derive( - [bind(network, "primary"), bind(network, "wifi"), bind(network, "wired")], - (primary, wifi, wired) => { - switch (primary) { - case Network.Primary.WIFI: - return { - icon: wifi.iconName, - label: wifi.ssid, - tooltip: `Connected to ${wifi.ssid}`, - }; - case Network.Primary.WIRED: - return { - icon: wired.iconName, - label: "Wired", - tooltip: { - [Network.Internet.CONNECTED]: `Connected to Ethernet`, - [Network.Internet.CONNECTING]: `Connecting to Ethernet...`, - [Network.Internet.DISCONNECTED]: `Connected to Ethernet (no internet!)`, - }[wired.internet], - }; - default: - return { - icon: "network-disconnected-symbolic", - label: "Disconnected", - tooltip: "No connection", - }; - } - } - ) - - const binding = bind(state); - return t.tooltip)} spacing={5}> - i.icon)} /> - -} \ No newline at end of file diff --git a/widget/quick_settings/bluetooth.tsx b/widget/quick_settings/bluetooth.tsx index c01f66d..21d5957 100644 --- a/widget/quick_settings/bluetooth.tsx +++ b/widget/quick_settings/bluetooth.tsx @@ -46,9 +46,7 @@ export default function BluetoothWindow(_monitor_id: number) { const bt = Bluetooth.get_default(); const hypr = Hyprland.get_default(); - const devices = bind(bt, "devices"); - const connectedDevice = devices.as(devices => devices?.find(device => device.connected)); return