From d695d9aa6736946d00f00ad9c17d41fb9b5c15ed Mon Sep 17 00:00:00 2001 From: Jas Singh Date: Sun, 9 Jun 2024 18:40:51 -0700 Subject: [PATCH] Added Power menu and show only the workspaces allocated to monitor --- ; | 40 ++++ config.js | 28 +-- modules/bar/bar.js | 66 ++++++- modules/bar/media/index.js | 35 +++- modules/bar/power/index.js | 2 + modules/bar/window_title/index.js | 3 +- modules/bar/workspaces/index.js | 60 ++++-- modules/menu/menu.js | 36 ---- modules/menus/PopupWindow.js | 137 +++++++++++++ modules/menus/main.js | 4 + modules/menus/power/helpers/actions.js | 54 +++++ modules/menus/power/index.js | 37 ++++ modules/menus/power/verification.js | 48 +++++ modules/volume/volume.js | 261 ------------------------- nohup.out | 37 ---- scss/bar/media.scss | 1 + scss/bar/window_title.scss | 2 +- scss/colors.scss | 2 + scss/{ => common}/common.scss | 0 scss/common/floating-widget.scss | 11 ++ scss/common/widget-button.scss | 53 +++++ scss/main.scss | 7 +- scss/menus/powermenu.scss | 105 ++++++++++ style.css | 136 ++++++++++++- style.css.map | 2 +- 25 files changed, 784 insertions(+), 383 deletions(-) create mode 100644 ; delete mode 100644 modules/menu/menu.js create mode 100644 modules/menus/PopupWindow.js create mode 100644 modules/menus/main.js create mode 100644 modules/menus/power/helpers/actions.js create mode 100644 modules/menus/power/index.js create mode 100644 modules/menus/power/verification.js delete mode 100644 modules/volume/volume.js delete mode 100644 nohup.out rename scss/{ => common}/common.scss (100%) create mode 100644 scss/common/floating-widget.scss create mode 100644 scss/common/widget-button.scss create mode 100644 scss/menus/powermenu.scss diff --git a/; b/; new file mode 100644 index 0000000..2ae433e --- /dev/null +++ b/; @@ -0,0 +1,40 @@ +import { exec } from "resource:///com/github/Aylur/ags/utils.js"; +import { Bar, BarAlt } from "./modules/bar/bar.js"; +import DirectoryMonitorService from "./directoryMonitorService.js"; +import MenuWindows from "./modules/menus/main.js"; + +const applyScss = () => { + // Compile scss + exec(`sass ${App.configDir}/scss/main.scss ${App.configDir}/style.css`); + exec( + `sass ${App.configDir}/scss/highlight.scss ${App.configDir}/highlight.css`, + ); + console.log("Scss compiled"); + + // Apply compiled css + App.resetCss(); + App.applyCss(`${App.configDir}/style.css`); + console.log("Compiled css applied"); +}; + +DirectoryMonitorService.connect("changed", () => applyScss()); + +applyScss(); + +export default { + style: `${App.configDir}/style.css`, + closeWindowDelay: { + sideright: 350, + launcher: 350, + bar0: 350, + }, +}; + +App.config({ + windows: [ + ...MenuWindows, + Bar(0), + BarAlt(1), + BarAlt(2), + ], +}); diff --git a/config.js b/config.js index e3f1a1a..4def2dc 100644 --- a/config.js +++ b/config.js @@ -1,12 +1,14 @@ -import {exec, idle, monitorFile} from "resource:///com/github/Aylur/ags/utils.js"; -import { Volume } from './modules/volume/volume.js'; -import { Bar } from './modules/bar/bar.js'; +import { exec } from "resource:///com/github/Aylur/ags/utils.js"; +import { Bar, BarAlt } from "./modules/bar/bar.js"; import DirectoryMonitorService from "./directoryMonitorService.js"; +import MenuWindows from "./modules/menus/main.js"; const applyScss = () => { // Compile scss exec(`sass ${App.configDir}/scss/main.scss ${App.configDir}/style.css`); - exec(`sass ${App.configDir}/scss/highlight.scss ${App.configDir}/highlight.css`); + exec( + `sass ${App.configDir}/scss/highlight.scss ${App.configDir}/highlight.css`, + ); console.log("Scss compiled"); // Apply compiled css @@ -19,7 +21,11 @@ DirectoryMonitorService.connect("changed", () => applyScss()); applyScss(); - +const workspaceMonitorMap = { + 0: [4,5], + 1: [6,7], + 2: [1,2,3,8,9,10], +} export default { style: `${App.configDir}/style.css`, closeWindowDelay: { @@ -28,16 +34,12 @@ export default { bar0: 350, }, }; -// const win = Widget.Window({ -// name: "volumeModule", -// child: Volume(), -// }); App.config({ windows: [ - // win, - Bar(0), - Bar(1), - Bar(2), + ...MenuWindows, + BarAlt(0, workspaceMonitorMap), + BarAlt(1, workspaceMonitorMap), + Bar(2, workspaceMonitorMap), ], }); diff --git a/modules/bar/bar.js b/modules/bar/bar.js index 40d03cf..1c55bc9 100644 --- a/modules/bar/bar.js +++ b/modules/bar/bar.js @@ -7,19 +7,18 @@ import { Volume } from "./volume/index.js"; import { Network } from "./network/index.js"; import { Bluetooth } from "./bluetooth/index.js"; import { BatteryLabel } from "./battery/index.js"; -import { Clock } from "./clock/index.js"; -import { SysTray } from "./systray/index.js"; +import { Clock } from "./clock/index.js"; import { SysTray } from "./systray/index.js"; import { Power } from "./power/index.js"; import { BarItemBox } from "../shared/barItemBox.js"; // layout of the bar -const Left = () => { +const Left = (monitor, wsMap) => { return Widget.Box({ class_name: "box-left", hpack: "start", spacing: 5, - children: [Menu(), BarItemBox(Workspaces()), BarItemBox(ClientTitle())], + children: [Menu(), BarItemBox(Workspaces(monitor, wsMap, 10)), BarItemBox(ClientTitle())], }); }; @@ -27,7 +26,10 @@ const Center = () => { return Widget.Box({ class_name: "box-center", spacing: 5, - children: [BarItemBox(Media()), BarItemBox(Notification())], + children: [ + BarItemBox(Media()), + // BarItemBox(Notification()) + ], }); }; @@ -48,7 +50,40 @@ const Right = () => { }); }; -const Bar = (monitor = 0) => { +const LeftAlt = (monitor, wsMap) => { + return Widget.Box({ + class_name: "box-left", + hpack: "start", + spacing: 5, + children: [Menu(), BarItemBox(Workspaces(monitor, wsMap)), BarItemBox(ClientTitle())], + }); +}; + +const CenterAlt = () => { + return Widget.Box({ + class_name: "box-center", + spacing: 5, + children: [ + BarItemBox(Media()), + // BarItemBox(Notification()) + ], + }); +}; + +const RightAlt = () => { + return Widget.Box({ + class_name: "box-right", + hpack: "end", + spacing: 5, + children: [ + BarItemBox(Volume()), + BarItemBox(Clock()), + BarItemBox(Power()), + ], + }); +}; + +const Bar = (monitor = 0, wsMap) => { return Widget.Window({ name: `bar-${monitor}`, class_name: "bar", @@ -56,11 +91,26 @@ const Bar = (monitor = 0) => { anchor: ["top", "left", "right"], exclusivity: "exclusive", child: Widget.CenterBox({ - start_widget: Left(), + start_widget: Left(monitor, wsMap), center_widget: Center(), end_widget: Right(), }), }); }; -export { Bar }; +const BarAlt = (monitor = 0, wsMap) => { + return Widget.Window({ + name: `bar-${monitor}`, + class_name: "bar", + monitor, + anchor: ["top", "left", "right"], + exclusivity: "exclusive", + child: Widget.CenterBox({ + start_widget: LeftAlt(monitor, wsMap), + center_widget: CenterAlt(), + end_widget: RightAlt(), + }), + }); +}; + +export { Bar, BarAlt }; diff --git a/modules/bar/media/index.js b/modules/bar/media/index.js index 648fcfb..f3540ff 100644 --- a/modules/bar/media/index.js +++ b/modules/bar/media/index.js @@ -2,6 +2,7 @@ const mpris = await Service.import("mpris"); const Media = () => { const activePlayer = Variable(mpris.players[0]); + mpris.connect("changed", (value) => { const statusOrder = { Playing: 1, @@ -22,12 +23,31 @@ const Media = () => { } }); - const label = Utils.watch("", mpris, "player-changed", () => { + const getIconForPlayer = (playerName) => { + const windowTitleMap = [ + ["Mozilla Firefox", "󰈹"], + ["Microsoft Edge", "󰇩"], + ["(.*)Discord(.*)", ""], + ["Plex", "󰚺 Plex"], + ["(.*) Spotify Free", "󰓇"], + ["(.*)Spotify Premium", "󰓇"], + ["Spotify", "󰓇"], + ["(.*)", "󰝚"], + ]; + + const foundMatch = windowTitleMap.find((wt) => + RegExp(wt[0]).test(playerName), + ); + + return foundMatch ? foundMatch[1] : "󰝚"; + }; + + const label = Utils.watch("󰎇 Nothing is playing 󰎇", mpris, "player-changed", () => { if (activePlayer.value) { - const { track_artists, track_title } = activePlayer.value; - return `󰝚 ${track_title} - ${track_artists.join(", ")} 󰝚`; + const { track_title, identity } = activePlayer.value; + return `${getIconForPlayer(identity)} ${track_title}`; } else { - return "Nothing is playing"; + return "󰎇 Nothing is playing 󰎇"; } }); @@ -39,7 +59,12 @@ const Media = () => { on_primary_click: () => mpris.getPlayer("")?.playPause(), on_scroll_up: () => mpris.getPlayer("")?.next(), on_scroll_down: () => mpris.getPlayer("")?.previous(), - child: Widget.Label({ label }), + child: Widget.Label({ + label, + truncate: 'end', + wrap: true, + maxWidthChars: 30, + }), }), }), isVisible: false, diff --git a/modules/bar/power/index.js b/modules/bar/power/index.js index 27ed441..60cdfda 100644 --- a/modules/bar/power/index.js +++ b/modules/bar/power/index.js @@ -2,6 +2,8 @@ export const Power = () => { return { component: Widget.Box({ child: Widget.Button({ + class_name: "powermenu", + on_clicked: () => App.toggleWindow("powermenu"), child: Widget.Label({ class_name: "bar-power_label", label: "⏻", diff --git a/modules/bar/window_title/index.js b/modules/bar/window_title/index.js index 0bb416e..ba6493f 100644 --- a/modules/bar/window_title/index.js +++ b/modules/bar/window_title/index.js @@ -18,7 +18,8 @@ const filterTitle = (titleString) => { ["Spotify", "󰓇 Spotify"], [" ~", " Terminal"], ["(.*) - Obsidian(.*)", "󱓧 Obsidian"], - ["(.*)", `󰣆 ${titleString.charAt(0).toUpperCase() + titleString.slice(1)}`], + ["(.+)", `󰣆 ${titleString.charAt(0).toUpperCase() + titleString.slice(1)}`], + ["(.*)", `󰣆 Desktop`], ]; const foundMatch = windowTitleMap.find((wt) => diff --git a/modules/bar/workspaces/index.js b/modules/bar/workspaces/index.js index a09fce1..757f886 100644 --- a/modules/bar/workspaces/index.js +++ b/modules/bar/workspaces/index.js @@ -4,28 +4,52 @@ function range(length, start = 1) { return Array.from({ length }, (_, i) => i + start); } -const Workspaces = (ws) => { +const Workspaces = (monitor = -1, wsMap = {}, ws = 8) => { + const getWorkspacesForMonitor = (curWs) => { + if ( + Object.keys(wsMap) + .map((mn) => Number(mn)) + .includes(monitor) + ) { + return wsMap[monitor].includes(curWs); + } + return true; + }; return { component: Widget.Box({ class_name: "workspaces", - children: range(ws || 8).map((i) => - Widget.Label({ - attribute: i, - vpack: "center", - label: `${i}`, - setup: (self) => - self.hook(hyprland, () => { - self.toggleClassName( - "active", - hyprland.active.workspace.id === i, - ); - self.toggleClassName( - "occupied", - (hyprland.getWorkspace(i)?.windows || 0) > 0, - ); - }), + children: range(ws || 8) + .filter((i) => getWorkspacesForMonitor(i)) + .map((i) => { + return Widget.Label({ + attribute: i, + vpack: "center", + label: `${i}`, + setup: (self) => + self.hook(hyprland, () => { + // console.log(`currentMonitor: ${monitor}`); + console.log(i); + console.log(JSON.stringify(hyprland.getWorkspace(i), null, 2)); + if (hyprland.getWorkspace(i)) { + // console.log(`currentMonitor: ${monitor}`); + } + self.toggleClassName( + "active", + hyprland.active.workspace.id === i, + ); + self.toggleClassName( + "occupied", + (hyprland.getWorkspace(i)?.windows || 0) > 0, + ); + + const isCurrentMonitor = + monitor !== -1 && + hyprland.getWorkspace(i)?.monitorID !== monitor; + + self.toggleClassName("hidden", isCurrentMonitor); + }), + }); }), - ), setup: (box) => { if (ws === 0) { box.hook(hyprland.active.workspace, () => diff --git a/modules/menu/menu.js b/modules/menu/menu.js deleted file mode 100644 index f37817b..0000000 --- a/modules/menu/menu.js +++ /dev/null @@ -1,36 +0,0 @@ -import Widget from "resource:///com/github/Aylur/ags/widget.js"; - -/** - * @param {Object} param - * @param {string} param.title - * @param {string} param.icon - * @param {import('gi://Gtk').Gtk.Widget} param.content - * @param {import('gi://Gtk').Gtk.Widget} [param.headerChild] - * @return {import('types/widgets/box').default} - */ -export default ({title, icon, content, headerChild = Widget.Box()}) => Widget.Box({ - children: [ - Widget.Box({ - class_name: "qs-menu", - vertical: true, - children: [ - Widget.Box({ - class_name: "qs-title", - spacing: 5, - children: [ - Widget.Icon(icon), - Widget.Label(title), - Widget.Box({hexpand: true}), - headerChild - ], - }), - Widget.Separator(), - Widget.Box({ - class_name: "qs-content", - children: [content], - }), - ], - }) - ], -}); - diff --git a/modules/menus/PopupWindow.js b/modules/menus/PopupWindow.js new file mode 100644 index 0000000..a7c8c90 --- /dev/null +++ b/modules/menus/PopupWindow.js @@ -0,0 +1,137 @@ +export const Padding = (name) => Widget.EventBox({ + hexpand: true, + vexpand: true, + can_focus: false, + child: Widget.Box(), + setup: w => w.on("button-press-event", () => App.toggleWindow(name)), +}) + +const PopupRevealer = ( + name, + child, + transition = "slide_down", +) => Widget.Box( + { css: "padding: 1px;" }, + Widget.Revealer({ + transition, + child: Widget.Box({ + class_name: "window-content", + child, + }), + transitionDuration: 200, + setup: self => self.hook(App, (_, wname, visible) => { + if (wname === name) + self.reveal_child = visible + }), + }), +) + +const Layout = (name, child, transition) => ({ + "center": () => Widget.CenterBox({}, + Padding(name), + Widget.CenterBox( + { vertical: true }, + Padding(name), + PopupRevealer(name, child, transition), + Padding(name), + ), + Padding(name), + ), + "top": () => Widget.CenterBox({}, + Padding(name), + Widget.Box( + { vertical: true }, + PopupRevealer(name, child, transition), + Padding(name), + ), + Padding(name), + ), + "top-right": () => Widget.Box({}, + Padding(name), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + PopupRevealer(name, child, transition), + Padding(name), + ), + ), + "top-center": () => Widget.Box({}, + Padding(name), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + PopupRevealer(name, child, transition), + Padding(name), + ), + Padding(name), + ), + "top-left": () => Widget.Box({}, + Widget.Box( + { + hexpand: false, + vertical: true, + }, + PopupRevealer(name, child, transition), + Padding(name), + ), + Padding(name), + ), + "bottom-left": () => Widget.Box({}, + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name), + PopupRevealer(name, child, transition), + ), + Padding(name), + ), + "bottom-center": () => Widget.Box({}, + Padding(name), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name), + PopupRevealer(name, child, transition), + ), + Padding(name), + ), + "bottom-right": () => Widget.Box({}, + Padding(name), + Widget.Box( + { + hexpand: false, + vertical: true, + }, + Padding(name), + PopupRevealer(name, child, transition), + ), + ), +}) + +export default ({ + name, + child, + layout = "center", + transition, + exclusivity = "ignore", + ...props +}) => Widget.Window({ + name, + class_names: [name, "popup-window"], + setup: w => w.keybind("Escape", () => App.closeWindow(name)), + visible: true, + keymode: "on-demand", + exclusivity, + layer: "top", + anchor: ["top", "bottom", "right", "left"], + child: Layout(name, child, transition)[layout](), + ...props, +}) diff --git a/modules/menus/main.js b/modules/menus/main.js new file mode 100644 index 0000000..85e4f61 --- /dev/null +++ b/modules/menus/main.js @@ -0,0 +1,4 @@ +import PowerMenu from "./power/index.js"; +import Verification from "./power/verification.js"; + +export default [PowerMenu(), Verification()]; diff --git a/modules/menus/power/helpers/actions.js b/modules/menus/power/helpers/actions.js new file mode 100644 index 0000000..900f9e1 --- /dev/null +++ b/modules/menus/power/helpers/actions.js @@ -0,0 +1,54 @@ +const powerOptions = { + sleep: "systemctl suspend", + reboot: "systemctl reboot", + logout: "pkill Hyprland", + shutdown: "shutdown now", +}; + +class PowerMenu extends Service { + static { + Service.register( + this, + {}, + { + title: ["string"], + cmd: ["string"], + }, + ); + } + + #title = ""; + #cmd = ""; + + get title() { + return this.#title; + } + + action(action) { + [this.#cmd, this.#title] = { + sleep: [powerOptions.sleep, "Sleep"], + reboot: [powerOptions.reboot, "Reboot"], + logout: [powerOptions.logout, "Log Out"], + shutdown: [powerOptions.shutdown, "Shutdown"], + }[action]; + + this.notify("cmd"); + this.notify("title"); + this.emit("changed"); + App.closeWindow("powermenu"); + App.openWindow("verification"); + } + + shutdown = () => { + this.action("shutdown"); + }; + + exec = () => { + App.closeWindow("verification"); + Utils.exec(this.#cmd); + }; +} + +const powermenu = new PowerMenu(); +Object.assign(globalThis, { powermenu }); +export default powermenu; diff --git a/modules/menus/power/index.js b/modules/menus/power/index.js new file mode 100644 index 0000000..20f7273 --- /dev/null +++ b/modules/menus/power/index.js @@ -0,0 +1,37 @@ +import PopupWindow from "../PopupWindow.js"; +import powermenu from "./helpers/actions.js"; +import icons from "../../icons/index.js"; + +const SysButton = (action, label) => + Widget.Button({ + class_name: `widget-button powermenu-button-${action}`, + on_clicked: () => powermenu.action(action), + child: Widget.Box({ + vertical: true, + class_name: "system-button widget-button", + children: [ + Widget.Icon({ + class_name: `system-button_icon ${action}`, + icon: icons.powermenu[action], + }), + Widget.Label({ + class_name: `system-button_label ${action}`, + label, + }), + ], + }), + }); +export default () => + PopupWindow({ + name: "powermenu", + transition: "crossfade", + child: Widget.Box({ + class_name: "powermenu horizontal", + children: [ + SysButton("shutdown", "SHUTDOWN"), + SysButton("logout", "LOG OUT"), + SysButton("reboot", "REBOOT"), + SysButton("sleep", "SLEEP"), + ], + }), + }); diff --git a/modules/menus/power/verification.js b/modules/menus/power/verification.js new file mode 100644 index 0000000..87c22df --- /dev/null +++ b/modules/menus/power/verification.js @@ -0,0 +1,48 @@ +import PopupWindow from "../PopupWindow.js"; +import powermenu from "./helpers/actions.js"; + +export default () => + PopupWindow({ + name: "verification", + transition: "crossfade", + child: Widget.Box({ + class_name: "verification", + vertical: true, + children: [ + Widget.Box({ + class_name: "text-box", + vertical: true, + children: [ + Widget.Label({ + class_name: "title", + label: powermenu.bind("title"), + }), + Widget.Label({ + class_name: "desc", + label: "Are you sure?", + }), + ], + }), + Widget.Box({ + class_name: "buttons horizontal", + vexpand: true, + vpack: "end", + homogeneous: true, + children: [ + Widget.Button({ + child: Widget.Label("No"), + on_clicked: () => App.toggleWindow("verification"), + setup: (self) => + self.hook(App, (_, name, visible) => { + if (name === "verification" && visible) self.grab_focus(); + }), + }), + Widget.Button({ + child: Widget.Label("Yes"), + on_clicked: powermenu.exec, + }), + ], + }), + ], + }), + }); diff --git a/modules/volume/volume.js b/modules/volume/volume.js deleted file mode 100644 index 417aecb..0000000 --- a/modules/volume/volume.js +++ /dev/null @@ -1,261 +0,0 @@ -// const audio = await Service.import("audio"); -// -// /** @param {'speaker' | 'microphone'} type */ -// const VolumeSlider = (type = "speaker") => -// Widget.Slider({ -// hexpand: true, -// drawValue: false, -// onChange: ({ value }) => (audio[type].volume = value), -// value: audio[type].bind("volume"), -// }); -// -// const speakerSlider = VolumeSlider("speaker"); -// const micSlider = VolumeSlider("microphone"); -// -// const VolumeCtl = () => { -// const volCtlLabel = Widget.Label({ -// class_name: "volCtlLabel", -// label: "Volume", -// }); -// -// const volSliderBox = Widget.Box( -// { class_name: "volumeSliderBox" }, -// speakerSlider, -// ); -// -// return Widget.Box( -// { -// class_name: "volumeCtlContainer", -// vertical: true, -// css: 'min-width: 100px' -// }, -// volCtlLabel, -// volSliderBox, -// ); -// }; -// -// export const Volume = () => { -// return Widget.Box({ -// child: VolumeCtl(), -// }); -// }; -import icons from "../icons/index.js"; -import Widget from "resource:///com/github/Aylur/ags/widget.js"; -import {execAsync} from "resource:///com/github/Aylur/ags/utils.js"; -import Audio from "resource:///com/github/Aylur/ags/service/audio.js"; -import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js"; -import Menu from "../menu/menu.js"; - -/** @param {string} type */ -const sorm = (type) => type === "sink" ? "speaker" : "microphone"; -/** @param {string} type */ -const sorms = (type) => type === "sink" ? "speakers" : "microphones"; -/** @param {string | null} item - * @param {string} type */ -const iconSubstitute = (item, type) => { - const microphoneSubstitutes = { - "audio-headset-analog-usb": "audio-headset-symbolic", - "audio-headset-bluetooth": "audio-headphones-symbolic", - "audio-card-analog-usb": "audio-input-microphone-symbolic", - "audio-card-analog-pci": "audio-input-microphone-symbolic", - "audio-card-analog": "audio-input-microphone-symbolic", - "camera-web-analog-usb": "camera-web-symbolic" - }; - const substitues = { - "audio-headset-bluetooth": "audio-headphones-symbolic", - "audio-card-analog-usb": "audio-speakers-symbolic", - "audio-card-analog-pci": "audio-speakers-symbolic", - "audio-card-analog": "audio-speakers-symbolic", - "audio-headset-analog-usb": "audio-headset-symbolic" - }; - - if (type === "sink") { - return substitues[item] || item; - } - return microphoneSubstitutes[item] || item; -}; - -/** @param {import('types/service/audio').Stream} stream */ -const streamIconSubstiture = stream => { - const subs = { - "spotify": "spotify", - "Firefox": "firefox", - }; - return subs[stream.name] || stream.icon_name; -}; - -/** @param {string} type */ -const TypeIndicator = (type = "sink") => Widget.Button({ - on_clicked: () => execAsync(`pactl set-${type}-mute @DEFAULT_${type.toUpperCase()}@ toggle`), - child: Widget.Icon() - .hook(Audio, icon => { - if (Audio[sorm(type)]) - // @ts-ignore - icon.icon = iconSubstitute(Audio[sorm(type)].icon_name, type); - }, sorm(type) + "-changed") -}); - -/** @param {string} type */ -const PercentLabel = (type = "sink") => Widget.Label({ - class_name: "audio-volume-label", -}) - .hook(Audio, label => { - if (Audio[sorm(type)]) - // @ts-ignore - label.label = `${Math.floor(Audio[sorm(type)].volume * 100)}%`; - }, sorm(type) + "-changed"); - -/** @param {string} type */ -const VolumeSlider = (type = "sink") => Widget.Slider({ - hexpand: true, - draw_value: false, - // @ts-ignore - on_change: ({value}) => Audio[sorm(type)].volume = value, -}) - .hook(Audio, slider => { - if (!Audio[sorm(type)]) - return; - - // @ts-ignore - slider.sensitive = !Audio[sorm(type)].is_muted; - // @ts-ignore - slider.value = Audio[sorm(type)].volume; - }, sorm(type) + "-changed"); - -/** @param {string} type */ -export const Volume = (type = "sink") => Widget.Box({ - class_name: "audio-volume-box", - children: [ - TypeIndicator(type), - VolumeSlider(type), - PercentLabel(type) - ], -}); - -/** @param {import('types/service/audio').Stream} stream */ -const MixerItem = stream => Widget.EventBox({ - on_primary_click: () => stream.is_muted = !stream.is_muted, - on_scroll_up: () => stream.volume += 0.03, - on_scroll_down: () => stream.volume -= 0.03, - child: Widget.Box({ - hexpand: true, - class_name: "mixer-item", - children: [ - Widget.Icon({ - icon: stream.bind("icon_name").transform(() => streamIconSubstiture(stream)), - tooltip_text: stream.bind("name").transform(name => name || "") - }), - Widget.Box({ - vertical: true, - vpack: "center", - children: [ - Widget.Box({ - children: [ - Widget.Label({ - xalign: 0, - hexpand: true, - class_name: "mixer-item-title", - truncate: "end", - label: stream.bind("description").transform(desc => desc || ""), - }), - Widget.Label({ - xalign: 0, - class_name: "mixer-item-volume", - label: stream.bind("volume").transform(volume => `${Math.floor(volume * 100)}%`) - }), - ] - }), - Widget.Slider({ - hexpand: true, - class_name: "mixer-item-slider", - draw_value: false, - value: stream.bind("volume"), - on_change: ({value}) => { - stream.volume = value; - }, - }), - ], - }), - ], - }) -}); - -/** - * @param {string} type - * @returns {function(import('types/service/audio').Stream): import('types/widgets/button').default} - */ -const SinkItem = (type) => stream => Widget.Button({ - on_clicked: () => Audio[sorm(type)] = stream, - child: Widget.Box({ - spacing: 5, - children: [ - Widget.Icon({ - icon: iconSubstitute(stream.icon_name, type), - tooltip_text: stream.icon_name, - }), - Widget.Label(stream.description?.split(" ").slice(0, 4).join(" ")), - Widget.Icon({ - icon: icons.tick, - hexpand: true, - hpack: "end", - }).hook(Audio, icon => { - icon.visible = Audio[sorm(type)] === stream; - }), - ], - }), -}); - -/** @param {number} tab */ -const SettingsButton = (tab = 0) => Widget.Button({ - on_clicked: () => Hyprland.sendMessage("dispatch exec pavucontrol -t " + tab), - child: Widget.Icon(icons.settings), -}); - -export const AppMixer = () => Menu({ - title: "App Mixer", - icon: icons.audio.mixer, - content: Widget.Box({ - class_name: "app-mixer", - vertical: true, - children: [ - Widget.Box({vertical: true}) - .hook(Audio, box => { - box.children = Audio.apps.map(MixerItem); - }, "notify::apps") - ], - }), - headerChild: SettingsButton(1), -}); - -export const SinkSelector = (type = "sink") => Menu({ - title: type + " Selector", - icon: type === "sink" ? icons.audio.type.headset : icons.audio.mic.unmuted, - content: Widget.Box({ - class_name: "sink-selector", - vertical: true, - children: [ - Widget.Box({vertical: true}) - .hook(Audio, box => { - box.children = Array.from(Audio[sorms(type)].values()).map(SinkItem(type)); - }, "stream-added") - .hook(Audio, box => { - box.children = Array.from(Audio[sorms(type)].values()).map(SinkItem(type)); - }, "stream-removed") - ], - }), - headerChild: SettingsButton(type === "sink" ? 3 : 4), -}); - -const AudioContent = () => Widget.Box({ - vertical: true, - class_name: "qs-page", - children: [ - Volume("sink"), - Volume("source"), - SinkSelector("sink"), - SinkSelector("source"), - AppMixer(), - ] -}); - -export default AudioContent; diff --git a/nohup.out b/nohup.out deleted file mode 100644 index 3047745..0000000 --- a/nohup.out +++ /dev/null @@ -1,37 +0,0 @@ -[2024-06-08 14:18:53.312] [info] Using configuration file /home/jaskir/.config/waybar/config -[2024-06-08 14:18:53.313] [info] Unable to receive desktop appearance: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface “org.freedesktop.portal.Settings” on object at path /org/freedesktop/portal/desktop -[2024-06-08 14:18:53.313] [info] Using CSS file /home/jaskir/.config/waybar/style.css -[2024-06-08 14:18:53.316] [warning] persistent_workspaces is deprecated. Please change config to use persistent-workspaces. -[2024-06-08 14:18:53.316] [info] Hyprland IPC starting -[2024-06-08 14:18:53.317] [info] Loading persistent workspaces from Waybar config -[2024-06-08 14:18:53.317] [info] Loading persistent workspaces from Hyprland workspace rules -[2024-06-08 14:18:53.320] [warning] module backlight: Disabling module "backlight", No backlight found -[2024-06-08 14:18:53.321] [warning] No batteries. -[2024-06-08 14:18:53.354] [warning] persistent_workspaces is deprecated. Please change config to use persistent-workspaces. -[2024-06-08 14:18:53.355] [info] Loading persistent workspaces from Waybar config -[2024-06-08 14:18:53.355] [info] Loading persistent workspaces from Hyprland workspace rules -[2024-06-08 14:18:53.357] [warning] module backlight: Disabling module "backlight", No backlight found -[2024-06-08 14:18:53.358] [warning] No batteries. -[2024-06-08 14:18:53.361] [warning] persistent_workspaces is deprecated. Please change config to use persistent-workspaces. -[2024-06-08 14:18:53.362] [info] Loading persistent workspaces from Waybar config -[2024-06-08 14:18:53.362] [info] Loading persistent workspaces from Hyprland workspace rules -[2024-06-08 14:18:53.363] [warning] module backlight: Disabling module "backlight", No backlight found -[2024-06-08 14:18:53.365] [warning] No batteries. -[2024-06-08 14:18:53.495] [error] Host: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: Method RegisterStatusNotifierHost is not implemented -[2024-06-08 14:18:53.495] [error] Host: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: Method RegisterStatusNotifierHost is not implemented -[2024-06-08 14:18:53.495] [error] Host: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: Method RegisterStatusNotifierHost is not implemented -[2024-06-08 14:18:53.503] [info] Bar configured (width: 1440, height: 38) for output: DP-1 -[2024-06-08 14:18:53.503] [info] Bar configured (width: 1440, height: 38) for output: DP-2 -[2024-06-08 14:18:53.503] [info] Bar configured (width: 3840, height: 38) for output: DP-3 - -(waybar:4162481): Gtk-CRITICAL **: 14:19:32.496: gtk_widget_set_accel_path: assertion 'GTK_IS_ACCEL_GROUP (accel_group)' failed - -(waybar:4162481): Gtk-CRITICAL **: 14:19:32.496: gtk_widget_add_accelerator: assertion 'GTK_IS_ACCEL_GROUP (accel_group)' failed - -(waybar:4162481): Gtk-CRITICAL **: 14:19:32.497: gtk_widget_set_accel_path: assertion 'GTK_IS_ACCEL_GROUP (accel_group)' failed - -(waybar:4162481): Gtk-CRITICAL **: 14:19:32.497: gtk_widget_add_accelerator: assertion 'GTK_IS_ACCEL_GROUP (accel_group)' failed - -(waybar:4162481): Gtk-CRITICAL **: 14:19:32.497: gtk_widget_set_accel_path: assertion 'GTK_IS_ACCEL_GROUP (accel_group)' failed - -(waybar:4162481): Gtk-CRITICAL **: 14:19:32.497: gtk_widget_add_accelerator: assertion 'GTK_IS_ACCEL_GROUP (accel_group)' failed diff --git a/scss/bar/media.scss b/scss/bar/media.scss index 7dacc4f..4c5f23e 100644 --- a/scss/bar/media.scss +++ b/scss/bar/media.scss @@ -1,5 +1,6 @@ @import "../colors"; .media { + font-size: 1rem; color: $green; } diff --git a/scss/bar/window_title.scss b/scss/bar/window_title.scss index 461fc84..50062ec 100644 --- a/scss/bar/window_title.scss +++ b/scss/bar/window_title.scss @@ -1,5 +1,5 @@ @import "../colors"; .window_title { - color: $yellow; + color: $pink; } diff --git a/scss/colors.scss b/scss/colors.scss index 66679d6..abe5955 100644 --- a/scss/colors.scss +++ b/scss/colors.scss @@ -44,6 +44,8 @@ $crust: #11111b; $default_fg: $primary-color; $default_bg: #000000; $primary_bg: $dark-background; +// $primary_fg: mix($mauve, $primary_bg, 70%); $primary_fg: mix($mauve, $primary_bg, 70%); $secondary_fg: $green; $secondary_bg: $light-background; +$shadow-color: #1e1e2e; diff --git a/scss/common.scss b/scss/common/common.scss similarity index 100% rename from scss/common.scss rename to scss/common/common.scss diff --git a/scss/common/floating-widget.scss b/scss/common/floating-widget.scss new file mode 100644 index 0000000..26dbd5e --- /dev/null +++ b/scss/common/floating-widget.scss @@ -0,0 +1,11 @@ +@import "../colors"; + +@mixin floating-widget { + // box-shadow: 0 0 5px 0 $shadow-color; + margin: max($spacing, 8px); + border: 3px solid $surface1; + background-color: $base; + color: $primary_fg; + border-radius: 11px; + padding: 20px; +} diff --git a/scss/common/widget-button.scss b/scss/common/widget-button.scss new file mode 100644 index 0000000..0cfe486 --- /dev/null +++ b/scss/common/widget-button.scss @@ -0,0 +1,53 @@ +$popover-padding: 7px * 1.6; +$padding: 7px; +$spacing: 12px; +$radius: 11px; + +.widget-button { + background: $crust; + opacity: 1; + font-size: 1.6rem; + font-weight: bold; + margin: 10px; + padding: 15px 20px; + border-radius: 10px; + border: 3px solid; + border-color: $crust; + transition: border-color .3s ease-in-out; + transition: opacity .3s ease-in-out; + + &:hover { + border: 3px solid; + border-color: $lavender; + transition: border-color .3s ease-in-out; + } + + &:focus, + &:active { + border: 3px solid; + border-color: $lavender; + } + + image { + border-radius: $radius + ($popover-padding * 1.4); + min-width: 1.2em; + min-height: 1.2em; + font-size: 4em; + } + + label { + margin-top: $spacing; + } + + &:focus, + &:active { + image { + opacity: .3; + transition: opacity .3s ease-in-out; + } + label { + opacity: .3; + transition: opacity .3s ease-in-out; + } + } +} diff --git a/scss/main.scss b/scss/main.scss index 0e58cf4..ddedb17 100644 --- a/scss/main.scss +++ b/scss/main.scss @@ -5,7 +5,9 @@ //general @import "colors"; -@import "common"; +@import "../scss/common/common.scss"; +@import "../scss/common/floating-widget.scss"; +@import "../scss/common/widget-button.scss"; //modules - bar @import "bar/menu"; @@ -19,3 +21,6 @@ @import "bar/systray"; @import "bar/power"; @import "bar/bar"; + +//modules - menus +@import "menus/powermenu"; diff --git a/scss/menus/powermenu.scss b/scss/menus/powermenu.scss new file mode 100644 index 0000000..dfc5174 --- /dev/null +++ b/scss/menus/powermenu.scss @@ -0,0 +1,105 @@ +@import "../colors"; + +window#powermenu, +window#verification { + // the fraction has to be more than hyprland ignorealpha + background-color: rgba(0, 0, 0, .4); +} + +$popover-padding: 7px * 1.6; +$padding: 7px; +$spacing: 12px; +$radius: 11px; + +window#verification .verification { + @include floating-widget; + padding: 7px * 1.6 * 1.5; + min-width: 300px; + min-height: 100px; + + .text-box { + margin-bottom: $spacing; + + .title { + font-size: 1.6em; + } + + .desc { + color: transparentize($primary_fg, 0.1); + font-size: 1.1em; + } + } +} + +window#powermenu .powermenu { + @include floating-widget; + &.line { + padding: $popover-padding * 1.5; + } + + &.box { + padding: $popover-padding * 2; + } +} + +.widget-button { + &:hover { + &.powermenu-button-shutdown { + border-color: $red; + } + &.powermenu-button-logout { + border-color: $green; + } + &.powermenu-button-sleep { + border-color: $sky; + } + &.powermenu-button-reboot { + border-color: $peach; + } + } + + &:focus, + &:active { + &.powermenu-button-shutdown { + border-color: rgba($red, .5); + } + &.powermenu-button-logout { + border-color: rgba($green, .5); + } + &.powermenu-button-sleep { + border-color: rgba($sky, .5); + } + &.powermenu-button-reboot { + border-color: rgba($peach, .5); + } + } +} +.system-button_icon { + &.shutdown { + color: $red; + } + &.logout { + color: $green; + } + &.reboot { + color: $peach; + } + &.sleep { + color: $sky; + } +} + +.system-button_label { + &.shutdown { + color: $red; + } + &.logout { + color: $green; + } + &.reboot { + color: $peach; + } + &.sleep { + color: $sky; + } +} diff --git a/style.css b/style.css index e10d953..0e6fc2f 100644 --- a/style.css +++ b/style.css @@ -232,6 +232,46 @@ spinner:checked { animation: spin 1s linear infinite; } +.widget-button { + background: #11111b; + opacity: 1; + font-size: 1.6rem; + font-weight: bold; + margin: 10px; + padding: 15px 20px; + border-radius: 10px; + border: 3px solid; + border-color: #11111b; + transition: border-color 0.3s ease-in-out; + transition: opacity 0.3s ease-in-out; +} +.widget-button:hover { + border: 3px solid; + border-color: #b4befe; + transition: border-color 0.3s ease-in-out; +} +.widget-button:focus, .widget-button:active { + border: 3px solid; + border-color: #b4befe; +} +.widget-button image { + border-radius: 26.68px; + min-width: 1.2em; + min-height: 1.2em; + font-size: 4em; +} +.widget-button label { + margin-top: 12px; +} +.widget-button:focus image, .widget-button:active image { + opacity: 0.3; + transition: opacity 0.3s ease-in-out; +} +.widget-button:focus label, .widget-button:active label { + opacity: 0.3; + transition: opacity 0.3s ease-in-out; +} + .bar-menu_label { color: #94e2d5; margin-right: 20px; @@ -268,6 +308,7 @@ spinner:checked { } .media { + font-size: 1rem; color: #a6e3a1; } @@ -313,7 +354,7 @@ spinner:checked { } .window_title { - color: #f9e2af; + color: #f5c2e7; } .systray button:not(:first-child) { @@ -375,4 +416,97 @@ spinner:checked { margin-right: 25px; } +window#powermenu, +window#verification { + background-color: rgba(0, 0, 0, 0.4); +} + +window#verification .verification { + margin: 12px; + border: 3px solid #45475a; + background-color: #1e1e2e; + color: #9278b6; + border-radius: 11px; + padding: 20px; + padding: 16.8px; + min-width: 300px; + min-height: 100px; +} +window#verification .verification .text-box { + margin-bottom: 12px; +} +window#verification .verification .text-box .title { + font-size: 1.6em; +} +window#verification .verification .text-box .desc { + color: rgba(146, 120, 182, 0.9); + font-size: 1.1em; +} + +window#powermenu .powermenu { + margin: 12px; + border: 3px solid #45475a; + background-color: #1e1e2e; + color: #9278b6; + border-radius: 11px; + padding: 20px; +} +window#powermenu .powermenu.line { + padding: 16.8px; +} +window#powermenu .powermenu.box { + padding: 22.4px; +} + +.widget-button:hover.powermenu-button-shutdown { + border-color: #f38ba8; +} +.widget-button:hover.powermenu-button-logout { + border-color: #a6e3a1; +} +.widget-button:hover.powermenu-button-sleep { + border-color: #89dceb; +} +.widget-button:hover.powermenu-button-reboot { + border-color: #fab387; +} +.widget-button:focus.powermenu-button-shutdown, .widget-button:active.powermenu-button-shutdown { + border-color: rgba(243, 139, 168, 0.5); +} +.widget-button:focus.powermenu-button-logout, .widget-button:active.powermenu-button-logout { + border-color: rgba(166, 227, 161, 0.5); +} +.widget-button:focus.powermenu-button-sleep, .widget-button:active.powermenu-button-sleep { + border-color: rgba(137, 220, 235, 0.5); +} +.widget-button:focus.powermenu-button-reboot, .widget-button:active.powermenu-button-reboot { + border-color: rgba(250, 179, 135, 0.5); +} + +.system-button_icon.shutdown { + color: #f38ba8; +} +.system-button_icon.logout { + color: #a6e3a1; +} +.system-button_icon.reboot { + color: #fab387; +} +.system-button_icon.sleep { + color: #89dceb; +} + +.system-button_label.shutdown { + color: #f38ba8; +} +.system-button_label.logout { + color: #a6e3a1; +} +.system-button_label.reboot { + color: #fab387; +} +.system-button_label.sleep { + color: #89dceb; +} + /*# sourceMappingURL=style.css.map */ diff --git a/style.css.map b/style.css.map index 26907e3..2393ac2 100644 --- a/style.css.map +++ b/style.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["scss/main.scss","scss/common.scss","scss/colors.scss","scss/bar/menu.scss","scss/bar/audio.scss","scss/bar/media.scss","scss/bar/network.scss","scss/bar/bluetooth.scss","scss/bar/clock.scss","scss/bar/workspace.scss","scss/bar/window_title.scss","scss/bar/systray.scss","scss/bar/power.scss","scss/bar/bar.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;ACFF;EACE;EACA;EACA,kBCFgB;EDGhB;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI,kBChBW;;ADkBf;EACE,OCnBa;;ADqBf;EACE;EACA;EACA;EACA;;AAGJ;EACE;EACA;EACA,kBC/Be;;;ADmCnB;EACE;EACA;EACA;EACA,OCvCiB;;;AD0CnB;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA,kBC9DiB;ED+DjB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;EACA,kBC9EgB;;;ADiFlB;AAAA;EAEE;EACA,kBCpFgB;;;ADuFlB;AAAA;AAAA;EAGE,OC1FgB;ED2FhB,kBC9CW;;;ADiDb;AAAA;AAAA;EAGE;EACA,kBCrDW;;;ADwDb;AAAA;AAAA;EAGE;EACA,kBC5DW;;;AD+Db;EACE;EACA;EACA;EACA;EACA,kBChHiB;EDiHjB;EACA;EACA;;;AAGF;EACE,kBC3EW;;;AD8Eb;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA,kBCvIc;EDwId;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA,kBChKiB;;;ADmKnB;EACE;EACA,kBCzHW;;;AD4Hb;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA,OC3IW;ED4IX,kBCzLgB;ED0LhB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,kBC1MgB;ED2MhB,OC5Mc;ED6Md;;;AAGF;EACE;EACA;EACA,kBCjNiB;;;ADoNnB;EACE;IACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AElOF;EACE,ODsBK;ECrBL;EACA;;;ACFA;EACE;;;AAIJ;EACE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA,OFRM;;;AEWR;EACE,OFZM;;;AGpBR;EACE,OHqBM;;;AItBR;EACE,OJgBM;;;AKjBR;EACE;EACA,OLsBI;;;AKnBN;EACE,OLkBI;;;AMxBN;EACE,ONeK;;;AOfL;EACE;EACA;EACA;EACA;EACA;EACA;EACA,kBPmBO;;AOjBP;EACE,kBPQG;EOPH;EACA;;AAGF;EACE,kBPOA;EONA;EACA;;;AAMN;EACE;;;AC1BF;EACE,ORoBO;;;ASrBT;EACE;;;AAGF;EACE,YTiCO;;;AS/BT;EACE;EACA,OTkBS;;;ASfX;EACE,kBTsBS;;;ASnBX;EACE,kBTmBS;ESlBT;;;AAGF;EACE,kBTKS;;;AU3BX;EACE,OViBI;EUhBJ;;;ACFF;EACE,YXqCO;EWpCP;EACA;;;AAGF;EACE,YX6BS;EW5BT;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE","file":"style.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["scss/main.scss","scss/common/common.scss","scss/colors.scss","scss/common/widget-button.scss","scss/bar/menu.scss","scss/bar/audio.scss","scss/bar/media.scss","scss/bar/network.scss","scss/bar/bluetooth.scss","scss/bar/clock.scss","scss/bar/workspace.scss","scss/bar/window_title.scss","scss/bar/systray.scss","scss/bar/power.scss","scss/bar/bar.scss","scss/menus/powermenu.scss","scss/common/floating-widget.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;ACFF;EACE;EACA;EACA,kBCFgB;EDGhB;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI,kBChBW;;ADkBf;EACE,OCnBa;;ADqBf;EACE;EACA;EACA;EACA;;AAGJ;EACE;EACA;EACA,kBC/Be;;;ADmCnB;EACE;EACA;EACA;EACA,OCvCiB;;;AD0CnB;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA,kBC9DiB;ED+DjB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;EACA,kBC9EgB;;;ADiFlB;AAAA;EAEE;EACA,kBCpFgB;;;ADuFlB;AAAA;AAAA;EAGE,OC1FgB;ED2FhB,kBC7CW;;;ADgDb;AAAA;AAAA;EAGE;EACA,kBCpDW;;;ADuDb;AAAA;AAAA;EAGE;EACA,kBC3DW;;;AD8Db;EACE;EACA;EACA;EACA;EACA,kBChHiB;EDiHjB;EACA;EACA;;;AAGF;EACE,kBC1EW;;;AD6Eb;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA,kBCvIc;EDwId;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA,kBChKiB;;;ADmKnB;EACE;EACA,kBCxHW;;;AD2Hb;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA,OC1IW;ED2IX,kBCzLgB;ED0LhB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,kBC1MgB;ED2MhB,OC5Mc;ED6Md;;;AAGF;EACE;EACA;EACA,kBCjNiB;;;ADoNnB;EACE;IACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AE/NF;EACE,YDmCM;EClCN;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cD2BM;EC1BN;EACA;;AAEA;EACE;EACA,cDSO;ECRP;;AAGF;EAEE;EACA,cDEO;;ACCT;EACE;EACA;EACA;EACA;;AAGF;EACE,YApCM;;AAyCN;EACE;EACJ;;AAEE;EACE;EACJ;;;AC/CF;EACE,OFsBK;EErBL;EACA;;;ACFA;EACE;;;AAIJ;EACE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA,OHRM;;;AGWR;EACE,OHZM;;;AIpBR;EACE;EACA,OJoBM;;;AKtBR;EACE,OLgBM;;;AMjBR;EACE;EACA,ONsBI;;;AMnBN;EACE,ONkBI;;;AOxBN;EACE,OPeK;;;AQfL;EACE;EACA;EACA;EACA;EACA;EACA;EACA,kBRmBO;;AQjBP;EACE,kBRQG;EQPH;EACA;;AAGF;EACE,kBROA;EQNA;EACA;;;AAMN;EACE;;;AC1BF;EACE,OTeK;;;AUhBP;EACE;;;AAGF;EACE,YViCO;;;AU/BT;EACE;EACA,OVkBS;;;AUfX;EACE,kBVsBS;;;AUnBX;EACE,kBVmBS;EUlBT;;;AAGF;EACE,kBVKS;;;AW3BX;EACE,OXiBI;EWhBJ;;;ACFF;EACE,YZqCO;EYpCP;EACA;;;AAGF;EACE,YZ6BS;EY5BT;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;ACzBF;AAAA;EAGI;;;AAQJ;ECTI;EACA;EACA,kBdiCG;EchCH,OdwCS;EcvCT;EACA;EDMA;EACA;EACA;;AAEA;EACI,eAVE;;AAYF;EACI;;AAGJ;EACI;EACA;;;AAKZ;EC7BI;EACA;EACA,kBdiCG;EchCH,OdwCS;EcvCT;EACA;;AD0BA;EACI;;AAGJ;EACI;;;AAMA;EACI,cb3BN;;Aa6BE;EACI,cb1BJ;;Aa4BA;EACI,cb3BN;;Aa6BE;EACI,cblCJ;;AawCA;EACI;;AAEJ;EACI;;AAEJ;EACI;;AAEJ;EACI;;;AAKR;EACI,Ob1DF;;Aa4DF;EACI,ObzDA;;Aa2DJ;EACI,Ob9DA;;AagEJ;EACI,Ob7DF;;;AakEF;EACI,ObzEF;;Aa2EF;EACI,ObxEA;;Aa0EJ;EACI,Ob7EA;;Aa+EJ;EACI,Ob5EF","file":"style.css"} \ No newline at end of file