diff --git a/desktops/hyprland/ags/config.js b/desktops/hyprland/ags/config.js new file mode 100644 index 0000000..206debb --- /dev/null +++ b/desktops/hyprland/ags/config.js @@ -0,0 +1,51 @@ +// Import +import { App, Utils } from './imports.js'; +// Windows +import Bar from './windows/bar.js'; +import Cheatsheet from './windows/cheatsheet.js'; +import { CornerTopleft, CornerTopright, CornerBottomleft, CornerBottomright } from './windows/corners.js'; +import Indicator from './windows/osd.js'; +import Osk from './windows/osk.js'; +import Overview from './windows/overview.js'; +import Session from './windows/session.js'; +import SideLeft from './windows/sideleft.js'; +import SideRight from './windows/sideright.js'; + +const CLOSE_ANIM_TIME = 150; + +// Init +Utils.exec(`bash -c 'mkdir -p ~/.cache/ags/user'`); +// SCSS compilation +Utils.exec(`sassc ${App.configDir}/scss/main.scss ${App.configDir}/style.css`); +App.resetCss(); +App.applyCss(`${App.configDir}/style.css`); + +// Config object +export default { + style: `${App.configDir}/style.css`, + stackTraceOnError: true, + closeWindowDelay: { + // For animations + 'sideright': CLOSE_ANIM_TIME, + 'sideleft': CLOSE_ANIM_TIME, + 'osk': CLOSE_ANIM_TIME, + // No anims, but allow menu service update + 'session': 1, + 'overview': 1, + 'cheatsheet': 1, + }, + windows: [ + Bar(), + CornerTopleft(), + CornerTopright(), + CornerBottomleft(), + CornerBottomright(), + Overview(), + Indicator(), + Cheatsheet(), + SideRight(), + SideLeft(), + Osk(), // On-screen keyboard + Session(), + ], +}; diff --git a/desktops/hyprland/ags/data/keybinds.js b/desktops/hyprland/ags/data/keybinds.js new file mode 100644 index 0000000..3aa0a7d --- /dev/null +++ b/desktops/hyprland/ags/data/keybinds.js @@ -0,0 +1,132 @@ +export const keybindList = [[ + { + "icon": "pin_drop", + "name": "Workspaces: navigation", + "binds": [ + { "keys": ["", "+", "#"], "action": "Go to workspace #" }, + { "keys": ["", "+", "S"], "action": "Toggle special workspace" }, + { "keys": ["", "+", "(Scroll ↑↓)"], "action": "Go to workspace -1/+1" }, + { "keys": ["Ctrl", "", "+", "←"], "action": "Go to workspace on the left" }, + { "keys": ["Ctrl", "", "+", "→"], "action": "Go to workspace on the right" }, + { "keys": ["", "+", "PageUp"], "action": "Go to workspace on the left" }, + { "keys": ["", "+", "PageDown"], "action": "Go to workspace on the right" } + ], + "appeartick": 1 + }, + { + "icon": "overview_key", + "name": "Workspaces: management", + "binds": [ + { "keys": ["", "Alt", "+", "#"], "action": "Move window to workspace #" }, + { "keys": ["", "Alt", "+", "S"], "action": "Move window to special workspace" }, + { "keys": ["", "Alt", "+", "PageUp"], "action": "Move window to workspace on the left" }, + { "keys": ["", "Alt", "+", "PageDown"], "action": "Move window to workspace on the right" } + ], + "appeartick": 1 + }, + { + "icon": "move_group", + "name": "Windows", + "binds": [ + { "keys": ["", "+", "←↑→↓"], "action": "Focus window in direction" }, + { "keys": ["", "Shift", "+", "←↑→↓"], "action": "Swap window in direction" }, + { "keys": ["", "+", ";"], "action": "Split ratio -" }, + { "keys": ["", "+", "'"], "action": "Split ratio +" }, + { "keys": ["", "+", "Lmb"], "action": "Move window" }, + { "keys": ["", "+", "Mmb"], "action": "Move window" }, + { "keys": ["", "+", "Rmb"], "action": "Resize window" }, + { "keys": ["", "+", "F"], "action": "Fullscreen" }, + { "keys": ["", "Alt", "+", "F"], "action": "Fake fullscreen" } + ], + "appeartick": 1 + } +], +[ + { + "icon": "widgets", + "name": "Widgets (AGS)", + "binds": [ + { "keys": ["", "OR", "", "+", "Tab"], "action": "Toggle overview/launcher" }, + { "keys": ["Ctrl", "", "+", "R"], "action": "Restart AGS" }, + { "keys": ["", "+", "/"], "action": "Toggle this cheatsheet" }, + { "keys": ["", "+", "N"], "action": "Toggle sidebar" }, + { "keys": ["", "+", "K"], "action": "Toggle virtual keyboard" }, + { "keys": ["Ctrl", "Alt", "+", "Del"], "action": "Power/Session menu" }, + + { "keys": ["Esc"], "action": "Exit a window" }, + { "keys": ["rightCtrl"], "action": "Dismiss/close sidebar" }, + + // { "keys": ["", "+", "B"], "action": "Toggle left sidebar" }, + // { "keys": ["", "+", "N"], "action": "Toggle right sidebar" }, + // { "keys": ["", "+", "G"], "action": "Toggle volume mixer" }, + // { "keys": ["", "+", "M"], "action": "Toggle useless audio visualizer" }, + // { "keys": ["(right)Ctrl"], "action": "Dismiss notification & close menus" } + ], + "appeartick": 2 + }, + { + "icon": "construction", + "name": "Utilities", + "binds": [ + { "keys": ["PrtSc"], "action": "Screenshot >> clipboard" }, + { "keys": ["", "Shift", "+", "S"], "action": "Screen snip >> clipboard" }, + { "keys": ["", "Shift", "+", "T"], "action": "Image to text >> clipboard" }, + { "keys": ["", "Shift", "+", "C"], "action": "Color picker" }, + { "keys": ["", "Alt", "+", "R"], "action": "Record region" }, + { "keys": ["Ctrl", "Alt", "+", "R"], "action": "Record region with sound" }, + { "keys": ["", "Shift", "Alt", "+", "R"], "action": "Record screen with sound" } + ], + "appeartick": 2 + }, + // { + // "icon": "edit", + // "name": "Edit mode", + // "binds": [ + // { "keys": ["Esc"], "action": "Exit Edit mode" }, + // { "keys": ["#"], "action": "Go to to workspace #" }, + // { "keys": ["Alt", "+", "#"], "action": "Dump windows to workspace #" }, + // { "keys": ["Shift", "+", "#"], "action": "Swap windows with workspace #" }, + // { "keys": ["Lmb"], "action": "Move window" }, + // { "keys": ["Mmb"], "action": "Move window" }, + // { "keys": ["Rmb"], "action": "Resize window" } + // ], + // "appeartick": 2 + // } +], +[ + { + "icon": "apps", + "name": "Apps", + "binds": [ + { "keys": ["", "+", "T"], "action": "Launch terminal: foot" }, + { "keys": ["", "+", "↵"], "action": "Launch terminal: WezTerm" }, + { "keys": ["", "+", "W"], "action": "Launch browser: Firefox" }, + { "keys": ["", "+", "C"], "action": "Launch editor: vscode" }, + { "keys": ["", "+", "X"], "action": "Launch editor: GNOME Text Editor" }, + { "keys": ["", "+", "I"], "action": "Launch settings: GNOME Control center" } + ], + "appeartick": 3 + }, + { + "icon": "keyboard", + "name": "Typing", + "binds": [ + { "keys": ["", "+", "V"], "action": "Clipboard history >> clipboard" }, + { "keys": ["", "+", "."], "action": "Emoji picker >> clipboard" }, + { "keys": ["", "+", " 󱁐 "], "action": "Switch language" } + ], + "appeartick": 3 + }, + { + "icon": "terminal", + "name": "Launcher commands", + "binds": [ + { "keys": [">raw"], "action": "Toggle mouse acceleration" }, + { "keys": [">img"], "action": "Select wallpaper and generate colorscheme" }, + { "keys": [">light"], "action": "Use light theme for next color generations" }, + { "keys": [">dark"], "action": "Use dark theme for next color generations" }, + { "keys": [">todo"], "action": "Type something after that to add a To-do item" }, + ], + "appeartick": 3 + } +]]; diff --git a/desktops/hyprland/ags/data/keyboardlayouts.js b/desktops/hyprland/ags/data/keyboardlayouts.js new file mode 100644 index 0000000..f58e378 --- /dev/null +++ b/desktops/hyprland/ags/data/keyboardlayouts.js @@ -0,0 +1,114 @@ +// We're going to use ydotool +// See /usr/include/linux/input-event-codes.h for keycodes + +export const defaultOskLayout = "qwerty_full" +export const oskLayouts = { + qwerty_full: { + name: "QWERTY - Full", + name_short: "US", + comment: "Like physical keyboard", + // A key looks like this: { k: "a", ks: "A", t: "normal" } (key, key-shift, type) + // key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand + // keys: [ + // [{ k: "Esc", t: "fn" }, { k: "F1", t: "fn" }, { k: "F2", t: "fn" }, { k: "F3", t: "fn" }, { k: "F4", t: "fn" }, { k: "F5", t: "fn" }, { k: "F6", t: "fn" }, { k: "F7", t: "fn" }, { k: "F8", t: "fn" }, { k: "F9", t: "fn" }, { k: "F10", t: "fn" }, { k: "F11", t: "fn" }, { k: "F12", t: "fn" }, { k: "PrtSc", t: "fn" }, { k: "Del", t: "fn" }], + // [{ k: "`", ks: "~", t: "normal" }, { k: "1", ks: "!", t: "normal" }, { k: "2", ks: "@", t: "normal" }, { k: "3", ks: "#", t: "normal" }, { k: "4", ks: "$", t: "normal" }, { k: "5", ks: "%", t: "normal" }, { k: "6", ks: "^", t: "normal" }, { k: "7", ks: "&", t: "normal" }, { k: "8", ks: "*", t: "normal" }, { k: "9", ks: "(", t: "normal" }, { k: "0", ks: ")", t: "normal" }, { k: "-", ks: "_", t: "normal" }, { k: "=", ks: "+", t: "normal" }, { k: "Backspace", t: "shift" }], + // [{ k: "Tab", t: "tab" }, { k: "q", ks: "Q", t: "normal" }, { k: "w", ks: "W", t: "normal" }, { k: "e", ks: "E", t: "normal" }, { k: "r", ks: "R", t: "normal" }, { k: "t", ks: "T", t: "normal" }, { k: "y", ks: "Y", t: "normal" }, { k: "u", ks: "U", t: "normal" }, { k: "i", ks: "I", t: "normal" }, { k: "o", ks: "O", t: "normal" }, { k: "p", ks: "P", t: "normal" }, { k: "[", ks: "{", t: "normal" }, { k: "]", ks: "}", t: "normal" }, { k: "\\", ks: "|", t: "expand" }], + // [{ k: "Caps", t: "caps" }, { k: "a", ks: "A", t: "normal" }, { k: "s", ks: "S", t: "normal" }, { k: "d", ks: "D", t: "normal" }, { k: "f", ks: "F", t: "normal" }, { k: "g", ks: "G", t: "normal" }, { k: "h", ks: "H", t: "normal" }, { k: "j", ks: "J", t: "normal" }, { k: "k", ks: "K", t: "normal" }, { k: "l", ks: "L", t: "normal" }, { k: ";", ks: ":", t: "normal" }, { k: "'", ks: '"', t: "normal" }, { k: "Enter", t: "expand" }], + // [{ k: "Shift", t: "shift" }, { k: "z", ks: "Z", t: "normal" }, { k: "x", ks: "X", t: "normal" }, { k: "c", ks: "C", t: "normal" }, { k: "v", ks: "V", t: "normal" }, { k: "b", ks: "B", t: "normal" }, { k: "n", ks: "N", t: "normal" }, { k: "m", ks: "M", t: "normal" }, { k: ",", ks: "<", t: "normal" }, { k: ".", ks: ">", t: "normal" }, { k: "/", ks: "?", t: "normal" }, { k: "Shift", t: "expand" }], + // [{ k: "Ctrl", t: "control" }, { k: "Fn", t: "normal" }, { k: "Win", t: "normal" }, { k: "Alt", t: "normal" }, { k: "Space", t: "space" }, { k: "Alt", t: "normal" }, { k: "Menu", t: "normal" }, { k: "Ctrl", t: "control" }] + // ] + // A normal key looks like this: {label: "a", labelShift: "A", shape: "normal", keycode: 30, type: "normal"} + // A modkey looks like this: {label: "Ctrl", shape: "control", keycode: 29, type: "modkey"} + // key types are: normal, tab, caps, shift, control, fn (normal w/ half height), space, expand + keys: [ + [ + { keytype: "normal", label: "Esc", shape: "fn", keycode: 1 }, + { keytype: "normal", label: "F1", shape: "fn", keycode: 59 }, + { keytype: "normal", label: "F2", shape: "fn", keycode: 60 }, + { keytype: "normal", label: "F3", shape: "fn", keycode: 61 }, + { keytype: "normal", label: "F4", shape: "fn", keycode: 62 }, + { keytype: "normal", label: "F5", shape: "fn", keycode: 63 }, + { keytype: "normal", label: "F6", shape: "fn", keycode: 64 }, + { keytype: "normal", label: "F7", shape: "fn", keycode: 65 }, + { keytype: "normal", label: "F8", shape: "fn", keycode: 66 }, + { keytype: "normal", label: "F9", shape: "fn", keycode: 67 }, + { keytype: "normal", label: "F10", shape: "fn", keycode: 68 }, + { keytype: "normal", label: "F11", shape: "fn", keycode: 87 }, + { keytype: "normal", label: "F12", shape: "fn", keycode: 88 }, + { keytype: "normal", label: "PrtSc", shape: "fn", keycode: 99 }, + { keytype: "normal", label: "Del", shape: "fn", keycode: 111 } + ], + [ + { keytype: "normal", label: "`", labelShift: "~", shape: "normal", keycode: 41 }, + { keytype: "normal", label: "1", labelShift: "!", shape: "normal", keycode: 2 }, + { keytype: "normal", label: "2", labelShift: "@", shape: "normal", keycode: 3 }, + { keytype: "normal", label: "3", labelShift: "#", shape: "normal", keycode: 4 }, + { keytype: "normal", label: "4", labelShift: "$", shape: "normal", keycode: 5 }, + { keytype: "normal", label: "5", labelShift: "%", shape: "normal", keycode: 6 }, + { keytype: "normal", label: "6", labelShift: "^", shape: "normal", keycode: 7 }, + { keytype: "normal", label: "7", labelShift: "&", shape: "normal", keycode: 8 }, + { keytype: "normal", label: "8", labelShift: "*", shape: "normal", keycode: 9 }, + { keytype: "normal", label: "9", labelShift: "(", shape: "normal", keycode: 10 }, + { keytype: "normal", label: "0", labelShift: ")", shape: "normal", keycode: 11 }, + { keytype: "normal", label: "-", labelShift: "_", shape: "normal", keycode: 12 }, + { keytype: "normal", label: "=", labelShift: "+", shape: "normal", keycode: 13 }, + { keytype: "normal", label: "Backspace", shape: "expand", keycode: 14 } + ], + [ + { keytype: "normal", label: "Tab", shape: "tab", keycode: 15 }, + { keytype: "normal", label: "q", labelShift: "Q", shape: "normal", keycode: 16 }, + { keytype: "normal", label: "w", labelShift: "W", shape: "normal", keycode: 17 }, + { keytype: "normal", label: "e", labelShift: "E", shape: "normal", keycode: 18 }, + { keytype: "normal", label: "r", labelShift: "R", shape: "normal", keycode: 19 }, + { keytype: "normal", label: "t", labelShift: "T", shape: "normal", keycode: 20 }, + { keytype: "normal", label: "y", labelShift: "Y", shape: "normal", keycode: 21 }, + { keytype: "normal", label: "u", labelShift: "U", shape: "normal", keycode: 22 }, + { keytype: "normal", label: "i", labelShift: "I", shape: "normal", keycode: 23 }, + { keytype: "normal", label: "o", labelShift: "O", shape: "normal", keycode: 24 }, + { keytype: "normal", label: "p", labelShift: "P", shape: "normal", keycode: 25 }, + { keytype: "normal", label: "[", labelShift: "{", shape: "normal", keycode: 26 }, + { keytype: "normal", label: "]", labelShift: "}", shape: "normal", keycode: 27 }, + { keytype: "normal", label: "\\", labelShift: "|", shape: "expand", keycode: 43 } + ], + [ + { keytype: "normal", label: "Caps", shape: "caps", keycode: 58 }, + { keytype: "normal", label: "a", labelShift: "A", shape: "normal", keycode: 30 }, + { keytype: "normal", label: "s", labelShift: "S", shape: "normal", keycode: 31 }, + { keytype: "normal", label: "d", labelShift: "D", shape: "normal", keycode: 32 }, + { keytype: "normal", label: "f", labelShift: "F", shape: "normal", keycode: 33 }, + { keytype: "normal", label: "g", labelShift: "G", shape: "normal", keycode: 34 }, + { keytype: "normal", label: "h", labelShift: "H", shape: "normal", keycode: 35 }, + { keytype: "normal", label: "j", labelShift: "J", shape: "normal", keycode: 36 }, + { keytype: "normal", label: "k", labelShift: "K", shape: "normal", keycode: 37 }, + { keytype: "normal", label: "l", labelShift: "L", shape: "normal", keycode: 38 }, + { keytype: "normal", label: ";", labelShift: ":", shape: "normal", keycode: 39 }, + { keytype: "normal", label: "'", labelShift: '"', shape: "normal", keycode: 40 }, + { keytype: "normal", label: "Enter", shape: "expand", keycode: 28 } + ], + [ + { keytype: "modkey", label: "Shift", shape: "shift", keycode: 42 }, + { keytype: "normal", label: "z", labelShift: "Z", shape: "normal", keycode: 44 }, + { keytype: "normal", label: "x", labelShift: "X", shape: "normal", keycode: 45 }, + { keytype: "normal", label: "c", labelShift: "C", shape: "normal", keycode: 46 }, + { keytype: "normal", label: "v", labelShift: "V", shape: "normal", keycode: 47 }, + { keytype: "normal", label: "b", labelShift: "B", shape: "normal", keycode: 48 }, + { keytype: "normal", label: "n", labelShift: "N", shape: "normal", keycode: 49 }, + { keytype: "normal", label: "m", labelShift: "M", shape: "normal", keycode: 50 }, + { keytype: "normal", label: ",", labelShift: "<", shape: "normal", keycode: 51 }, + { keytype: "normal", label: ".", labelShift: ">", shape: "normal", keycode: 52 }, + { keytype: "normal", label: "/", labelShift: "?", shape: "normal", keycode: 53 }, + { keytype: "modkey", label: "Shift", shape: "expand", keycode: 54 } + ], + [ + { keytype: "modkey", label: "Ctrl", shape: "control", keycode: 29 }, + // { label: "Super", shape: "normal", keycode: 125 }, // dangerous + { keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 }, + { keytype: "normal", label: "Space", shape: "space", keycode: 57 }, + { keytype: "modkey", label: "Alt", shape: "normal", keycode: 100 }, + // { label: "Super", shape: "normal", keycode: 126 }, // dangerous + { keytype: "normal", label: "Menu", shape: "normal", keycode: 139 }, + { keytype: "modkey", label: "Ctrl", shape: "control", keycode: 97 } + ] + ] + } +} \ No newline at end of file diff --git a/desktops/hyprland/ags/data/quotes.js b/desktops/hyprland/ags/data/quotes.js new file mode 100644 index 0000000..7cea990 --- /dev/null +++ b/desktops/hyprland/ags/data/quotes.js @@ -0,0 +1,14 @@ +export const quotes = [ + { + quote: 'Nvidia, fuck you', + author: 'Linus Torvalds', + }, + { + quote: 'reproducible system? cock and vagina?', + author: 'vaxry', + }, + { + quote: "haha pointers hee hee i love pointe-\\\nProcess Vaxry exited with signal SIGSEGV", + author: 'vaxry', + } +]; diff --git a/desktops/hyprland/ags/imports.js b/desktops/hyprland/ags/imports.js new file mode 100644 index 0000000..89aa0db --- /dev/null +++ b/desktops/hyprland/ags/imports.js @@ -0,0 +1,40 @@ +const resource = file => `resource:///com/github/Aylur/ags/${file}.js`; +const require = async file => (await import(resource(file))).default; +const service = async file => (await require(`service/${file}`)); + +export const App = await require('app'); +export const Widget = await require('widget'); +export const Service = await require('service'); +export const Variable = await require('variable'); +export const Utils = await import(resource('utils')); + +export const Applications = await service('applications'); +export const Audio = await service('audio'); +export const Battery = await service('battery'); +export const Bluetooth = await service('bluetooth'); +export const Hyprland = await service('hyprland'); +export const Mpris = await service('mpris'); +export const Network = await service('network'); +export const Notifications = await service('notifications'); +export const SystemTray = await service('systemtray'); + +globalThis['App'] = App; ////////////////////////////// +// globalThis['Widget'] = Widget; +// globalThis['Service'] = Service; +// globalThis['Variable'] = Variable; +globalThis['Utils'] = Utils; /////////////////////////// +// globalThis['Applications'] = Applications; +// globalThis['Audio'] = Audio; +// globalThis['Battery'] = Battery; +// globalThis['Bluetooth'] = Bluetooth; +// globalThis['Hyprland'] = Hyprland; +// globalThis['Mpris'] = Mpris; +// globalThis['Network'] = Network; +globalThis['Notifications'] = Notifications; +// globalThis['SystemTray'] = SystemTray; + +const { exec } = Utils; +const SCREEN_WIDTH = Number(exec(`bash -c "xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f1 | head -1"`)); +const SCREEN_HEIGHT = Number(exec(`bash -c "xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2 | head -1"`)); +globalThis['SCREEN_WIDTH'] = SCREEN_WIDTH; +globalThis['SCREEN_HEIGHT'] = SCREEN_HEIGHT; \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/calendar.js b/desktops/hyprland/ags/modules/calendar.js new file mode 100644 index 0000000..a349b13 --- /dev/null +++ b/desktops/hyprland/ags/modules/calendar.js @@ -0,0 +1,364 @@ +const { Gio, Gdk, Gtk } = imports.gi; +import { App, Widget, Utils } from '../imports.js'; +const { Box, CenterBox, Label, Button } = Widget; +import { MaterialIcon } from "./lib/materialicon.js"; +import { getCalendarLayout } from "../scripts/calendarlayout.js"; +import Todo from "../scripts/todo.js"; +import { setupCursorHover } from "./lib/cursorhover.js"; +import { NavigationIndicator } from "./lib/navigationindicator.js"; + +let calendarJson = getCalendarLayout(undefined, true); +let monthshift = 0; + +function fileExists(filePath) { + let file = Gio.File.new_for_path(filePath); + return file.query_exists(null); +} + +function getDateInXMonthsTime(x) { + var currentDate = new Date(); // Get the current date + var targetMonth = currentDate.getMonth() + x; // Calculate the target month + var targetYear = currentDate.getFullYear(); // Get the current year + + // Adjust the year and month if necessary + targetYear += Math.floor(targetMonth / 12); + targetMonth = (targetMonth % 12 + 12) % 12; + + // Create a new date object with the target year and month + var targetDate = new Date(targetYear, targetMonth, 1); + + // Set the day to the last day of the month to get the desired date + // targetDate.setDate(0); + + return targetDate; +} + +const weekDays = [ // stupid stupid stupid!! how tf is Sunday the first day of the week?? + { day: 'Su', today: 0 }, + { day: 'Mo', today: 0 }, + { day: 'Tu', today: 0 }, + { day: 'We', today: 0 }, + { day: 'Th', today: 0 }, + { day: 'Fr', today: 0 }, + { day: 'Sa', today: 0 }, +] + +const CalendarDay = (day, today) => Widget.Button({ + className: `sidebar-calendar-btn ${today == 1 ? 'sidebar-calendar-btn-today' : (today == -1 ? 'sidebar-calendar-btn-othermonth' : '')}`, + child: Widget.Overlay({ + child: Box({}), + overlays: [Label({ + halign: 'center', + className: 'txt-smallie txt-semibold sidebar-calendar-btn-txt', + label: String(day), + })], + }) +}) + +const CalendarWidget = () => { + const calendarMonthYear = Widget.Button({ + className: 'txt txt-large sidebar-calendar-monthyear-btn', + onClicked: () => shiftCalendarXMonths(0), + setup: (button) => { + button.label = `${new Date().toLocaleString('default', { month: 'long' })} ${new Date().getFullYear()}`; + setupCursorHover(button); + } + }); + const addCalendarChildren = (box, calendarJson) => { + box.children = calendarJson.map((row, i) => Widget.Box({ + // homogeneous: true, + className: 'spacing-h-5', + children: row.map((day, i) => + CalendarDay(day.day, day.today) + ) + })) + } + function shiftCalendarXMonths(x) { + if (x == 0) + monthshift = 0; + else + monthshift += x; + var newDate = undefined; + if (monthshift == 0) + newDate = new Date(); + else + newDate = getDateInXMonthsTime(monthshift); + calendarJson = getCalendarLayout(newDate, monthshift == 0); + calendarMonthYear.label = `${monthshift == 0 ? '' : '• '}${newDate.toLocaleString('default', { month: 'long' })} ${newDate.getFullYear()}`; + addCalendarChildren(calendarDays, calendarJson); + } + const calendarHeader = Widget.Box({ + className: 'spacing-h-5 sidebar-calendar-header', + setup: (box) => { + box.pack_start(calendarMonthYear, false, false, 0); + box.pack_end(Widget.Box({ + className: 'spacing-h-5', + children: [ + Button({ + className: 'sidebar-calendar-monthshift-btn', + onClicked: () => shiftCalendarXMonths(-1), + child: MaterialIcon('chevron_left', 'norm'), + setup: (button) => setupCursorHover(button), + }), + Button({ + className: 'sidebar-calendar-monthshift-btn', + onClicked: () => shiftCalendarXMonths(1), + child: MaterialIcon('chevron_right', 'norm'), + setup: (button) => setupCursorHover(button), + }) + ] + }), false, false, 0); + } + }) + const calendarDays = Widget.Box({ + hexpand: true, + vertical: true, + className: 'spacing-v-5', + setup: (box) => { + addCalendarChildren(box, calendarJson); + } + }); + return Widget.EventBox({ + onScrollUp: () => shiftCalendarXMonths(-1), + onScrollDown: () => shiftCalendarXMonths(1), + child: Widget.Box({ + halign: 'center', + children: [ + Widget.Box({ + hexpand: true, + vertical: true, + className: 'spacing-v-5', + children: [ + calendarHeader, + Widget.Box({ + homogeneous: true, + className: 'spacing-h-5', + children: weekDays.map((day, i) => CalendarDay(day.day, day.today)) + }), + calendarDays, + ] + }) + ] + }) + }); +}; + +const defaultTodoSelected = 'undone'; + +const todoItems = (isDone) => Widget.Scrollable({ + child: Widget.Box({ + vertical: true, + connections: [[Todo, (self) => { + self.children = Todo.todo_json.map((task, i) => { + if (task.done != isDone) return null; + return Widget.Box({ + className: 'spacing-h-5', + children: [ + Widget.Label({ + className: 'txt txt-small', + label: '•', + }), + Widget.Label({ + hexpand: true, + xalign: 0, + wrap: true, + className: 'txt txt-small sidebar-todo-txt', + label: task.content, + }), + Widget.Button({ + valign: 'center', + className: 'txt sidebar-todo-item-action', + child: MaterialIcon(`${isDone ? 'remove_done' : 'check'}`, 'norm', { valign: 'center' }), + onClicked: () => { + if (isDone) + Todo.uncheck(i); + else + Todo.check(i); + }, + setup: (button) => setupCursorHover(button), + }), + Widget.Button({ + valign: 'center', + className: 'txt sidebar-todo-item-action', + child: MaterialIcon('delete_forever', 'norm', { valign: 'center' }), + onClicked: () => { + Todo.remove(i); + }, + setup: (button) => setupCursorHover(button), + }), + ] + }); + }) + if (self.children.length == 0) { + self.homogeneous = true; + self.children = [ + Widget.Box({ + hexpand: true, + vertical: true, + valign: 'center', + className: 'txt', + children: [ + MaterialIcon(`${isDone ? 'checklist' : 'check_circle'}`, 'badonkers'), + Label({ label: `${isDone ? 'Finished tasks will go here' : 'Nothing here!'}` }) + ] + }) + ] + } + else self.homogeneous = false; + }, 'updated']] + }) +}); + +const todoItemsBox = Widget.Stack({ + valign: 'fill', + transition: 'slide_left_right', + items: [ + ['undone', todoItems(false)], + ['done', todoItems(true)], + ], +}); + +const TodoWidget = () => { + const TodoTabButton = (isDone, navIndex) => Widget.Button({ + hexpand: true, + className: 'sidebar-todo-selector-tab', + onClicked: (button) => { + todoItemsBox.shown = `${isDone ? 'done' : 'undone'}`; + const kids = button.get_parent().get_children(); + for (let i = 0; i < kids.length; i++) { + if (kids[i] != button) kids[i].toggleClassName('sidebar-todo-selector-tab-active', false); + else button.toggleClassName('sidebar-todo-selector-tab-active', true); + } + // Fancy highlighter line width + const buttonWidth = button.get_allocated_width(); + const highlightWidth = button.get_children()[0].get_allocated_width(); + navIndicator.style = ` + font-size: ${navIndex}px; + padding: 0px ${(buttonWidth - highlightWidth) / 2}px; + `; + }, + child: Box({ + halign: 'center', + className: 'spacing-h-5', + children: [ + MaterialIcon(`${isDone ? 'task_alt' : 'format_list_bulleted'}`, 'larger'), + Label({ + className: 'txt txt-smallie', + label: `${isDone ? 'Done' : 'Unfinished'}`, + }) + ] + }), + setup: (button) => { + button.toggleClassName('sidebar-todo-selector-tab-active', defaultTodoSelected === `${isDone ? 'done' : 'undone'}`); + setupCursorHover(button); + }, + }); + const undoneButton = TodoTabButton(false, 0); + const doneButton = TodoTabButton(true, 1); + const navIndicator = NavigationIndicator(2, false, { + className: 'sidebar-todo-selector-highlight', + style: 'font-size: 0px;', + setup: (self) => { + // Fancy highlighter line width + const buttonWidth = undoneButton.get_allocated_width(); + const highlightWidth = undoneButton.get_children()[0].get_allocated_width(); + navIndicator.style = ` + font-size: ${navIndex}px; + padding: 0px ${(buttonWidth - highlightWidth) / 2}px; + `; + } + }) + return Widget.Box({ + hexpand: true, + vertical: true, + className: 'spacing-v-10', + setup: (box) => { + // undone/done selector rail + box.pack_start(Widget.Box({ + vertical: true, + children: [ + Widget.Box({ + className: 'sidebar-todo-selectors spacing-h-5', + homogeneous: true, + setup: (box) => { + box.pack_start(undoneButton, false, true, 0); + box.pack_start(doneButton, false, true, 0); + } + }), + Widget.Box({ + className: 'sidebar-todo-selector-highlight-offset', + homogeneous: true, + children: [navIndicator] + }) + ] + }), false, false, 0); + box.pack_end(todoItemsBox, true, true, 0); + } + }); +}; + +const defaultShown = 'calendar'; +const contentStack = Widget.Stack({ + hexpand: true, + items: [ + ['calendar', CalendarWidget()], + ['todo', TodoWidget()], + // ['stars', Widget.Label({ label: 'GitHub feed will be here' })], + ], + transition: 'slide_up_down', + transitionDuration: 180, + setup: (stack) => { + stack.shown = defaultShown; + } +}) + +const StackButton = (stackItemName, icon, name) => Widget.Button({ + className: 'button-minsize sidebar-navrail-btn sidebar-button-alone txt-small spacing-h-5', + onClicked: (button) => { + contentStack.shown = stackItemName; + const kids = button.get_parent().get_children(); + for (let i = 0; i < kids.length; i++) { + if (kids[i] != button) kids[i].toggleClassName('sidebar-navrail-btn-active', false); + else button.toggleClassName('sidebar-navrail-btn-active', true); + } + }, + child: Box({ + className: 'spacing-v-5', + vertical: true, + children: [ + Label({ + className: `txt icon-material txt-hugeass`, + label: icon, + }), + Label({ + label: name, + className: 'txt txt-smallie', + }), + ] + }), + setup: (button) => { + button.toggleClassName('sidebar-navrail-btn-active', defaultShown === stackItemName); + setupCursorHover(button); + } +}); + +export const ModuleCalendar = () => Box({ + className: 'sidebar-group spacing-h-5', + setup: (box) => { + box.pack_start(Box({ + valign: 'center', + homogeneous: true, + vertical: true, + className: 'sidebar-navrail spacing-v-10', + children: [ + StackButton('calendar', 'calendar_month', 'Calendar'), + StackButton('todo', 'lists', 'To Do'), + // StackButton(box, 'stars', 'star', 'GitHub'), + ] + }), false, false, 0); + // ags.Widget({ // TDOO: replace this sad default calendar with a custom one + // type: imports.gi.Gtk.Calendar, + // }), + box.pack_end(contentStack, false, false, 0); + } +}) diff --git a/desktops/hyprland/ags/modules/keybinds.js b/desktops/hyprland/ags/modules/keybinds.js new file mode 100644 index 0000000..3bcfec8 --- /dev/null +++ b/desktops/hyprland/ags/modules/keybinds.js @@ -0,0 +1,60 @@ +import { Widget } from '../imports.js'; +import { keybindList } from "../data/keybinds.js"; + +export const Keybinds = () => Widget.Box({ + vertical: false, + className: "spacing-h-15", + homogeneous: true, + children: keybindList.map((group, i) => Widget.Box({ // Columns + vertical: true, + className: "spacing-v-15", + children: group.map((category, i) => Widget.Box({ // Categories + vertical: true, + className: "spacing-v-15", + children: [ + Widget.Box({ // Category header + vertical: false, + className: "spacing-h-10", + children: [ + Widget.Label({ + xalign: 0, + className: "icon-material txt txt-larger", + label: category.icon, + }), + Widget.Label({ + xalign: 0, + className: "cheatsheet-category-title txt", + label: category.name, + }), + ] + }), + Widget.Box({ + vertical: false, + className: "spacing-h-10", + children: [ + Widget.Box({ // Keys + vertical: true, + homogeneous: true, + children: category.binds.map((keybinds, i) => Widget.Box({ // Binds + vertical: false, + children: keybinds.keys.map((key, i) => Widget.Label({ // Specific keys + className: `${key == 'OR' || key == '+' ? 'cheatsheet-key-notkey' : 'cheatsheet-key'} txt-small`, + label: key, + })) + })) + }), + Widget.Box({ // Actions + vertical: true, + homogeneous: true, + children: category.binds.map((keybinds, i) => Widget.Label({ // Binds + xalign: 0, + label: keybinds.action, + className: "txt chearsheet-action txt-small", + })) + }) + ] + }) + ] + })) + })), +}); \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/leftspace.js b/desktops/hyprland/ags/modules/leftspace.js new file mode 100644 index 0000000..d1239a9 --- /dev/null +++ b/desktops/hyprland/ags/modules/leftspace.js @@ -0,0 +1,101 @@ +import { App, Service, Utils, Widget } from '../imports.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +const { CONFIG_DIR, exec, execAsync } = Utils; +import { deflisten } from '../scripts/scripts.js'; +import { setupCursorHover } from "./lib/cursorhover.js"; +import { RoundedCorner } from "./lib/roundedcorner.js"; +import Brightness from '../scripts/brightness.js'; +import Indicator from '../scripts/indicator.js'; + +// Removes everything after the last +// em dash, en dash, minus, vertical bar, or middle dot (note: maybe add open parenthesis?) +// For example: +// • Discord | #ricing-theming | r/unixporn — Mozilla Firefox --> • Discord | #ricing-theming +// GJS Error · Issue #112 · Aylur/ags — Mozilla Firefox --> GJS Error · Issue #112 +function truncateTitle(str) { + let lastDash = -1; + let found = -1; // 0: em dash, 1: en dash, 2: minus, 3: vertical bar, 4: middle dot + for (let i = str.length - 1; i >= 0; i--) { + if (str[i] === '—') { + found = 0; + lastDash = i; + } + else if (str[i] === '–' && found < 1) { + found = 1; + lastDash = i; + } + else if (str[i] === '-' && found < 2) { + found = 2; + lastDash = i; + } + else if (str[i] === '|' && found < 3) { + found = 3; + lastDash = i; + } + else if (str[i] === '·' && found < 4) { + found = 4; + lastDash = i; + } + } + if (lastDash === -1) return str; + return str.substring(0, lastDash); +} + +export const ModuleLeftSpace = () => Widget.EventBox({ + onScrollUp: () => { + Indicator.popup(1); // Since the brightness and speaker are both on the same window + Brightness.screen_value += 0.05; + }, + onScrollDown: () => { + Indicator.popup(1); // Since the brightness and speaker are both on the same window + Brightness.screen_value -= 0.05; + }, + child: Widget.Box({ + homogeneous: false, + children: [ + RoundedCorner('topleft', { className: 'corner-black' }), + Widget.Overlay({ + overlays: [ + Widget.Box({ hexpand: true }), + Widget.Box({ + className: 'bar-sidemodule', hexpand: true, + children: [Widget.Button({ + className: 'bar-space-button', + child: Widget.Box({ + vertical: true, + children: [ + Widget.Scrollable({ + hexpand: true, vexpand: true, + hscroll: 'automatic', vscroll: 'never', + child: Widget.Box({ + vertical: true, + children: [ + Widget.Label({ + xalign: 0, + className: 'txt txt-smaller bar-topdesc', + connections: [[Hyprland, label => { // Hyprland.active.client + label.label = Hyprland.active.client._class.length === 0 ? 'Desktop' : Hyprland.active.client._class; + }]], + }), + Widget.Label({ + xalign: 0, + className: 'txt txt-smallie', + connections: [ + [Hyprland, label => { // Hyprland.active.client + label.label = Hyprland.active.client._title.length === 0 ? `Workspace ${Hyprland.active.workspace.id}` : truncateTitle(Hyprland.active.client._title); + }] + ], + }) + ] + }) + }) + ] + }), + setup: (button) => setupCursorHover(button), + })] + }), + ] + }) + ] + }) +}); \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/lib/actioncenter.js b/desktops/hyprland/ags/modules/lib/actioncenter.js new file mode 100644 index 0000000..9b8fb60 --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/actioncenter.js @@ -0,0 +1,245 @@ +// Not yet used. For cool drag and drop stuff. Thanks DevAlien + +const Toggles = {}; +Toggles.Wifi = NetworkToggle; +Toggles.Bluetooth = BluetoothToggle; +Toggles.DND = DNDToggle; +Toggles.ThemeToggle = ThemeToggle; +Toggles.ProfileToggle = ProfileToggle; +// Toggles.Record = RecordToggle; +// Toggles.Airplane = AirplaneToggle; +// Toggles.DoNotDisturb = DoNotDisturbToggle; +const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)]; + +export class ActionCenter extends Gtk.Box { + static { + GObject.registerClass({ + GTypeName: 'ActionCenter', + Properties: { + + }, + }, this); + } + + constructor({ className = "ActionCenter", toggles, ...rest }) { + super(rest); + this.toggles = Toggles + this.currentToggles = Settings.getSetting("toggles", []); + this.mainFlowBox = this._setupFlowBox(className + QSView.editing && className + "Editing"); + this.mainFlowBox.connect("drag_motion", this._dragMotionMain); + this.mainFlowBox.connect("drag_drop", this._dragDropMain); + this._dragged = {}; + this._draggedExtra = {}; + + this._dragged; + this._currentPosition = 0; + this._orderedState; + this._draggedName; + + this.updateList(toggles, this.mainFlowBox) + + this.set_orientation(Gtk.Orientation.VERTICAL); + this.add(this.mainFlowBox) + this.mainFlowBox.set_size_request(1, 30) + if (QSView.editing) { + this.extraFlowBox = this._setupFlowBox(className); + this.extraFlowBox.connect("drag_motion", this._dragMotionExtra); + this.extraFlowBox.connect("drag_drop", this._dragDropExtra); + this.updateList(this._getExtraToggles(), this.extraFlowBox) + this.add(Box({ + vertical: true, + children: [ + Label("Extra widgets"), + Label("Drop here to remove or drag from here to add"), + this.extraFlowBox + ] + })) + } + } + + _getExtraToggles() { + let toggles = { ...this.toggles } + this.currentToggles.map(t => { + if (toggles[t]) { + delete toggles[t]; + } + }); + return Object.keys(toggles); + } + + _setupFlowBox(className) { + const flowBox = new Gtk.FlowBox(); + flowBox.set_valign(Gtk.Align.FILL); + flowBox.set_min_children_per_line(2); + flowBox.set_max_children_per_line(2); + flowBox.set_selection_mode(Gtk.SelectionMode.NONE); + flowBox.get_style_context().add_class(className); + flowBox.set_homogeneous(true); + flowBox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); + + return flowBox; + } + + createWidget = (name, index, type) => { + const editSetup = (widget) => { + widget.drag_source_set( + Gdk.ModifierType.BUTTON1_MASK, + TARGET, + Gdk.DragAction.COPY + ); + + widget.connect("drag-begin", (w, context) => { + const widgetContainer = widget.get_parent(); + + Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(widgetContainer)); + this._dragged = { + widget: widgetContainer.get_parent().get_parent(), + container: widgetContainer, + name: name, + currentPosition: type === "Main" ? index : null, + currentPositionExtra: type === "Extra" ? index : null, + from: type, + } + widgetContainer.get_style_context().add_class("hidden"); + if (type !== "Main") { + this.extraFlowBox.remove(this._dragged.widget); + } + + + return true; + }); + widget.connect("drag-failed", () => { + this.updateList(Settings.getSetting("toggles"), this.mainFlowBox) + this.updateList(this._getExtraToggles(), this.extraFlowBox) + }); + } + + let row = new Gtk.FlowBoxChild({ visible: true }); + row.add(Toggles[name]({ setup: QSView.editing && editSetup, QSView: QSView })); + row._index = index; + row._name = name; + return row; + } + + updateList(toggles, flowBox) { + let type = flowBox === this.mainFlowBox ? "Main" : "Extra" + var childrenBox = flowBox.get_children(); + childrenBox.forEach((element) => { + flowBox.remove(element); + element.destroy(); + }); + + if (!toggles) return; + + toggles.forEach((name, i) => { + if (Toggles[name]) + flowBox.add(this.createWidget(name, i, type)); + }); + flowBox.show_all(); + } + + + _dragMotionMain = (widget, context, x, y, time) => { + if (this._dragged.currentPositionExtra !== null) { + this._dragged.currentPositionExtra = null; + if (this._isChild(this.extraFlowBox, this._dragged.widget)) { + this.extraFlowBox.remove(this._dragged.widget); + } + } + const children = this.mainFlowBox.get_children(); + const sampleItem = children[0]; + const sampleWidth = sampleItem.get_allocation().width; + const sampleHeight = sampleItem.get_allocated_height(); + const perLine = Math.floor(this.mainFlowBox.get_allocation().width / sampleWidth); + const pos = Math.floor(y / sampleHeight) * perLine + Math.floor(x / sampleWidth); + if (pos >= children.length && pos !== 0) return false; + + if (this._dragged.currentPosition === null) { + this.mainFlowBox.insert(this._dragged.widget, pos); + + this._dragged.currentPosition = pos; + } else if (this._dragged.currentPosition !== pos) { + if (this._isChild(this.mainFlowBox, this._dragged.widget)) { + this.mainFlowBox.remove(this._dragged.widget); + } + + this.mainFlowBox.insert(this._dragged.widget, pos); + + this._dragged.currentPosition = pos; + } + + return true; + } + + _dragDropMain = () => { + if (this._dragged.from !== "Main") { + this.currentToggles.splice(this._dragged.currentPosition, 0, this._dragged.name); + } else { + const indexCurrentToggle = this.currentToggles.indexOf(this._dragged.name); + this.currentToggles.splice(indexCurrentToggle, 1); + this.currentToggles.splice(this._dragged.currentPosition, 0, this._dragged.name); + } + + Settings.setSetting("toggles", this.currentToggles); + this._dragged.container.get_style_context().remove_class("hidden"); + return true; + } + + _dragDropExtra = () => { + if (this._dragged.from === "Main") { + const indexCurrentToggle = this.currentToggles.indexOf(this._dragged.name); + this.currentToggles.splice(indexCurrentToggle, 1); + } + + Settings.setSetting("toggles", this.currentToggles); + this._dragged.container.get_style_context().remove_class("hidden"); + return true; + } + + _dragMotionExtra = (widget, context, x, y, time) => { + if (this._dragged.currentPosition !== null) { + this._dragged.currentPosition = null; + if (this._isChild(this.mainFlowBox, this._dragged.widget)) { + this.mainFlowBox.remove(this._dragged.widget); + } + } + + const children = this.extraFlowBox.get_children(); + const sampleItem = children[0]; + let pos = 0; + if (sampleItem) { + const sampleWidth = sampleItem.get_allocation().width; + const sampleHeight = sampleItem.get_allocated_height(); + const perLine = Math.floor(this.extraFlowBox.get_allocation().width / sampleWidth); + pos = Math.floor(y / sampleHeight) * perLine + Math.floor(x / sampleWidth); + } + + if (pos >= children.length && pos !== 0) return false; + + if (this._dragged.currentPositionExtra === null) { + this.extraFlowBox.insert(this._dragged.widget, pos); + + this._dragged.currentPositionExtra = pos; + } + + if (this._dragged.currentPositionExtra !== pos) { + if (this._isChild(this.extraFlowBox, this._dragged.widget)) { + this.extraFlowBox.remove(this._dragged.widget); + } + + this.extraFlowBox.insert(this._dragged.widget, pos); + + this._dragged.currentPositionExtra = pos; + } + + return true; + } + + _isChild(container, widget) { + let found = false; + container.get_children().forEach((c) => { + if (c === widget) found = true; + }) + return found; + } +} \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/lib/animatedcircularprogress.js b/desktops/hyprland/ags/modules/lib/animatedcircularprogress.js new file mode 100644 index 0000000..9ea548f --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/animatedcircularprogress.js @@ -0,0 +1,74 @@ +const { Gdk, Gtk } = imports.gi; +const GObject = imports.gi.GObject; +const Lang = imports.lang; +import { Utils, Widget } from '../../imports.js'; + +// min-height for diameter +// min-width for trough stroke +// padding for space between trough and progress +// margin for space between widget and parent +// background-color for trough color +// color for progress color +// font size for progress value (0-100px) (hacky i know, but i want animations) +// TODO: border-radius for rounded ends maybe (unimportant) +export const AnimatedCircProg = (props) => Widget({ + ...props, + type: Gtk.DrawingArea, + setup: (area) => { + const styleContext = area.get_style_context(); + const width = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); + const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); + const padding = styleContext.get_padding(Gtk.StateFlags.NORMAL).left; + const marginLeft = styleContext.get_margin(Gtk.StateFlags.NORMAL).left; + const marginRight = styleContext.get_margin(Gtk.StateFlags.NORMAL).right; + const marginTop = styleContext.get_margin(Gtk.StateFlags.NORMAL).top; + const marginBottom = styleContext.get_margin(Gtk.StateFlags.NORMAL).bottom; + area.set_size_request(width + marginLeft + marginRight, height + marginTop + marginBottom); + area.connect('draw', Lang.bind(area, (area, cr) => { + const styleContext = area.get_style_context(); + const width = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); + const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); + const padding = styleContext.get_padding(Gtk.StateFlags.NORMAL).left; + const marginLeft = styleContext.get_margin(Gtk.StateFlags.NORMAL).left; + const marginRight = styleContext.get_margin(Gtk.StateFlags.NORMAL).right; + const marginTop = styleContext.get_margin(Gtk.StateFlags.NORMAL).top; + const marginBottom = styleContext.get_margin(Gtk.StateFlags.NORMAL).bottom; + area.set_size_request(width + marginLeft + marginRight, height + marginTop + marginBottom); + + const progressValue = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 100.0; + + const bg_stroke = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL); + const fg_stroke = bg_stroke - padding; + const radius = Math.min(width, height) / 2.0 - Math.max(bg_stroke, fg_stroke) / 2.0; + const center_x = width / 2.0 + marginLeft; + const center_y = height / 2.0 + marginTop; + const start_angle = -Math.PI / 2.0; + const end_angle = start_angle + (2 * Math.PI * progressValue); + const start_x = center_x + Math.cos(start_angle) * radius; + const start_y = center_y + Math.sin(start_angle) * radius; + const end_x = center_x + Math.cos(end_angle) * radius; + const end_y = center_y + Math.sin(end_angle) * radius; + + // Draw background + const background_color = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL); + cr.setSourceRGBA(background_color.red, background_color.green, background_color.blue, background_color.alpha); + cr.arc(center_x, center_y, radius, 0, 2 * Math.PI); + cr.setLineWidth(bg_stroke); + cr.stroke(); + + // Draw progress + const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL); + cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha); + cr.arc(center_x, center_y, radius, start_angle, end_angle); + cr.setLineWidth(fg_stroke); + cr.stroke(); + + // Draw rounded ends for progress arcs + cr.setLineWidth(0); + cr.arc(start_x, start_y, fg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + cr.arc(end_x, end_y, fg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + })) + }, +}) \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/lib/contextmenuitem.js b/desktops/hyprland/ags/modules/lib/contextmenuitem.js new file mode 100644 index 0000000..8dca27c --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/contextmenuitem.js @@ -0,0 +1,10 @@ +const { Gdk, Gtk } = imports.gi; +import { Widget } from '../../imports.js'; + +export const ContextMenuItem = ({ label, onClick }) => Widget({ + type: Gtk.MenuItem, + label: `${label}`, + setup: menuItem => { + menuItem.connect("activate", onClick); + } +}) \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/lib/cursorhover.js b/desktops/hyprland/ags/modules/lib/cursorhover.js new file mode 100644 index 0000000..89717df --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/cursorhover.js @@ -0,0 +1,70 @@ +const { Gdk, Gtk } = imports.gi; + +const CLICK_BRIGHTEN_AMOUNT = 0.13; + +export function setupCursorHover(button) { + var clicked = false; + var dummy = false; + var cursorX = 0; + var cursorY = 0; + const styleContext = button.get_style_context(); + var clickColor = styleContext.get_property('background-color', Gtk.StateFlags.HOVER); + clickColor.green += CLICK_BRIGHTEN_AMOUNT; + clickColor.blue += CLICK_BRIGHTEN_AMOUNT; + clickColor.red += CLICK_BRIGHTEN_AMOUNT; + clickColor = clickColor.to_string(); + + const display = Gdk.Display.get_default(); + button.connect('enter-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'pointer'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); + + // button.add_events(Gdk.EventMask.POINTER_MOTION_MASK); + // button.connect('motion-notify-event', (widget, event) => { + // [dummy, cursorX, cursorY] = event.get_coords(); // Get the mouse coordinates relative to the widget + // if(!clicked) widget.style = ` + // background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%); + // `; + // }); + + // button.connect('button-press-event', (widget, event) => { + // clicked = true; + // [dummy, cursorX, cursorY] = event.get_coords(); // Get the mouse coordinates relative to the widget + // cursorX = Math.round(cursorX); cursorY = Math.round(cursorY); + // widget.style = ` + // background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%); + // `; + // widget.toggleClassName('growingRadial', true); + // widget.style = ` + // background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 70%, ${clickColor} 70%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%); + // ` + // }); + // button.connect('button-release-event', (widget, event) => { + // widget.toggleClassName('growingRadial', false); + // widget.toggleClassName('fadingRadial', false); + // widget.style = ` + // background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%); + // ` + // clicked = false; + // }); +} + +export function setupCursorHoverAim(button) { + button.connect('enter-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'crosshair'); + button.get_window().set_cursor(cursor); + }); + + button.connect('leave-notify-event', () => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + }); +} \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/lib/materialicon.js b/desktops/hyprland/ags/modules/lib/materialicon.js new file mode 100644 index 0000000..6bcb6c6 --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/materialicon.js @@ -0,0 +1,7 @@ +import { Widget } from '../../imports.js'; + +export const MaterialIcon = (icon, size, props = {}) => Widget.Label({ + className: `icon-material txt-${size}`, + label: icon, + ...props, +}) \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/lib/navigationindicator.js b/desktops/hyprland/ags/modules/lib/navigationindicator.js new file mode 100644 index 0000000..b0a6340 --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/navigationindicator.js @@ -0,0 +1,73 @@ +const { Gdk, Gtk } = imports.gi; +const GObject = imports.gi.GObject; +const Lang = imports.lang; +import { Utils, Widget } from '../../imports.js'; + +// min-height/min-width for height/width +// background-color/color for background/indicator color +// padding for pad of indicator +// font-size for selected index (0-based) +export const NavigationIndicator = (count, vertical, props) => Widget({ + ...props, + type: Gtk.DrawingArea, + setup: (area) => { + const styleContext = area.get_style_context(); + const width = Math.max(styleContext.get_property('min-width', Gtk.StateFlags.NORMAL), area.get_allocated_width()); + const height = Math.max(styleContext.get_property('min-height', Gtk.StateFlags.NORMAL), area.get_allocated_height()); + area.set_size_request(width, height); + + area.connect('draw', Lang.bind(area, (area, cr) => { + const styleContext = area.get_style_context(); + const width = Math.max(styleContext.get_property('min-width', Gtk.StateFlags.NORMAL), area.get_allocated_width()); + const height = Math.max(styleContext.get_property('min-height', Gtk.StateFlags.NORMAL), area.get_allocated_height()); + // console.log('allocated width/height:', area.get_allocated_width(), '/', area.get_allocated_height()) + area.set_size_request(width, height); + const paddingLeft = styleContext.get_padding(Gtk.StateFlags.NORMAL).left; + const paddingRight = styleContext.get_padding(Gtk.StateFlags.NORMAL).right; + const paddingTop = styleContext.get_padding(Gtk.StateFlags.NORMAL).top; + const paddingBottom = styleContext.get_padding(Gtk.StateFlags.NORMAL).bottom; + + const selectedCell = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL); + + let cellWidth = width; + let cellHeight = height; + if (vertical) cellHeight /= count; + else cellWidth /= count; + const indicatorWidth = cellWidth - paddingLeft - paddingRight; + const indicatorHeight = cellHeight - paddingTop - paddingBottom; + + const background_color = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL); + const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL); + cr.setLineWidth(2); + // Background + cr.setSourceRGBA(background_color.red, background_color.green, background_color.blue, background_color.alpha); + cr.rectangle(0, 0, width, height); + cr.fill(); + + // The indicator line + cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha); + if (vertical) { + cr.rectangle(paddingLeft, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth, indicatorHeight - indicatorWidth); + cr.stroke(); + cr.rectangle(paddingLeft, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth, indicatorHeight - indicatorWidth); + cr.fill(); + cr.arc(paddingLeft + indicatorWidth / 2, paddingTop + cellHeight * selectedCell + indicatorWidth / 2, indicatorWidth / 2, Math.PI, 2 * Math.PI); + cr.fill(); + cr.arc(paddingLeft + indicatorWidth / 2, paddingTop + cellHeight * selectedCell + indicatorHeight - indicatorWidth / 2, indicatorWidth / 2, 0, Math.PI); + cr.fill(); + } + else { + cr.rectangle(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop, indicatorWidth - indicatorHeight, indicatorHeight); + cr.stroke(); + cr.rectangle(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop, indicatorWidth - indicatorHeight, indicatorHeight); + cr.fill(); + cr.arc(paddingLeft + cellWidth * selectedCell + indicatorHeight / 2, paddingTop + indicatorHeight / 2, indicatorHeight / 2, 0.5 * Math.PI, 1.5 * Math.PI); + cr.fill(); + cr.arc(paddingLeft + cellWidth * selectedCell + indicatorWidth - indicatorHeight / 2, paddingTop + indicatorHeight / 2, indicatorHeight / 2, -0.5 * Math.PI, 0.5 * Math.PI); + cr.fill(); + } + })) + }, +}) + + diff --git a/desktops/hyprland/ags/modules/lib/notification.js b/desktops/hyprland/ags/modules/lib/notification.js new file mode 100644 index 0000000..6ae5a56 --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/notification.js @@ -0,0 +1,298 @@ +// This file is for the actual widget for each single notification + +const { GLib, Gdk, Gtk } = imports.gi; +import { Utils, Widget } from '../../imports.js'; +const { lookUpIcon, timeout } = Utils; +const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget; +import { MaterialIcon } from "./materialicon.js"; +import { setupCursorHover } from "./cursorhover.js"; + +const NotificationIcon = (notifObject) => { + // { appEntry, appIcon, image }, urgency = 'normal' + if (notifObject.image) { + return Box({ + valign: 'center', + hexpand: false, + className: 'notif-icon', + style: ` + background-image: url("${notifObject.image}"); + background-size: auto 100%; + background-repeat: no-repeat; + background-position: center; + `, + }); + } + + let icon = 'NO_ICON'; + if (lookUpIcon(notifObject.appIcon)) + icon = notifObject.appIcon; + if (lookUpIcon(notifObject.appEntry)) + icon = notifObject.appEntry; + + return Box({ + valign: 'center', + hexpand: false, + className: 'notif-icon', + setup: box => { + if (icon != 'NO_ICON') box.pack_start(Icon({ + icon, size: 30, + halign: 'center', hexpand: true, + valign: 'center', + setup: () => { + box.toggleClassName(`notif-icon-material-${notifObject.urgency}`, true); + }, + }), false, true, 0); + else box.pack_start(MaterialIcon(`${notifObject.urgency == 'critical' ? 'release_alert' : 'chat'}`, 'hugeass', { + hexpand: true, + setup: () => box.toggleClassName(`notif-icon-material-${notifObject.urgency}`, true), + }), false, true, 0) + } + }); +}; + +export default ({ + notifObject, + isPopup = false, + props = {}, +} = {}) => { + const command = (isPopup ? + () => notifObject.dismiss() : + () => notifObject.close() + ) + const destroyWithAnims = () => { + widget.sensitive = false; + notificationBox.setStyle(rightAnim1); + Utils.timeout(200, () => { + wholeThing.revealChild = false; + }); + Utils.timeout(400, () => { + command(); + wholeThing.destroy(); + }); + } + const widget = EventBox({ + onHover: (self) => { + self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); + if (!wholeThing._hovered) + wholeThing._hovered = true; + }, + onHoverLost: (self) => { + self.window.set_cursor(null); + if (wholeThing._hovered) + wholeThing._hovered = false; + if(isPopup) { + command(); + } + }, + onMiddleClick: (self) => { + destroyWithAnims(); + } + }); + const wholeThing = Revealer({ + properties: [ + ['id', notifObject.id], + ['close', undefined], + ['hovered', false], + ['dragging', false], + ['destroyWithAnims', () => destroyWithAnims] + ], + revealChild: false, + transition: 'slide_down', + transitionDuration: 200, + child: Box({ // Box to make sure css-based spacing works + homogeneous: true, + }) + }); + + const display = Gdk.Display.get_default(); + const notificationContent = Box({ + ...props, + className: `${isPopup ? 'popup-' : ''}notif-${notifObject.urgency} spacing-h-10`, + children: [ + NotificationIcon(notifObject), + Box({ + valign: 'center', + vertical: true, + hexpand: true, + children: [ + Box({ + children: [ + Label({ + xalign: 0, + className: 'txt-small txt-semibold titlefont', + justify: Gtk.Justification.LEFT, + hexpand: true, + maxWidthChars: 24, + ellipsize: 3, + wrap: true, + useMarkup: notifObject.summary.startsWith('<'), + label: notifObject.summary, + }), + ] + }), + Label({ + xalign: 0, + className: 'txt-smallie notif-body-${urgency}', + useMarkup: true, + xalign: 0, + justify: Gtk.Justification.LEFT, + wrap: true, + label: notifObject.body, + }), + ] + }), + Box({ + className: 'spacing-h-5', + children: [ + Label({ + valign: 'center', + className: 'txt-smaller txt-semibold', + justify: Gtk.Justification.RIGHT, + setup: (label) => { + const messageTime = GLib.DateTime.new_from_unix_local(notifObject.time); + if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year()) { + label.label = messageTime.format('%H:%M'); + } + else if (messageTime.get_day_of_year() == GLib.DateTime.new_now_local().get_day_of_year() - 1) { + label.label = messageTime.format('%H:%M\nYesterday'); + } + else { + label.label = messageTime.format('%H:%M\n%d/%m'); + } + } + }), + Button({ + className: 'notif-close-btn', + onClicked: () => { + destroyWithAnims() + }, + child: MaterialIcon('close', 'large', { + valign: 'center', + }), + setup: (button) => setupCursorHover(button), + }), + ] + }), + + // what is this? i think it should be at the bottom not on the right + // Box({ + // className: 'actions', + // children: actions.map(action => Button({ + // className: 'action-button', + // onClicked: () => Notifications.invoke(id, action.id), + // hexpand: true, + // child: Label(action.label), + // })), + // }), + ] + }) + + // Gesture stuff + + const gesture = Gtk.GestureDrag.new(widget); + var initialDir = 0; + // in px + const startMargin = 0; + const dragThreshold = 100; + // in rem + const maxOffset = 10.227; + const endMargin = 20.455; + const disappearHeight = 6.818; + const leftAnim1 = `transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + margin-left: -${Number(maxOffset + endMargin)}rem; + margin-right: ${Number(maxOffset + endMargin)}rem; + opacity: 0;`; + + const rightAnim1 = `transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + margin-left: ${Number(maxOffset + endMargin)}rem; + margin-right: -${Number(maxOffset + endMargin)}rem; + opacity: 0;`; + + const notificationBox = Box({ + properties: [ + ['leftAnim1', leftAnim1], + ['rightAnim1', rightAnim1], + ['ready', false], + ], + homogeneous: true, + children: [notificationContent], + connections: [ + [gesture, self => { + var offset = gesture.get_offset()[1]; + if (initialDir == 0 && offset != 0) + initialDir = (offset > 0 ? 1 : -1) + + if (offset > 0) { + if (initialDir < 0) + self.setStyle(`margin-left: 0px; margin-right: 0px;`); + else + self.setStyle(` + margin-left: ${Number(offset + startMargin)}px; + margin-right: -${Number(offset + startMargin)}px; + `); + } + else if (offset < 0) { + if (initialDir > 0) + self.setStyle(`margin-left: 0px; margin-right: 0px;`); + else { + offset = Math.abs(offset); + self.setStyle(` + margin-right: ${Number(offset + startMargin)}px; + margin-left: -${Number(offset + startMargin)}px; + `); + } + } + + wholeThing._dragging = Math.abs(offset) > 10; + + if (widget.window) + widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing')); + }, 'drag-update'], + + [gesture, self => { + if (!self._ready) { + wholeThing.revealChild = true; + self._ready = true; + return; + } + + const offset = gesture.get_offset()[1]; + + if (Math.abs(offset) > dragThreshold && offset * initialDir > 0) { + if (offset > 0) { + self.setStyle(rightAnim1); + widget.sensitive = false; + } + else { + self.setStyle(leftAnim1); + widget.sensitive = false; + } + Utils.timeout(200, () => { + wholeThing.revealChild = false + }); + Utils.timeout(400, () => { + command(); + wholeThing.destroy(); + }); + } + else { + self.setStyle(`transition: margin 200ms cubic-bezier(0.05, 0.7, 0.1, 1), opacity 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + margin-left: ${startMargin}px; + margin-right: ${startMargin}px; + margin-bottom: unset; margin-top: unset; + opacity: 1;`); + if (widget.window) + widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab')); + + wholeThing._dragging = false; + } + initialDir = 0; + }, 'drag-end'], + + ], + }) + widget.add(notificationBox); + wholeThing.child.children = [widget]; + + return wholeThing; +} diff --git a/desktops/hyprland/ags/modules/lib/roundedcorner.js b/desktops/hyprland/ags/modules/lib/roundedcorner.js new file mode 100644 index 0000000..bef91ca --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/roundedcorner.js @@ -0,0 +1,51 @@ +import { Widget } from '../../imports.js'; +const { Gtk } = imports.gi; +const Lang = imports.lang; + +export const RoundedCorner = (place, props) => Widget({ + ...props, + type: Gtk.DrawingArea, + halign: place.includes('left') ? 'start' : 'end', + valign: place.includes('top') ? 'start' : 'end', + setup: widget => { + const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL); + const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL); + widget.set_size_request(r, r); + widget.connect('draw', Lang.bind(widget, (widget, cr) => { + const c = widget.get_style_context().get_property('background-color', Gtk.StateFlags.NORMAL); + const r = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL); + // const borderColor = widget.get_style_context().get_property('color', Gtk.StateFlags.NORMAL); + // const borderWidth = widget.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway + widget.set_size_request(r, r); + + switch (place) { + case 'topleft': + cr.arc(r, r, r, Math.PI, 3 * Math.PI / 2); + cr.lineTo(0, 0); + break; + + case 'topright': + cr.arc(0, r, r, 3 * Math.PI / 2, 2 * Math.PI); + cr.lineTo(r, 0); + break; + + case 'bottomleft': + cr.arc(r, 0, r, Math.PI / 2, Math.PI); + cr.lineTo(0, r); + break; + + case 'bottomright': + cr.arc(0, 0, r, 0, Math.PI / 2); + cr.lineTo(r, r); + break; + } + + cr.closePath(); + cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha); + cr.fill(); + // cr.setLineWidth(borderWidth); + // cr.setSourceRGBA(borderColor.red, borderColor.green, borderColor.blue, borderColor.alpha); + // cr.stroke(); + })); + }, +}); \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/lib/searchitem.js b/desktops/hyprland/ags/modules/lib/searchitem.js new file mode 100644 index 0000000..38f79d0 --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/searchitem.js @@ -0,0 +1,69 @@ +const { Gdk, Gtk } = imports.gi; +import { App, Service, Utils, Widget } from '../../imports.js'; +const { execAsync, exec } = Utils; +import { setupCursorHover, setupCursorHoverAim } from "./cursorhover.js"; +import { MaterialIcon } from './materialicon.js'; + +export const searchItem = ({ materialIconName, name, actionName, content, onActivate }) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: 200, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: `${actionName}`, + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: 300, + child: actionText, + }) + return Widget.Button({ + className: 'overview-search-result-btn', + onClicked: onActivate, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Label({ + className: `icon-material overview-search-results-icon`, + label: `${materialIconName}`, + }), + Widget.Box({ + vertical: true, + children: [ + Widget.Label({ + halign: 'start', + className: 'overview-search-results-txt txt txt-smallie txt-subtext', + label: `${name}`, + truncate: "end", + }), + Widget.Label({ + halign: 'start', + className: 'overview-search-results-txt txt txt-norm', + label: `${content}`, + truncate: "end", + }), + ] + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ], + }) + ] + }), + connections: [ + ['focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }], + ['focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }], + ] + }); +} diff --git a/desktops/hyprland/ags/modules/lib/separator.js b/desktops/hyprland/ags/modules/lib/separator.js new file mode 100644 index 0000000..4eaa02c --- /dev/null +++ b/desktops/hyprland/ags/modules/lib/separator.js @@ -0,0 +1,9 @@ +const { Gdk, Gtk } = imports.gi; +import { App, Service, Utils, Widget } from '../../imports.js'; +const { execAsync, exec } = Utils; +import { setupCursorHover, setupCursorHoverAim } from "./cursorhover.js"; +import { MaterialIcon } from './materialicon.js'; + +export const separatorLine = Widget.Box({ + className: 'separator-line', +}) \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/misctoggles.js b/desktops/hyprland/ags/modules/misctoggles.js new file mode 100644 index 0000000..0031fca --- /dev/null +++ b/desktops/hyprland/ags/modules/misctoggles.js @@ -0,0 +1,111 @@ +import { App, Utils, Widget } from '../imports.js'; +const { execAsync, exec } = Utils; +import { MaterialIcon } from "./lib/materialicon.js"; +import { setupCursorHover } from "./lib/cursorhover.js"; + +const RECORD_SCRIPT_DIR = `${App.configDir}/scripts/record-script.sh`; +const RECORDER_PROCESS = 'record-script.sh'; +const CLOSE_ANIM_TIME = 150; + +async function toggleSystemdService(serviceName, button) { + const serviceState = exec(`systemctl is-enabled ${serviceName}`) == 'enabled'; + // console.log(`pkexec bash -c "systemctl ${serviceState ? 'disable' : 'enable'} ${serviceName}"`) + exec(`pkexec bash -c "systemctl ${serviceState ? 'disable' : 'enable'} ${serviceName}"`); + const newServiceState = exec(`systemctl is-enabled ${serviceName}`) == 'enabled'; + button.toggleClassName('sidebar-button-active', newServiceState); + serviceState.toggleClassName('invisible', newServiceState); +} + +const ModuleRecord = (props = {}) => Widget.Button({ + ...props, + className: 'button-minsize sidebar-button-nopad sidebar-button-alone-normal txt-small', + onClicked: () => { + execAsync(['bash', '-c', RECORD_SCRIPT_DIR]).catch(print); + setTimeout(() => { + button.toggleClassName('sidebar-button-active', exec(`pidof ${RECORDER_PROCESS} >/dev/null && echo 1 || echo`) == '1'); + }, CLOSE_ANIM_TIME); + }, + child: MaterialIcon('screen_record', 'larger'), + setup: button => { + button.toggleClassName('sidebar-button-active', exec(`pidof ${RECORDER_PROCESS} >/dev/null && echo 1 || echo`)); + setupCursorHover(button); + } +}) + +const SystemdService = (serviceName) => { + const serviceState = Widget.Label({ + className: `icon-material txt-larger`, + label: 'check', + setup: label => { + // label.toggleClassName('invisible', exec(`bash -c "systemctl is-enabled ${serviceName} >/dev/null && echo ON || echo OFF"`) == 'OFF'); + } + }); + return Widget.Button({ + className: 'button-minsize sidebar-button sidebar-button-alone-normal txt-small', + onClicked: (button) => { + toggleSystemdService(serviceName, button); + }, + setup: button => { + button.toggleClassName('sidebar-button-active', exec(`systemctl is-enabled ${serviceName}`) == 'enabled'); + setupCursorHover(button); + }, + child: Widget.Box({ + setup: box => { + box.pack_start(Widget.Label({ + xalign: 0, + label: serviceName, + }), true, true, 0); + // box.pack_end(serviceState, false, false, 0); + } + }) + }); +} + +export const ModuleMiscToggles = () => { + const PowerSavers = Widget.Revealer({ + revealChild: false, + transition: 'slide_left', + transitionDuration: 100, + child: Widget.Box({ + className: 'spacing-v-5 margin-right-10', + vertical: true, + children: [ + SystemdService('tlp'), + SystemdService('auto-cpufreq'), + ] + }) + }) + const ModulePowerSavers = Widget.Button({ + className: 'button-minsize sidebar-button-nopad sidebar-button-alone-normal txt-small', + child: MaterialIcon('keyboard_arrow_leftenergy_savings_leaf', 'larger', { + xalign: 0.2, + }), + onClicked: (button) => { + const revealed = PowerSavers.revealChild; + PowerSavers.revealChild = !revealed; + button.toggleClassName('sidebar-button-active', !revealed); + button.child.label = revealed ? 'keyboard_arrow_leftenergy_savings_leaf' : 'keyboard_arrow_rightenergy_savings_leaf'; + }, + setup: (button) => setupCursorHover(button), + }) + return Widget.Box({ + className: 'sidebar-group spacing-h-10', + children: [ + PowerSavers, + Widget.Box({ + vertical: true, + className: 'spacing-v-5', + children: [ + ModulePowerSavers, + Widget.Box({ + className: 'spacing-h-5', + children: [ + ModuleNightLight(), + ModuleRecord(), + ] + }) + ] + }) + ] + }); +} diff --git a/desktops/hyprland/ags/modules/music.js b/desktops/hyprland/ags/modules/music.js new file mode 100644 index 0000000..667f4d9 --- /dev/null +++ b/desktops/hyprland/ags/modules/music.js @@ -0,0 +1,78 @@ +import { Service, Utils, Widget } from '../imports.js'; +import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; +import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; +const { execAsync, exec } = Utils; +import { AnimatedCircProg } from "./lib/animatedcircularprogress.js"; + +const TrackProgress = () => { + const _updateProgress = (circprog) => { + const mpris = Mpris.getPlayer(''); + if (!mpris) return; + // Set circular progress (font size cuz that's how this hacky circprog works) + circprog.style = `font-size: ${mpris.position / mpris.length * 100}px;` + } + return AnimatedCircProg({ + className: 'bar-music-circprog', + valign: 'center', + connections: [ // Update on change/once every 3 seconds + [Mpris, _updateProgress], + [3000, _updateProgress] + ] + }) +} + +export const ModuleMusic = () => Widget.EventBox({ + onScrollUp: () => execAsync('hyprctl dispatch workspace -1'), + onScrollDown: () => execAsync('hyprctl dispatch workspace +1'), + onSecondaryClick: () => Mpris.getPlayer('')?.next(), + onMiddleClick: () => Mpris.getPlayer('')?.playPause(), + child: Widget.Box({ + className: 'bar-group-margin bar-sides', + children: [ + Widget.Box({ + className: 'bar-group bar-group-standalone bar-group-pad-music spacing-h-10', + children: [ + Widget.Box({ // Wrap a box cuz overlay can't have margins itself + homogeneous: true, + children: [Widget.Overlay({ + child: Widget.Box({ + valign: 'center', + className: 'bar-music-playstate', + children: [Widget.Label({ + valign: 'center', + className: 'bar-music-playstate-txt', + connections: [[Mpris, label => { + const mpris = Mpris.getPlayer(''); + label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? '' : ''}`; + }]], + })], + connections: [[Mpris, label => { + const mpris = Mpris.getPlayer(''); + if (!mpris) return; + label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing'); + label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused'); + }]], + }), + overlays: [ + TrackProgress(), + ] + })] + }), + Widget.Scrollable({ + hexpand: true, + child: Widget.Label({ + className: 'txt txt-smallie', + connections: [[Mpris, label => { + const mpris = Mpris.getPlayer(''); + if (mpris) + label.label = `${mpris.trackTitle} • ${mpris.trackArtists.join(', ')}`; + else + label.label = 'No mewwsic'; + }]], + }) + }) + ] + }) + ] + }) +}); \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/musiccontrols.js b/desktops/hyprland/ags/modules/musiccontrols.js new file mode 100644 index 0000000..3712718 --- /dev/null +++ b/desktops/hyprland/ags/modules/musiccontrols.js @@ -0,0 +1,9 @@ +import { Service, Utils, Widget } from '../imports.js'; +const { Box, CenterBox, Label } = Widget; +const { Mpris } = Service; +const { timeout } = Utils; +import { BluetoothIndicator, NetworkIndicator } from "./statusicons.js"; + +export const ModuleMusicControls = () => Box({ + +}) \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/notificationlist.js b/desktops/hyprland/ags/modules/notificationlist.js new file mode 100644 index 0000000..6478e00 --- /dev/null +++ b/desktops/hyprland/ags/modules/notificationlist.js @@ -0,0 +1,123 @@ +// This file is for the notification widget on the sidebar +// For the popup notifications, see onscreendisplay.js +// The actual widget for each single notification is in lib/notification.js + +const { GLib, Gtk } = imports.gi; +import { Service, Utils, Widget } from '../imports.js'; +import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; +const { lookUpIcon, timeout } = Utils; +const { Box, Icon, Scrollable, Label, Button, Revealer } = Widget; +import { MaterialIcon } from "./lib/materialicon.js"; +import { setupCursorHover } from "./lib/cursorhover.js"; +import Notification from "./lib/notification.js"; + +const NotificationList = Box({ + vertical: true, + valign: 'start', + className: 'spacing-v-5-revealer', + connections: [ + [Notifications, (box, id) => { + if (box.children.length == 0) { + Notifications.notifications + .forEach(n => { + box.pack_end(Notification({ + notifObject: n, + isPopup: false, + }), false, false, 0) + }); + box.show_all(); + } + else if (id) { + const notif = Notifications.getNotification(id); + + const NewNotif = Notification({ + notifObject: notif, + isPopup: false, + }); + + if (NewNotif) { + box.pack_end(NewNotif, false, false, 0); + box.show_all(); + } + } + }, 'notified'], + + [Notifications, (box, id) => { + if (!id) return; + for (const ch of box.children) { + if (ch._id === id) { + ch._destroyWithAnims(); + } + } + }, 'closed'], + + [Notifications, box => box.visible = Notifications.notifications.length > 0], + ], +}); + +export default (props) => { + const listTitle = Revealer({ + revealChild: false, + connections: [[Notifications, (revealer) => { + revealer.revealChild = (Notifications.notifications.length > 0); + }]], + child: Box({ + valign: 'start', + className: 'sidebar-group-invisible txt', + children: [ + Label({ + hexpand: true, + xalign: 0, + className: 'txt-title-small', + label: 'Notifications', + }), + Button({ + className: 'notif-closeall-btn', + onClicked: () => { + Notifications.clear(); + }, + child: Box({ + className: 'spacing-h-5', + children: [ + MaterialIcon('clear_all', 'norm'), + Label({ + className: 'txt-small', + label: 'Clear', + }) + ] + }), + setup: button => { + setupCursorHover(button); + }, + }) + ] + }) + }); + const listContents = Scrollable({ + hexpand: true, + hscroll: 'never', + vscroll: 'automatic', + child: Widget({ + type: Gtk.Viewport, + className: 'sidebar-viewport', + setup: (viewport) => { + viewport.add(Box({ + vexpand: true, + children: [NotificationList], + })); + } + }) + }); + listContents.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); + const vScrollbar = listContents.get_vscrollbar(); + vScrollbar.get_style_context().add_class('sidebar-scrollbar'); + return Box({ + ...props, + className: 'sidebar-group-invisible spacing-v-5', + vertical: true, + children: [ + listTitle, + listContents, + ] + }); +} diff --git a/desktops/hyprland/ags/modules/onscreendisplay.js b/desktops/hyprland/ags/modules/onscreendisplay.js new file mode 100644 index 0000000..217f6e6 --- /dev/null +++ b/desktops/hyprland/ags/modules/onscreendisplay.js @@ -0,0 +1,192 @@ +// This file is for brightness/volume indicator and popup notifications +// For the notification widget on the sidebar, see notificationlist.js +// The actual widget for each single notification is in lib/notification.js + +const { GLib, Gtk } = imports.gi; +import { App, Service, Utils, Widget } from '../imports.js'; +import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; +import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; +const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget; +import Brightness from '../scripts/brightness.js'; +import Indicator from '../scripts/indicator.js'; +import Notification from './lib/notification.js'; + +const OsdValue = (name, labelConnections, progressConnections, props = {}) => Widget.Box({ // Volume + ...props, + vertical: true, + className: 'osd-bg osd-value', + hexpand: true, + children: [ + Widget.Box({ + vexpand: true, + children: [ + Widget.Label({ + xalign: 0, yalign: 0, hexpand: true, + className: 'osd-label', + label: `${name}`, + }), + Widget.Label({ + hexpand: false, className: 'osd-value-txt', + label: '100', + connections: labelConnections, + }), + ] + }), + Widget.ProgressBar({ + className: 'osd-progress', + hexpand: true, + vertical: false, + connections: progressConnections, + }) + ], +}); + +const brightnessIndicator = OsdValue('Brightness', + [[Brightness, self => { + self.label = `${Math.round(Brightness.screen_value * 100)}`; + }, 'notify::screen-value']], + [[Brightness, (progress) => { + const updateValue = Brightness.screen_value; + progress.value = updateValue; + }, 'notify::screen-value']], +) + +const volumeIndicator = OsdValue('Volume', + [[Audio, (label) => { + label.label = `${Math.round(Audio.speaker?.volume * 100)}`; + }]], + [[Audio, (progress) => { + const updateValue = Audio.speaker?.volume; + if (!isNaN(updateValue)) progress.value = updateValue; + }]], +); + +const indicatorValues = Widget.Revealer({ + transition: 'slide_down', + connections: [ + [Indicator, (revealer, value) => { + revealer.revealChild = (value > -1); + }, 'popup'], + ], + child: Widget.Box({ + halign: 'center', + vertical: false, + children: [ + brightnessIndicator, + volumeIndicator, + ] + }) +}); + +const PopupNotification = (notifObject) => Widget.Box({ + homogeneous: true, + children: [ + Widget.EventBox({ + onHoverLost: () => { + notifObject.dismiss(); + }, + child: Widget.Revealer({ + revealChild: true, + child: Widget.Box({ + children: [Notification({ + notifObject: notifObject, + isPopup: true, + props: { halign: 'fill' }, + })], + }), + }) + }) + ] +}) + +const naiveNotifPopupList = Widget.Box({ + vertical: true, + className: 'spacing-v-5', + connections: [ + [Notifications, (box) => { + box.children = Notifications.popups.reverse() + .map(notifItem => PopupNotification(notifItem)); + }], + ], +}) + +const notifPopupList = Box({ + vertical: true, + className: 'spacing-v-5-revealer', + properties: [ + ['map', new Map()], + + ['dismiss', (box, id, force = false) => { + if (!id || !box._map.has(id) || box._map.get(id)._hovered && !force) + return; + + const notif = box._map.get(id); + // console.log(notif); + notif.revealChild = false; + Utils.timeout(200, () => { + notif._destroyWithAnims(); + }) + }], + + ['notify', (box, id) => { + if (!id || Notifications.dnd) + return; + + if (!Notifications.getNotification(id)) + return; + + box._map.delete(id); + + const notif = Notifications.getNotification(id); + box._map.set(id, Notification({ + notifObject: notif, + isPopup: true, + })); + + box.children = Array.from(box._map.values()).reverse(); + + Utils.timeout(10, () => { + box.get_parent().revealChild = true; + }); + + box._map.get(id).interval = Utils.interval(4500, () => { + const notif = box._map.get(id); + if (!notif._hovered) { + if (notif.interval) { + Utils.timeout(500, () => notif.destroy()); + GLib.source_remove(notif.interval); + notif.interval = undefined; + } + } + }); + }], + ], + connections: [ + [Notifications, (box, id) => box._notify(box, id), 'notified'], + [Notifications, (box, id) => box._dismiss(box, id), 'dismissed'], + [Notifications, (box, id) => box._dismiss(box, id, true), 'closed'], + ], +}); + +const notificationPopups = Widget.Revealer({ + className: 'osd-notifs', + transition: 'slide_down', + connections: [[Notifications, (self) => { + self.revealChild = Notifications.popups.length > 0; + }]], + child: notifPopupList, +}) + +export default () => Widget.EventBox({ + onHover: () => { //make the widget hide when hovering + Indicator.popup(-1); + }, + child: Widget.Box({ + vertical: true, + style: 'padding: 1px;', + children: [ + indicatorValues, + notificationPopups, + ] + }) +}); \ No newline at end of file diff --git a/desktops/hyprland/ags/modules/onscreenkeyboard.js b/desktops/hyprland/ags/modules/onscreenkeyboard.js new file mode 100644 index 0000000..5d44d27 --- /dev/null +++ b/desktops/hyprland/ags/modules/onscreenkeyboard.js @@ -0,0 +1,114 @@ +const { Gdk, Gtk } = imports.gi; +import { App, Service, Utils, Widget } from '../imports.js'; +const { Box, EventBox, Button, Revealer } = Widget; +const { execAsync, exec } = Utils; +import { setupCursorHover, setupCursorHoverAim } from "./lib/cursorhover.js"; +import { MaterialIcon } from './lib/materialicon.js'; +import { separatorLine } from './lib/separator.js'; +import { defaultOskLayout, oskLayouts } from '../data/keyboardlayouts.js'; + +const keyboardLayout = defaultOskLayout; +const keyboardJson = oskLayouts[keyboardLayout]; +execAsync(`ydotoold`).catch(print); // Start ydotool daemon + +function releaseAllKeys() { + const keycodes = Array.from(Array(249).keys()); + execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)]) + .then(console.log('Released all keys')) + .catch(print); +} +var modsPressed = false; + +const keyboardControlButton = (icon, text, runFunction) => Button({ + className: 'osk-control-button spacing-h-10', + onClicked: () => runFunction(), + child: Widget.Box({ + children: [ + MaterialIcon(icon, 'norm'), + Widget.Label({ + label: `${text}`, + }), + ] + }) +}) + +const keyboardControls = Box({ + vertical: true, + className: 'spacing-v-5', + children: [ + Button({ + className: 'osk-control-button txt-norm icon-material', + onClicked: () => { + releaseAllKeys(); + App.toggleWindow('osk'); + }, + label: 'keyboard_hide', + }), + Button({ + className: 'osk-control-button txt-norm', + label: `${keyboardJson['name_short']}`, + }), + Button({ + className: 'osk-control-button txt-norm icon-material', + onClicked: () => { // TODO: Proper clipboard widget, since fuzzel doesn't receive mouse inputs + execAsync([`bash`, `-c`, "pkill fuzzel || cliphist list | fuzzel --no-fuzzy --dmenu | cliphist decode | wl-copy"]).catch(print); + }, + label: 'assignment', + }), + ] +}) + +const keyboardItself = (kbJson) => { + return Box({ + vertical: true, + className: 'spacing-v-5', + children: kbJson.keys.map(row => Box({ + vertical: false, + className: 'spacing-h-5', + children: row.map(key => { + return Button({ + className: `osk-key osk-key-${key.shape}`, + hexpand: (key.shape == "space" || key.shape == "expand"), + label: key.label, + setup: (button) => { + let pressed = false; + if (key.keytype == "normal") { + button.connect('pressed', () => { // mouse down + execAsync(`ydotool key ${key.keycode}:1`); + }); + button.connect('clicked', () => { // release + execAsync(`ydotool key ${key.keycode}:0`); + }); + } + else if (key.keytype == "modkey") { + button.connect('pressed', () => { // release + if (pressed) { + execAsync(`ydotool key ${key.keycode}:0`); + button.toggleClassName('osk-key-active', false); + pressed = false; + } + else { + execAsync(`ydotool key ${key.keycode}:1`); + button.toggleClassName('osk-key-active', true); + pressed = true; + modsPressed = true; + } + }); + } + } + }) + }) + })) + }) +} + +export default () => Box({ + vexpand: true, + hexpand: true, + className: 'osk-window spacing-h-10', + children: [ + keyboardControls, + separatorLine, + keyboardItself(keyboardJson), + ], +}); diff --git a/desktops/hyprland/ags/modules/overview.js b/desktops/hyprland/ags/modules/overview.js new file mode 100644 index 0000000..f5e7425 --- /dev/null +++ b/desktops/hyprland/ags/modules/overview.js @@ -0,0 +1,580 @@ +const { Gdk, Gtk } = imports.gi; +import { App, Service, Utils, Widget } from '../imports.js'; +import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +const { execAsync, exec } = Utils; +import { setupCursorHover, setupCursorHoverAim } from "./lib/cursorhover.js"; +import { MaterialIcon } from './lib/materialicon.js'; +import { searchItem } from './lib/searchitem.js'; +import { ContextMenuItem } from './lib/contextmenuitem.js'; +import Todo from "../scripts/todo.js"; + +var searching = false; +// Add math funcs +const { abs, sin, cos, tan, cot, asin, acos, atan, acot } = Math; +const pi = Math.PI; +// trigonometric funcs for deg +const sind = x => sin(x * pi / 180); +const cosd = x => cos(x * pi / 180); +const tand = x => tan(x * pi / 180); +const cotd = x => cot(x * pi / 180); +const asind = x => asin(x) * 180 / pi; +const acosd = x => acos(x) * 180 / pi; +const atand = x => atan(x) * 180 / pi; +const acotd = x => acot(x) * 180 / pi; + +const MAX_RESULTS = 10; +const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size +const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; +const searchPromptTexts = [ + 'Try "Kolourpaint"', + 'Try "6*cos(pi)"', + 'Try "sudo pacman -Syu"', + 'Try "How to basic"', + 'Drag n\' drop to move windows', + 'Type to search', +] + +function launchCustomCommand(command) { + App.closeWindow('overview'); + const args = command.split(' '); + if (args[0] == '>raw') { // Mouse raw input + execAsync([`bash`, `-c`, `hyprctl keyword input:force_no_accel $(( 1 - $(hyprctl getoption input:force_no_accel -j | gojq ".int") ))`, `&`]).catch(print); + } + else if (args[0] == '>img') { // Change wallpaper + execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchwall.sh`, `&`]).catch(print); + } + else if (args[0] == '>light') { // Light mode + execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "-l" > ~/.cache/ags/user/colormode.txt`, `&`]).catch(print); + } + else if (args[0] == '>dark') { // Dark mode + execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "" > ~/.cache/ags/user/colormode.txt`, `&`]).catch(print); + } + else if (args[0] == '>material') { // Light mode + execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "material" > ~/.cache/ags/user/colorbackend.txt`, `&`]).catch(print); + } + else if (args[0] == '>pywal') { // Dark mode + execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "pywal" > ~/.cache/ags/user/colorbackend.txt`, `&`]).catch(print); + } + else if (args[0] == '>todo') { // Todo + Todo.add(args.slice(1).join(' ')); + } + else if (args[0] == '>shutdown') { // Shut down + execAsync([`bash`, `-c`, `systemctl poweroff`]).catch(print); + } + else if (args[0] == '>reboot') { // Reboot + execAsync([`bash`, `-c`, `systemctl reboot`]).catch(print); + } + else if (args[0] == '>sleep') { // Sleep + execAsync([`bash`, `-c`, `systemctl suspend`]).catch(print); + } + else if (args[0] == '>logout') { // Log out + execAsync([`bash`, `-c`, `loginctl terminate-user $USER`]).catch(print); + } +} + +function execAndClose(command, terminal) { + App.closeWindow('overview'); + if (terminal) { + execAsync([`bash`, `-c`, `foot fish -C "${command}"`, `&`]).catch(print); + } + else + execAsync(command).catch(print); +} + +function startsWithNumber(str) { + var pattern = /^\d/; + return pattern.test(str); +} + +function substitute(str) { + const subs = [ + { from: 'code-url-handler', to: 'visual-studio-code' }, + { from: 'Code', to: 'visual-studio-code' }, + { from: 'GitHub Desktop', to: 'github-desktop' }, + { from: 'wpsoffice', to: 'wps-office2019-kprometheus' }, + { from: 'gnome-tweaks', to: 'org.gnome.tweaks' }, + { from: 'Minecraft* 1.20.1', to: 'minecraft' }, + { from: '', to: 'image-missing' }, + ]; + + for (const { from, to } of subs) { + if (from === str) + return to; + } + + return str; +} + +function destroyContextMenu(menu) { + if (menu !== null) { + menu.remove_all(); + menu.destroy(); + menu = null; + } +} +const CalculationResultButton = ({ result, text }) => searchItem({ + materialIconName: 'calculate', + name: `Math result`, + actionName: "Copy", + content: `${result}`, + onActivate: () => { + App.closeWindow('overview'); + console.log(result); + execAsync(['bash', '-c', `wl-copy '${result}'`, `&`]).catch(print); + }, +}); + +const DesktopEntryButton = (app) => { + const actionText = Widget.Revealer({ + revealChild: false, + transition: "crossfade", + transitionDuration: 200, + child: Widget.Label({ + className: 'overview-search-results-txt txt txt-small txt-action', + label: 'Launch', + }) + }); + const actionTextRevealer = Widget.Revealer({ + revealChild: false, + transition: "slide_left", + transitionDuration: 300, + child: actionText, + }); + return Widget.Button({ + className: 'overview-search-result-btn', + onClicked: () => { + App.closeWindow('overview'); + app.launch(); + }, + child: Widget.Box({ + children: [ + Widget.Box({ + vertical: false, + children: [ + Widget.Icon({ + className: 'overview-search-results-icon', + icon: app.iconName, + size: 35, // TODO: Make this follow font size. made for 11pt. + }), + Widget.Label({ + className: 'overview-search-results-txt txt txt-norm', + label: app.name, + }), + Widget.Box({ hexpand: true }), + actionTextRevealer, + ] + }) + ] + }), + connections: [ + ['focus-in-event', (button) => { + actionText.revealChild = true; + actionTextRevealer.revealChild = true; + }], + ['focus-out-event', (button) => { + actionText.revealChild = false; + actionTextRevealer.revealChild = false; + }], + ] + }) +} + +const ExecuteCommandButton = ({ command, terminal = false }) => searchItem({ + materialIconName: `${terminal ? 'terminal' : 'settings_b_roll'}`, + name: `Run command`, + actionName: `Execute ${terminal ? 'in terminal' : ''}`, + content: `${command}`, + onActivate: () => execAndClose(command, terminal), +}) + +const CustomCommandButton = ({ text = '' }) => searchItem({ + materialIconName: 'settings_suggest', + name: 'Action', + actionName: 'Run', + content: `${text}`, + onActivate: () => { + App.closeWindow('overview'); + launchCustomCommand(text); + }, +}); + +const SearchButton = ({ text = '' }) => searchItem({ + materialIconName: 'travel_explore', + name: 'Search Google', + actionName: 'Go', + content: `${text}`, + onActivate: () => { + App.closeWindow('overview'); + execAsync(['xdg-open', `https://www.google.com/search?q=${text}`]).catch(print); + }, +}); + +const ContextWorkspaceArray = ({ label, onClickBinary, thisWorkspace }) => Widget({ + type: Gtk.MenuItem, + label: `${label}`, + setup: menuItem => { + let submenu = new Gtk.Menu(); + submenu.className = 'menu'; + for (let i = 1; i <= 10; i++) { + let button = new Gtk.MenuItem({ label: `${i}` }); + button.connect("activate", () => { + execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print); + }); + submenu.append(button); + } + menuItem.set_reserve_indicator(true); + menuItem.set_submenu(submenu); + } +}) + +const client = ({ address, size: [w, h], workspace: { id, name }, class: c, title }) => Widget.Button({ + className: 'overview-tasks-window', + halign: 'center', + valign: 'center', + onClicked: () => { + execAsync([`bash`, `-c`, `hyprctl dispatch focuswindow address:${address}`, `&`]).catch(print); + App.closeWindow('overview'); + }, + onMiddleClick: () => execAsync([`bash`, `-c`, `hyprctl dispatch closewindow address:${address}`, `&`]).catch(print), + onSecondaryClick: (button) => { + button.toggleClassName('overview-tasks-window-selected', true); + const menu = Widget({ + type: Gtk.Menu, + className: 'menu', + setup: menu => { + menu.append(ContextMenuItem({ label: "Close (Middle-click)", onClick: () => { execAsync([`bash`, `-c`, `hyprctl dispatch closewindow address:${address}`, `&`]).catch(print); destroyContextMenu(menu); } })); + menu.append(ContextWorkspaceArray({ label: "Dump windows to workspace", onClickBinary: `${App.configDir}/scripts/dumptows`, thisWorkspace: Number(id) })); + menu.append(ContextWorkspaceArray({ label: "Swap windows with workspace", onClickBinary: `${App.configDir}/scripts/dumptows`, thisWorkspace: Number(id) })); + menu.show_all(); + } + }); + menu.connect("deactivate", () => { + button.toggleClassName('overview-tasks-window-selected', false); + }) + menu.connect("selection-done", () => { + button.toggleClassName('overview-tasks-window-selected', false); + }) + menu.popup_at_pointer(null); // Show the menu at the pointer's position + }, + child: Widget.Box({ + vertical: true, + children: [ + Widget.Icon({ + style: ` + min-width: ${w * OVERVIEW_SCALE - 4}px; + min-height: ${h * OVERVIEW_SCALE - 4}px; + `, + size: Math.min(w, h) * OVERVIEW_SCALE / 2.5, + icon: substitute(c), + }), + Widget.Scrollable({ + hexpand: true, + vexpand: true, + child: Widget.Label({ + style: ` + font-size: ${Math.min(w, h) * OVERVIEW_SCALE / 20}px; + `, + label: title, + }) + }) + ] + }), + tooltipText: `${c}: ${title}`, + setup: (button) => { + setupCursorHoverAim(button); + + button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.MOVE); + button.drag_source_set_icon_name(substitute(c)); + // button.drag_source_set_icon_gicon(icon); + + button.connect('drag-begin', (button) => { // On drag start, add the dragging class + button.toggleClassName('overview-tasks-window-dragging', true); + }); + button.connect('drag-data-get', (_w, _c, data) => { // On drag finish, give address + data.set_text(address, address.length); + button.toggleClassName('overview-tasks-window-dragging', false); + }); + + // button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.COPY); + // button.connect('drag-data-get', (_w, _c, data) => data.set_text(address, address.length)); + // button.connect('drag-begin', (_, context) => { + // Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(button)); + // button.toggleClassName('hidden', true); + // }); + // button.connect('drag-end', () => button.toggleClassName('hidden', false)); + + }, +}); + +const workspace = index => { + const fixed = Gtk.Fixed.new(); + const widget = Widget.Box({ + className: 'overview-tasks-workspace', + valign: 'center', + style: ` + min-width: ${SCREEN_WIDTH * OVERVIEW_SCALE}px; + min-height: ${SCREEN_HEIGHT * OVERVIEW_SCALE}px; + `, + connections: [[Hyprland, box => { + box.toggleClassName('active', Hyprland.active.workspace.id === index); + }]], + children: [Widget.EventBox({ + hexpand: true, + vexpand: true, + onPrimaryClickRelease: () => { + execAsync([`bash`, `-c`, `hyprctl dispatch workspace ${index}`, `&`]).catch(print); + App.closeWindow('overview'); + }, + // onSecondaryClick: (eventbox) => { + // const menu = Widget({ + // type: Gtk.Menu, + // setup: menu => { + // menu.append(ContextWorkspaceArray({ label: "Dump windows to workspace", onClickBinary: `${App.configDir}/scripts/dumptows`, thisWorkspace: Number(index) })); + // menu.append(ContextWorkspaceArray({ label: "Swap windows with workspace", onClickBinary: `${App.configDir}/scripts/dumptows`, thisWorkspace: Number(index) })); + // menu.show_all(); + // } + // }); + // menu.popup_at_pointer(null); // Show the menu at the pointer's position + // }, + setup: eventbox => { + eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY); + eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => { + execAsync([`bash`, `-c`, `hyprctl dispatch movetoworkspacesilent ${index},address:${data.get_text()}`, `&`]).catch(print); + }); + }, + child: fixed, + })], + }); + widget.update = clients => { + clients = clients.filter(({ workspace: { id } }) => id === index); + + // this is for my monitor layout + // shifts clients back by SCREEN_WIDTHpx if necessary + clients = clients.map(client => { + // console.log(client); + const [x, y] = client.at; + if (x > SCREEN_WIDTH) + client.at = [x - SCREEN_WIDTH, y]; + return client; + }); + + fixed.get_children().forEach(ch => ch.destroy()); + clients.forEach(c => c.mapped && fixed.put(client(c), c.at[0] * OVERVIEW_SCALE, c.at[1] * OVERVIEW_SCALE)); + fixed.show_all(); + }; + return widget; +}; + +const arr = (s, n) => { + const array = []; + for (let i = 0; i < n; i++) + array.push(s + i); + + return array; +}; + +const OverviewRow = ({ startWorkspace = 1, workspaces = 5, windowName = 'overview' }) => Widget.Box({ + children: arr(startWorkspace, workspaces).map(workspace), + properties: [['update', box => { + execAsync('hyprctl -j clients').then(clients => { + const json = JSON.parse(clients); + box.get_children().forEach(ch => ch.update(json)); + }).catch(print); + }]], + setup: box => box._update(box), + connections: [[Hyprland, box => { + if (!App.getWindow(windowName).visible) + return; + + box._update(box); + }]], +}); + + +export const SearchAndWindows = () => { + var _appSearchResults = []; + + const clickOutsideToClose = Widget.EventBox({ + onPrimaryClick: () => App.closeWindow('overview'), + onSecondaryClick: () => App.closeWindow('overview'), + onMiddleClick: () => App.closeWindow('overview'), + }); + const resultsBox = Widget.Box({ + className: 'spacing-v-15 overview-search-results', + vertical: true, + vexpand: true, + }); + const resultsRevealer = Widget.Revealer({ + transitionDuration: 200, + revealChild: false, + transition: 'slide_down', + // duration: 200, + halign: 'center', + child: resultsBox, + }); + const overviewRevealer = Widget.Revealer({ + revealChild: true, + transition: 'slide_down', + transitionDuration: 200, + child: Widget.Box({ + vertical: true, + className: 'overview-tasks', + children: [ + OverviewRow({ startWorkspace: 1, workspaces: 5 }), + OverviewRow({ startWorkspace: 6, workspaces: 5 }), + ] + }), + }); + const entryPromptRevealer = Widget.Revealer({ + transition: 'crossfade', + transitionDuration: 150, + revealChild: true, + halign: 'center', + child: Widget.Label({ + className: 'overview-search-prompt txt-small txt', + label: searchPromptTexts[Math.floor(Math.random() * searchPromptTexts.length)], + }) + }); + + const entryIconRevealer = Widget.Revealer({ + transition: 'crossfade', + transitionDuration: 150, + revealChild: false, + halign: 'end', + child: Widget.Label({ + className: 'txt txt-large icon-material overview-search-icon', + label: 'search', + }), + }); + + const entryIcon = Widget.Box({ + className: 'overview-search-prompt-box', + setup: box => box.pack_start(entryIconRevealer, true, true, 0), + }); + + const entry = Widget.Entry({ + className: 'overview-search-box txt-small txt', + halign: 'center', + onAccept: ({ text }) => { // This is when you press Enter + const isAction = text.startsWith('>'); + if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a workaround + try { + const fullResult = eval(text); + // copy + execAsync(['bash', '-c', `wl-copy '${fullResult}'`, `&`]).catch(print); + App.closeWindow('overview'); + return; + } catch (e) { + // console.log(e); + } + } + if (_appSearchResults.length > 0) { + App.closeWindow('overview'); + _appSearchResults[0].launch(); + return; + } + else if (text[0] == '>') { // Custom commands + launchCustomCommand(text); + return; + } + // Fallback: Execute command + if (!isAction && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { + if (text.startsWith('sudo')) + execAndClose(text, true); + else + execAndClose(text, false); + } + + else { + App.closeWindow('overview'); + execAsync(['xdg-open', `https://www.google.com/search?q=${text}`]).catch(print); + } + }, + // Actually onChange but this is ta workaround for a bug + connections: [ + ['notify::text', (entry) => { // This is when you type + const isAction = entry.text.startsWith('>'); + resultsBox.get_children().forEach(ch => ch.destroy()); + //check empty if so then dont do stuff + if (entry.text == '') { + resultsRevealer.set_reveal_child(false); + overviewRevealer.set_reveal_child(true); + entryPromptRevealer.set_reveal_child(true); + entryIconRevealer.set_reveal_child(false); + entry.toggleClassName('overview-search-box-extended', false); + searching = false; + } + else { + const text = entry.text; + resultsRevealer.set_reveal_child(true); + overviewRevealer.set_reveal_child(false); + entryPromptRevealer.set_reveal_child(false); + entryIconRevealer.set_reveal_child(true); + entry.toggleClassName('overview-search-box-extended', true); + _appSearchResults = Applications.query(text); + + // Calculate + if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a workaround. + try { + const fullResult = eval(text); + resultsBox.add(CalculationResultButton({ result: fullResult, text: text })); + } catch (e) { + // console.log(e); + } + } + if (isAction) { // Eval on typing is dangerous, this is a workaround. + resultsBox.add(CustomCommandButton({ text: entry.text })); + } + // Add application entries + let appsToAdd = MAX_RESULTS; + _appSearchResults.forEach(app => { + if (appsToAdd == 0) return; + resultsBox.add(DesktopEntryButton(app)); + appsToAdd--; + }); + + // Fallbacks + // if the first word is an actual command + if (!isAction && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') { + resultsBox.add(ExecuteCommandButton({ command: entry.text, terminal: entry.text.startsWith('sudo') })); + } + + // Add fallback: search + resultsBox.add(SearchButton({ text: entry.text })); + resultsBox.show_all(); + searching = true; + } + }] + ], + }); + + return Widget.Box({ + vertical: true, + children: [ + clickOutsideToClose, + Widget.Box({ + halign: 'center', + children: [ + entry, + Widget.Box({ + className: 'overview-search-icon-box', + setup: box => box.pack_start(entryPromptRevealer, true, true, 0), + }), + entryIcon, + ] + }), + overviewRevealer, + resultsRevealer, + ], + connections: [ + [App, (_b, name, visible) => { + if (name == 'overview' && !visible) { + entryPromptRevealer.child.label = searchPromptTexts[Math.floor(Math.random() * searchPromptTexts.length)]; + resultsBox.children = []; + entry.set_text(''); + } + }], + ], + }); +}; diff --git a/desktops/hyprland/ags/modules/quicktoggles.js b/desktops/hyprland/ags/modules/quicktoggles.js new file mode 100644 index 0000000..057d5bf --- /dev/null +++ b/desktops/hyprland/ags/modules/quicktoggles.js @@ -0,0 +1,148 @@ +import { Widget, Utils, Service } from '../imports.js'; +import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js'; +import Network from 'resource:///com/github/Aylur/ags/service/network.js'; +const { execAsync, exec } = Utils; +import { BluetoothIndicator, NetworkIndicator } from "./statusicons.js"; +import { setupCursorHover } from "./lib/cursorhover.js"; +import { MaterialIcon } from './lib/materialicon.js'; + +export const ToggleIconWifi = (props = {}) => Widget.Button({ + className: 'txt-small sidebar-iconbutton', + tooltipText: 'Wifi | Right-click to configure', + onClicked: Network.toggleWifi, + onSecondaryClickRelease: () => { + execAsync(['bash', '-c', 'XDG_CURRENT_DESKTOP="gnome" gnome-control-center wifi', '&']); + }, + child: NetworkIndicator(), + connections: [ + [Network, button => { + button.toggleClassName('sidebar-button-active', Network.wifi?.internet == 'connected' || Network.wired?.internet == 'connected') + }], + [Network, button => { + button.tooltipText = (`${Network.wifi?.ssid} | Right-click to configure` || 'Unknown'); + }], + ], + setup: (button) => setupCursorHover(button), + ...props, +}); + +export const ToggleIconBluetooth = (props = {}) => Widget.Button({ + className: 'txt-small sidebar-iconbutton', + tooltipText: 'Bluetooth | Right-click to configure', + onClicked: () => { // Provided service doesn't work hmmm + const status = Bluetooth?.enabled; + if (status) { + exec('rfkill block bluetooth'); + } + else { + exec('rfkill unblock bluetooth'); + } + }, + onSecondaryClickRelease: () => { + execAsync(['bash', '-c', 'XDG_CURRENT_DESKTOP="gnome" gnome-control-center bluetooth', '&']); + }, + child: BluetoothIndicator(), + connections: [ + [Bluetooth, button => { + button.toggleClassName('sidebar-button-active', Bluetooth?.enabled) + }], + ], + setup: (button) => setupCursorHover(button), + ...props, +}); + +export const HyprToggleIcon = (icon, name, hyprlandConfigValue, props = {}) => Widget.Button({ + className: 'txt-small sidebar-iconbutton', + tooltipText: `${name}`, + onClicked: (button) => { + // Set the value to 1 - value + Utils.execAsync(`hyprctl -j getoption ${hyprlandConfigValue}`).then((result) => { + const currentOption = JSON.parse(result).int; + execAsync(['bash', '-c', `hyprctl keyword ${hyprlandConfigValue} ${1 - currentOption} &`]).catch(print); + button.toggleClassName('sidebar-button-active', currentOption == 0); + }).catch(print); + }, + child: MaterialIcon(icon, 'norm', { halign: 'center' }), + setup: button => { + button.toggleClassName('sidebar-button-active', JSON.parse(Utils.exec(`hyprctl -j getoption ${hyprlandConfigValue}`)).int == 1); + setupCursorHover(button); + }, + ...props, +}) + +export const ModuleNightLight = (props = {}) => Widget.Button({ + className: 'txt-small sidebar-iconbutton', + tooltipText: 'Night Light', + onClicked: (button) => { + // Set the value to 1 - value + const shaderPath = JSON.parse(exec('hyprctl -j getoption decoration:screen_shader')).str; + if (shaderPath != "[[EMPTY]]" && shaderPath != "") { + execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader ''`]).catch(print); + button.toggleClassName('sidebar-button-active', false); + } + else { + execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader ~/.config/hypr/shaders/extradark.frag`]).catch(print); + button.toggleClassName('sidebar-button-active', true); + } + }, + child: MaterialIcon('nightlight', 'norm'), + setup: (button) => setupCursorHover(button), + ...props, +}) + +export const ModuleEditIcon = (props = {}) => Widget.Button({ // TODO: Make this work + ...props, + className: 'txt-small sidebar-iconbutton', + onClicked: () => { + execAsync(['bash', '-c', 'XDG_CURRENT_DESKTOP="gnome" gnome-control-center', '&']); + App.toggleWindow('sideright'); + }, + child: MaterialIcon('edit', 'norm'), + setup: button => { + setupCursorHover(button); + } +}) + +export const ModuleReloadIcon = (props = {}) => Widget.Button({ + ...props, + className: 'txt-small sidebar-iconbutton', + tooltipText: 'Reload Hyprland', + onClicked: () => { + execAsync(['bash', '-c', 'hyprctl reload &']); + App.toggleWindow('sideright'); + }, + child: MaterialIcon('refresh', 'norm'), + setup: button => { + setupCursorHover(button); + } +}) + +export const ModuleSettingsIcon = (props = {}) => Widget.Button({ + ...props, + className: 'txt-small sidebar-iconbutton', + tooltipText: 'Open Settings', + onClicked: () => { + execAsync(['bash', '-c', 'XDG_CURRENT_DESKTOP="gnome" gnome-control-center', '&']); + App.toggleWindow('sideright'); + }, + child: MaterialIcon('settings', 'norm'), + setup: button => { + setupCursorHover(button); + } +}) + +export const ModulePowerIcon = (props = {}) => Widget.Button({ + ...props, + className: 'txt-small sidebar-iconbutton', + tooltipText: 'Session', + onClicked: () => { + App.toggleWindow('session'); + }, + child: MaterialIcon('power_settings_new', 'norm'), + setup: button => { + setupCursorHover(button); + } +}) + + + diff --git a/desktops/hyprland/ags/modules/rightspace.js b/desktops/hyprland/ags/modules/rightspace.js new file mode 100644 index 0000000..2ecc867 --- /dev/null +++ b/desktops/hyprland/ags/modules/rightspace.js @@ -0,0 +1,44 @@ +import { App, Service, Utils, Widget } from '../imports.js'; +import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; +import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; +const { exec, execAsync, CONFIG_DIR } = Utils; +import Indicator from '../scripts/indicator.js'; +import { StatusIcons } from "./statusicons.js"; +import { RoundedCorner } from "./lib/roundedcorner.js"; +import { Tray } from "./tray.js"; + +export const ModuleRightSpace = () => Widget.EventBox({ + onScrollUp: () => { + if (Audio.speaker == null) return; + Audio.speaker.volume += 0.03; + Indicator.popup(1); + }, + onScrollDown: () => { + if (Audio.speaker == null) return; + Audio.speaker.volume -= 0.03; + Indicator.popup(1); + }, + onPrimaryClick: () => App.toggleWindow('sideright'), + onSecondaryClick: () => Mpris.getPlayer('')?.next(), + onMiddleClick: () => Mpris.getPlayer('')?.playPause(), + child: Widget.Box({ + homogeneous: false, + children: [ + Widget.Box({ + hexpand: true, + className: 'spacing-h-5 txt', + children: [ + Widget.Box({ + hexpand: true, + className: 'spacing-h-15 txt', + setup: box => { + box.pack_end(StatusIcons(), false, false, 0); + box.pack_end(Tray(), false, false, 0); + } + }), + ] + }), + RoundedCorner('topright', { className: 'corner-black' }) + ] + }) +}); diff --git a/desktops/hyprland/ags/modules/sessionscreen.js b/desktops/hyprland/ags/modules/sessionscreen.js new file mode 100644 index 0000000..f99d651 --- /dev/null +++ b/desktops/hyprland/ags/modules/sessionscreen.js @@ -0,0 +1,144 @@ +// This is for the cool memory indicator on the sidebar +// For the right pill of the bar, see system.js +const { Gdk, Gtk } = imports.gi; +const GObject = imports.gi.GObject; +const Lang = imports.lang; +import { App, Service, Utils, Widget } from '../imports.js'; +const { exec, execAsync } = Utils; + +const SessionButton = (name, icon, command, props = {}) => { + const buttonDescription = Widget.Revealer({ + valign: 'end', + transitionDuration: 200, + transition: 'slide_down', + revealChild: false, + child: Widget.Label({ + className: 'txt-smaller session-button-desc', + label: name, + }), + }); + return Widget.Button({ + onClicked: command, + className: 'session-button', + child: Widget.Overlay({ + className: 'session-button-box', + child: Widget.Label({ + vexpand: true, + className: 'icon-material', + label: icon, + }), + overlays: [ + buttonDescription, + ] + }), + onHover: (button) => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'pointer'); + button.get_window().set_cursor(cursor); + buttonDescription.revealChild = true; + }, + onHoverLost: (button) => { + const display = Gdk.Display.get_default(); + const cursor = Gdk.Cursor.new_from_name(display, 'default'); + button.get_window().set_cursor(cursor); + buttonDescription.revealChild = false; + }, + connections: [ + ['focus-in-event', (self) => { + buttonDescription.revealChild = true; + self.toggleClassName('session-button-focused', true); + }], + ['focus-out-event', (self) => { + buttonDescription.revealChild = false; + self.toggleClassName('session-button-focused', false); + }], + ], + ...props, + }); +} + +export default () => { + // lock, logout, sleep + const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('gtklock') }); + const logoutButton = SessionButton('Logout', 'logout', () => { App.closeWindow('session'); execAsync(['bash', '-c', 'loginctl terminate-user $USER']) }); + const sleepButton = SessionButton('Sleep', 'sleep', () => { App.closeWindow('session'); execAsync('systemctl suspend') }); + // hibernate, shutdown, reboot + const hibernateButton = SessionButton('Hibernate', 'downloading', () => { App.closeWindow('session'); execAsync('systemctl hibernate') }); + const shutdownButton = SessionButton('Shutdown', 'power_settings_new', () => { App.closeWindow('session'); execAsync('systemctl poweroff') }); + const rebootButton = SessionButton('Reboot', 'restart_alt', () => { App.closeWindow('session'); execAsync('systemctl reboot') }); + const cancelButton = SessionButton('Cancel', 'close', () => App.closeWindow('session'), { className: 'session-button-cancel' }); + return Widget.Box({ + className: 'session-bg', + style: ` + min-width: ${SCREEN_WIDTH * 2}px; + min-height: ${SCREEN_HEIGHT * 2}px; + `, // Hack to draw over reserved bar space + vertical: true, + children: [ + Widget.EventBox({ + onPrimaryClick: () => App.closeWindow('session'), + onSecondaryClick: () => App.closeWindow('session'), + onMiddleClick: () => App.closeWindow('session'), + }), + Widget.Box({ + halign: 'center', + vexpand: true, + vertical: true, + children: [ + Widget.Box({ + valign: 'center', + vertical: true, + className: 'spacing-v-15', + children: [ + Widget.Box({ + vertical: true, + style: 'margin-bottom: 0.682rem;', + children: [ + Widget.Label({ + className: 'txt-title txt', + label: 'Session', + }), + Widget.Label({ + justify: Gtk.Justification.CENTER, + className: 'txt-small txt', + label: 'Use arrow keys to navigate.\nEnter to select, Esc to cancel.' + }), + ] + }), + Widget.Box({ + halign: 'center', + className: 'spacing-h-15', + children: [ // lock, logout, sleep + lockButton, + logoutButton, + sleepButton, + ] + }), + Widget.Box({ + halign: 'center', + className: 'spacing-h-15', + children: [ // hibernate, shutdown, reboot + hibernateButton, + shutdownButton, + rebootButton, + ] + }), + Widget.Box({ + halign: 'center', + className: 'spacing-h-15', + children: [ // hibernate, shutdown, reboot + cancelButton, + ] + }), + ] + }) + ] + }) + ], + connections: [ + [App, (_b, name, visible) => { + if (visible) lockButton.grab_focus(); // Lock is the default option + }], + ], + }); +} diff --git a/desktops/hyprland/ags/modules/sideleft.js b/desktops/hyprland/ags/modules/sideleft.js new file mode 100644 index 0000000..7fb347c --- /dev/null +++ b/desktops/hyprland/ags/modules/sideleft.js @@ -0,0 +1,75 @@ +const { Gdk, Gtk } = imports.gi; +import { App, Service, Utils, Widget } from '../imports.js'; +import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +const { execAsync, exec } = Utils; +const { Box, EventBox, Button, Label, Scrollable } = Widget; + +const CLIPBOARD_SHOWN_ENTRIES = 20; + +const ClipboardItems = () => { + return Box({ + vertical: true, + className: 'spacing-v-5', + connections: [ + [App, (box, name, visible) => { + if (name != 'sideleft') + return; + + let clipboardContents = exec('cliphist list'); // Output is lines like this: 1000 copied text + clipboardContents = clipboardContents.split('\n'); + + // console.log(clipboardContents); + // console.log(`bash -c 'echo "${clipboardContents[0]}" | sed "s/ /\\t/" | cliphist decode'`); + // console.log(exec(`bash -c 'echo "${clipboardContents[0]}" | sed "s/ /\\t/" | cliphist decode'`)); + + box.children = clipboardContents.map((text, i) => { + if (i >= CLIPBOARD_SHOWN_ENTRIES) return; + return Button({ + onClicked: () => { + print(`bash` + `-c` + `echo "${clipboardContents[i]}" | sed "s/ /\\\t/" | cliphist decode | wl-copy`); + execAsync(`bash`, `-c`, `echo "${clipboardContents[i]}" | sed "s/ /\\\t/" | cliphist decode | wl-copy`).catch(print); + App.closeWindow('sideleft'); + }, + className: 'sidebar-clipboard-item', + child: Box({ + children: [ + Label({ + label: text, + className: 'txt-small', + truncate: 'end', + }) + ] + }) + }) + }); + }] + ] + }); +} + +export default () => Box({ + vertical: true, + children: [ + EventBox({ + onPrimaryClick: () => App.closeWindow('sideleft'), + onSecondaryClick: () => App.closeWindow('sideleft'), + onMiddleClick: () => App.closeWindow('sideleft'), + }), + ClipboardItems(), + // Box({ + // vertical: true, + // vexpand: true, + // className: 'sidebar-left', + // children: [ + // Widget.Box({ + // className: 'spacing-v-5', + // children: [ + // ClipboardItems(), + // ] + // }) + // ], + // }), + ] +}); + diff --git a/desktops/hyprland/ags/modules/sideright.js b/desktops/hyprland/ags/modules/sideright.js new file mode 100644 index 0000000..d900578 --- /dev/null +++ b/desktops/hyprland/ags/modules/sideright.js @@ -0,0 +1,109 @@ +const { Gdk, Gtk } = imports.gi; +import { Utils, Widget } from '../imports.js'; +const { execAsync, exec } = Utils; +const { Box, EventBox } = Widget; +import { + ToggleIconBluetooth, ToggleIconWifi, HyprToggleIcon, ModuleNightLight, + ModuleEditIcon, ModuleReloadIcon, ModuleSettingsIcon, ModulePowerIcon +} from "./quicktoggles.js"; +import ModuleNotificationList from "./notificationlist.js"; +import { ModuleMusicControls } from "./musiccontrols.js"; +import { ModuleCalendar } from "./calendar.js"; + +const NUM_OF_TOGGLES_PER_LINE = 5; + +const togglesFlowBox = Widget({ + type: Gtk.FlowBox, + className: 'sidebar-group spacing-h-10', + setup: (self) => { + self.set_max_children_per_line(NUM_OF_TOGGLES_PER_LINE); + self.add(ToggleIconWifi({ hexpand: 'true' })); + self.add(ToggleIconBluetooth({ hexpand: 'true' })); + self.add(HyprToggleIcon('mouse', 'Raw input', 'input:force_no_accel', { hexpand: 'true' })); + self.add(HyprToggleIcon('front_hand', 'No touchpad while typing', 'input:touchpad:disable_while_typing', { hexpand: 'true' })); + self.add(ModuleNightLight({ hexpand: 'true' })); + // Setup flowbox rearrange + self.connect('child-activated', (self, child) => { + if (child.get_index() === 0) { + self.reorder_child(child, self.get_children().length - 1); + } else { + self.reorder_child(child, 0); + } + }); + } +}) + +const togglesBox = Widget.Box({ + className: 'sidebar-group spacing-h-10', + children: [ + ToggleIconWifi({ hexpand: 'true' }), + ToggleIconBluetooth({ hexpand: 'true' }), + HyprToggleIcon('mouse', 'Raw input', 'input:force_no_accel', { hexpand: 'true' }), + HyprToggleIcon('front_hand', 'No touchpad while typing', 'input:touchpad:disable_while_typing', { hexpand: 'true' }), + ModuleNightLight({ hexpand: 'true' }), + ] +}) + +export default () => Box({ + // vertical: true, + vexpand: true, + hexpand: true, + children: [ + EventBox({ + onPrimaryClick: () => App.closeWindow('sideright'), + onSecondaryClick: () => App.closeWindow('sideright'), + onMiddleClick: () => App.closeWindow('sideright'), + }), + Box({ + vertical: true, + vexpand: true, + className: 'sidebar-right', + children: [ + Box({ + vertical: true, + vexpand: true, + className: 'spacing-v-15', + children: [ + Box({ + vertical: true, + className: 'spacing-v-5', + children: [ + Box({ // Header + className: 'spacing-h-5 sidebar-group-invisible-morehorizpad', + children: [ + Widget.Label({ + className: 'txt-title txt', + connections: [[5000, label => { + execAsync([`date`, "+%H:%M"]).then(timeString => { + label.label = timeString; + }).catch(print); + }]], + }), + Widget.Label({ + halign: 'center', + className: 'txt-small txt', + connections: [[5000, label => { + execAsync(['bash', '-c', `uptime -p | sed -e 's/up //;s/ hours,/h/;s/ minutes/m/'`]).then(upTimeString => { + label.label = `• uptime ${upTimeString}`; + }).catch(print); + }]], + }), + Widget.Box({ hexpand: true }), + // ModuleEditIcon({ halign: 'end' }), // TODO: Make this work + ModuleReloadIcon({ halign: 'end' }), + ModuleSettingsIcon({ halign: 'end' }), + ModulePowerIcon({ halign: 'end' }), + ] + }), + // togglesFlowBox, + togglesBox, + ] + }), + ModuleNotificationList({ vexpand: true, }), + ModuleCalendar(), + ] + }), + ], + }), + ] +}); diff --git a/desktops/hyprland/ags/modules/statusicons.js b/desktops/hyprland/ags/modules/statusicons.js new file mode 100644 index 0000000..88e34fe --- /dev/null +++ b/desktops/hyprland/ags/modules/statusicons.js @@ -0,0 +1,93 @@ +import { Service, Utils, Widget } from '../imports.js'; +const { exec, execAsync } = Utils; +import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; +import Battery from 'resource:///com/github/Aylur/ags/service/battery.js'; +import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js'; +import Network from 'resource:///com/github/Aylur/ags/service/network.js'; + +export const BluetoothIndicator = () => Widget.Stack({ + transition: 'slide_up_down', + items: [ + ['true', Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth' })], + ['false', Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth_disabled' })], + ], + connections: [[Bluetooth, stack => { stack.shown = String(Bluetooth.enabled); }]], +}); + + +const NetworkWiredIndicator = () => Widget.Stack({ + transition: 'slide_up_down', + items: [ + ['unknown', Widget.Label({ className: 'txt-norm icon-material', label: 'wifi_off' })], + ['disconnected', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_off' })], + ['disabled', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_statusbar_not_connected' })], + ['connected', Widget.Label({ className: 'txt-norm icon-material', label: 'lan' })], + ['connecting', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_0_bar' })], + ], + connections: [[Network, stack => { + if (!Network.wired) + return; + + const { internet } = Network.wired; + if (internet === 'connected' || internet === 'connecting') + stack.shown = internet; + + if (Network.connectivity !== 'full') + stack.shown = 'disconnected'; + + stack.shown = 'disabled'; + }]], +}); + +const NetworkWifiIndicator = () => Widget.Stack({ + transition: 'slide_up_down', + items: [ + ['disabled', Widget.Label({ className: 'txt-norm icon-material', label: 'wifi_off' })], + ['disconnected', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_off' })], + ['connecting', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_statusbar_not_connected' })], + ['4', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_4_bar' })], + ['3', Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_3_bar' })], + ['2', Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_2_bar' })], + ['1', Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_1_bar' })], + ['0', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_0_bar' })], + ], + connections: [[Network, + stack => { + if (!Network.wifi) + return; + const { internet, enabled, strength } = Network.wifi; + + if (internet == 'connected') { + stack.shown = String(Math.ceil(strength / 25)); + } + else { + stack.shown = 'disconnected' + } + } + ]], +}); + +export const NetworkIndicator = () => Widget.Stack({ + transition: 'slide_up_down', + items: [ + ['wifi', NetworkWifiIndicator()], + ['wired', NetworkWiredIndicator()], + ], + connections: [[Network, stack => { + const primary = Network.primary || 'wifi'; + stack.shown = primary; + }]], +}); + +export const StatusIcons = (props = {}) => Widget.Box({ + ...props, + children: [Widget.EventBox({ + child: Widget.Box({ + className: 'spacing-h-15', + children: [ + BluetoothIndicator(), + NetworkIndicator(), + ] + }) + })] +}); diff --git a/desktops/hyprland/ags/modules/sysinfo.js b/desktops/hyprland/ags/modules/sysinfo.js new file mode 100644 index 0000000..d5cc797 --- /dev/null +++ b/desktops/hyprland/ags/modules/sysinfo.js @@ -0,0 +1,108 @@ +// This is for the cool memory indicator on the sidebar +// For the right pill of the bar, see system.js +const { Gdk, Gtk } = imports.gi; +const GObject = imports.gi.GObject; +const Lang = imports.lang; +import { App, Service, Utils, Widget } from '../imports.js'; +import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +import Network from 'resource:///com/github/Aylur/ags/service/network.js'; +const { execAsync, exec } = Utils; +import { CircularProgress } from "./lib/circularprogress.js"; +import { MaterialIcon } from "./lib/materialicon.js"; + +let cpuUsageQueue = []; +const CPU_HISTORY_LENGTH = 10; + +export const ModuleSysInfo = (props = {}) => { + const swapCircle = Widget({ + type: CircularProgress, + className: 'sidebar-memory-swap-circprog', + valign: 'center', + }); + const ramCircle = Widget({ + type: CircularProgress, + className: 'sidebar-memory-ram-circprog margin-right-10', // margin right 10 here cuz overlay can't have margins itself + valign: 'center', + }); + const cpuCircle = Widget.Box({ + children: [Widget({ + type: CircularProgress, + className: 'sidebar-cpu-circprog margin-right-10', // margin right 10 here cuz overlay can't have margins itself + valign: 'center', + })] + }); + const memoryCircles = Widget.Box({ + homogeneous: true, + children: [Widget.Overlay({ + child: ramCircle, + overlays: [ + swapCircle, + ] + })] + }); + const ramText = Widget.Label({ + halign: 'start', xalign: 0, + className: 'txt txt-small', + }); + const swapText = Widget.Label({ + halign: 'start', xalign: 0, + className: 'txt txt-small', + }); + const memoryText = Widget.Box({ + vertical: true, + valign: 'center', + className: 'spacing-v--5', + children: [ + Widget.Box({ + className: 'spacing-h-5', + children: [ + MaterialIcon('memory', 'large', { setup: icon => icon.toggleClassName('txt', true) }), + ramText + ] + }), + Widget.Box({ + className: 'spacing-h-5', + children: [ + MaterialIcon('swap_horiz', 'large', { setup: icon => icon.toggleClassName('txt', true) }), + swapText + ] + }), + ] + }); + return Widget.Box({ + ...props, + className: 'sidebar-group-nopad', + children: [Widget.Scrollable({ + hexpand: true, + vscroll: 'never', + hscroll: 'automatic', + child: Widget.Box({ + className: 'sidebar-sysinfo-grouppad spacing-h--5', + children: [ + memoryCircles, + memoryText, + // cpuCircle, + // maybe make cpu a graph? + ], + connections: [ + [3000, () => { + // Get memory info + const ramString = exec(`bash -c 'free -h --si | rg "Mem:"'`); + const [ramTotal, ramUsed] = ramString.split(/\s+/).slice(1, 3); + const ramPerc = Number(exec(`bash -c "printf '%.1f' \\\"$(free -m | rg Mem | awk '{print ($3/$2)*100}')\\\""`)); + const swapString = exec(`bash -c 'free -h --si | rg "Swap:"'`); + const [swapTotal, swapUsed] = swapString.split(/\s+/).slice(1, 3); + const swapPerc = Number(exec(`bash -c "printf '%.1f' \\\"$(free -m | rg Swap | awk '{print ($3/$2)*100}')\\\""`)); + // const cpuPerc = parseFloat(exec(`bash -c "top -bn1 | grep 'Cpu(s)' | awk '{print $2 + $4}'"`)); + // Set circular progress (font size cuz hack for anims) + ramCircle.style = `font-size: ${ramPerc}px;` + swapCircle.style = `font-size: ${swapPerc}px;` + ramText.label = `${ramUsed} / ${ramTotal}`; + swapText.label = `${swapUsed} / ${swapTotal}`; + }] + ] + }) + })] + }); +}; diff --git a/desktops/hyprland/ags/modules/system.js b/desktops/hyprland/ags/modules/system.js new file mode 100644 index 0000000..8ca7dd4 --- /dev/null +++ b/desktops/hyprland/ags/modules/system.js @@ -0,0 +1,89 @@ +// This is for the right pill of the bar. +// For the cool memory indicator on the sidebar, see sysinfo.js +import { Service, Utils, Widget } from '../imports.js'; +const { exec, execAsync } = Utils; +import Battery from 'resource:///com/github/Aylur/ags/service/battery.js'; + +export const ModuleSystem = () => Widget.EventBox({ + onScrollUp: () => execAsync('hyprctl dispatch workspace -1'), + onScrollDown: () => execAsync('hyprctl dispatch workspace +1'), + child: Widget.Box({ + className: 'bar-group-margin bar-sides', + children: [ + Widget.Box({ + className: 'bar-group bar-group-standalone bar-group-pad-system spacing-h-15', + children: [ + Widget.Box({ // Clock + valign: 'center', + className: 'spacing-h-5', + children: [ + Widget.Label({ + className: 'bar-clock', + connections: [[5000, label => { + execAsync([`date`, "+%H:%M"]).then(timeString => { + label.label = timeString; + }).catch(print); + }]], + }), + Widget.Label({ + className: 'txt-norm txt', + label: '•', + }), + Widget.Label({ + className: 'txt-smallie txt', + connections: [[5000, label => { + execAsync([`date`, "+%A, %d/%m"]).then(dateString => { + label.label = dateString; + }).catch(print); + }]], + }), + ], + }), + /*Widget.Box({ // Battery + valign: 'center', + hexpand: true, + className: 'spacing-h-5 bar-batt', + connections: [[Battery, box => { + box.toggleClassName('bar-batt-low', Battery.percent <= 20); + box.toggleClassName('bar-batt-full', Battery.charged); + }]], + children: [ + Widget.Label({ + className: 'bar-batt-percentage', + connections: [[Battery, label => { + label.label = `${Battery.percent}`; + }]], + }), + Widget.ProgressBar({ + valign: 'center', + hexpand: true, + className: 'bar-prog-batt', + connections: [[Battery, progress => { + progress.value = Math.abs(Battery.percent / 100); // battery could be initially negative wtf + progress.toggleClassName('bar-prog-batt-low', Battery.percent <= 20); + progress.toggleClassName('bar-prog-batt-full', Battery.charged); + }]], + }), + Widget.Revealer({ + transitionDuration: 150, + revealChild: false, + transition: 'slide_left', + child: Widget.Box({ + valign: 'center', + className: 'bar-batt-chargestate-charging', + connections: [[Battery, box => { + box.toggleClassName('bar-batt-chargestate-low', Battery.percent <= 20); + box.toggleClassName('bar-batt-chargestate-full', Battery.charged); + }]], + }), + connections: [[Battery, revealer => { + revealer.revealChild = Battery.charging; + }]], + }), + ], + }),]*/ + ], + }), + ] + }) +}); diff --git a/desktops/hyprland/ags/modules/tray.js b/desktops/hyprland/ags/modules/tray.js new file mode 100644 index 0000000..1871a59 --- /dev/null +++ b/desktops/hyprland/ags/modules/tray.js @@ -0,0 +1,71 @@ +import { Service, Widget } from '../imports.js'; +import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js'; +const { Box, Icon, Button, Revealer } = Widget; +const { Gravity } = imports.gi.Gdk; + +const revealerDuration = 200; + +const SysTrayItem = item => Button({ + className: 'bar-systray-item', + child: Icon({ + halign: 'center', + size: 16, + binds: [['icon', item, 'icon']] + }), + binds: [['tooltipMarkup', item, 'tooltipMarkup']], + // setup: btn => { + // const id = item.menu.connect('popped-up', menu => { + // menu.disconnect(id); + // }); + // }, + onClicked: btn => item.menu.popup_at_widget(btn, Gravity.SOUTH, Gravity.NORTH, null), + onSecondaryClick: btn => item.menu.popup_at_widget(btn, Gravity.SOUTH, Gravity.NORTH, null), +}); + +export const Tray = (props = {}) => { + const trayContent = Box({ + valign: 'center', + className: 'bar-systray bar-group', + properties: [ + ['items', new Map()], + ['onAdded', (box, id) => { + const item = SystemTray.getItem(id); + if (!item) return; + item.menu.className = 'menu'; + if (box._items.has(id) || !item) + return; + const widget = SysTrayItem(item); + box._items.set(id, widget); + box.pack_start(widget, false, false, 0); + box.show_all(); + if (box._items.size === 1) + trayRevealer.revealChild = true; + }], + ['onRemoved', (box, id) => { + if (!box._items.has(id)) + return; + + box._items.get(id).destroy(); + box._items.delete(id); + if (box._items.size === 0) + trayRevealer.revealChild = false; + }], + ], + connections: [ + [SystemTray, (box, id) => box._onAdded(box, id), 'added'], + [SystemTray, (box, id) => box._onRemoved(box, id), 'removed'], + ], + }); + const trayRevealer = Widget.Revealer({ + revealChild: false, + transition: 'slide_left', + transitionDuration: revealerDuration, + child: trayContent, + }); + return Box({ + ...props, + children: [ + trayRevealer, + ] + }); +} diff --git a/desktops/hyprland/ags/modules/workspaces.js b/desktops/hyprland/ags/modules/workspaces.js new file mode 100644 index 0000000..f7637f0 --- /dev/null +++ b/desktops/hyprland/ags/modules/workspaces.js @@ -0,0 +1,97 @@ +import { App, Service, Utils, Widget } from '../imports.js'; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; +import { deflisten } from '../scripts/scripts.js'; + +const WORKSPACE_SIDE_PAD = 0.546; // rem +const NUM_OF_WORKSPACES = 10; +let lastWorkspace = 0; + +const activeWorkspaceIndicator = Widget.Box({ + // style: 'margin-left: -1px;', + children: [ + Widget.Box({ + valign: 'center', + halign: 'start', + className: 'bar-ws-active-box', + connections: [ + [Hyprland.active.workspace, (box) => { + const ws = Hyprland.active.workspace.id; + box.setStyle(` + margin-left: -${1.772 * (10 - ws + 1)}rem; + `); + lastWorkspace = ws; + }], + ], + children: [ + Widget.Label({ + valign: 'center', + className: 'bar-ws-active', + label: `•`, + }) + ] + }) + ] +}); + +export const ModuleWorkspaces = () => Widget.EventBox({ + onScrollUp: () => Utils.execAsync(['bash', '-c', 'hyprctl dispatch workspace -1 &']), + onScrollDown: () => Utils.execAsync(['bash', '-c', 'hyprctl dispatch workspace +1 &']), + onMiddleClickRelease: () => App.toggleWindow('overview'), + onSecondaryClickRelease: () => App.toggleWindow('osk'), + child: Widget.Box({ + homogeneous: true, + className: 'bar-ws-width', + children: [ + Widget.Overlay({ + passThrough: true, + child: Widget.Box({ + homogeneous: true, + className: 'bar-group-center', + children: [Widget.Box({ + className: 'bar-group-standalone bar-group-pad', + })] + }), + overlays: [ + Widget.Box({ + style: ` + padding: 0rem ${WORKSPACE_SIDE_PAD}rem; + `, + children: [ + Widget.Box({ + halign: 'center', + // homogeneous: true, + children: Array.from({ length: NUM_OF_WORKSPACES }, (_, i) => i + 1).map(i => Widget.Button({ + onPrimaryClick: () => Utils.execAsync(['bash', '-c', `hyprctl dispatch workspace ${i} &`]).catch(print), + child: Widget.Label({ + valign: 'center', + label: `${i}`, + className: 'bar-ws txt', + }), + })), + connections: [ + [Hyprland, (box) => { // TODO: connect to the right signal so that it doesn't update too much + // console.log('update'); + const kids = box.children; + kids.forEach((child, i) => { + child.child.toggleClassName('bar-ws-occupied', false); + child.child.toggleClassName('bar-ws-occupied-left', false); + child.child.toggleClassName('bar-ws-occupied-right', false); + child.child.toggleClassName('bar-ws-occupied-left-right', false); + }); + const occupied = Array.from({ length: NUM_OF_WORKSPACES }, (_, i) => Hyprland.getWorkspace(i + 1)?.windows > 0); + for (let i = 0; i < occupied.length; i++) { + if (!occupied[i]) continue; + const child = kids[i]; + child.child.toggleClassName(`bar-ws-occupied${!occupied[i - 1] ? '-left' : ''}${!occupied[i + 1] ? '-right' : ''}`, true); + } + }], + ], + }), + activeWorkspaceIndicator, + ] + }) + ] + }) + ] + }) +}); diff --git a/desktops/hyprland/ags/scripts/README.md b/desktops/hyprland/ags/scripts/README.md new file mode 100755 index 0000000..1292e1c --- /dev/null +++ b/desktops/hyprland/ags/scripts/README.md @@ -0,0 +1,2 @@ +# scripts folder +- For ARM devices, you have to compile C++ files yourself. \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/brightness.js b/desktops/hyprland/ags/scripts/brightness.js new file mode 100644 index 0000000..b86558c --- /dev/null +++ b/desktops/hyprland/ags/scripts/brightness.js @@ -0,0 +1,58 @@ +import { Service, Utils } from '../imports.js'; +const { exec, execAsync } = Utils; + +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); + +class BrightnessService extends Service { + static { + Service.register( + this, + { 'screen-changed': ['float'], }, + { 'screen-value': ['float', 'rw'], }, + ); + } + + _screenValue = 0; + + // the getter has to be in snake_case + get screen_value() { return this._screenValue; } + + // the setter has to be in snake_case too + set screen_value(percent) { + percent = clamp(percent, 0, 1); + this._screenValue = percent; + + Utils.execAsync(`brightnessctl s ${percent * 100}% -q`) + .then(() => { + // signals has to be explicity emitted + this.emit('screen-changed', percent); + this.notify('screen-value'); + + // or use Service.changed(propName: string) which does the above two + // this.changed('screen'); + }) + .catch(print); + } + + constructor() { + super(); + const current = Number(exec('brightnessctl g')); + const max = Number(exec('brightnessctl m')); + this._screenValue = current / max; + } + + // overwriting connectWidget method, let's you + // change the default event that widgets connect to + connectWidget(widget, callback, event = 'screen-changed') { + super.connectWidget(widget, callback, event); + } +} + +// the singleton instance +const service = new BrightnessService(); + +// make it global for easy use with cli +globalThis.brightness = service; + +// export to use in other modules +export default service; \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/calendarlayout.js b/desktops/hyprland/ags/scripts/calendarlayout.js new file mode 100644 index 0000000..691ee5e --- /dev/null +++ b/desktops/hyprland/ags/scripts/calendarlayout.js @@ -0,0 +1,101 @@ +export function getCalendarLayout(d, highlight) { + if (!d) d = new Date(); + var calendar = [...Array(6)].map(() => Array(7)); + var today = [...Array(6)].map(() => Array(7)); + const year = d.getFullYear(); + const month = d.getMonth() + 1; + const day = d.getDate(); + const weekdayOfMonthFirst = new Date(`${year}-${month}-01`).getDay(); + const leapYear = (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)); + const daysInMonth = (((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) ? 31 : ((month == 2 && leapYear) ? 29 : ((month == 2 && !leapYear) ? 28 : 30))); + const daysInNextMonth = ((month == 1 && leapYear) ? 29 : ((month == 1 && !leapYear) ? 28 : ((month == 7 || month == 12) ? 31 : (((month <= 6 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) ? 30 : 31)))); + const daysInLastMonth = ((month == 3 && leapYear) ? 29 : ((month == 3 && !leapYear) ? 28 : ((month == 1 || month == 8) ? 31 : ((month <= 7 && month % 2 == 1) || (month >= 9 && month % 2 == 0)) ? 30 : 31))); + var monthDiff = (weekdayOfMonthFirst == 0 ? 0 : -1); + var dim = daysInLastMonth; + var toFill = (weekdayOfMonthFirst == 0 ? 1 : (daysInLastMonth + 1 - weekdayOfMonthFirst)); + var i = 0, j = 0; + while (i < 6 && j < 7) { + calendar[i][j] = toFill; + if (toFill == day && monthDiff == 0 && highlight) today[i][j] = 1; + else if (monthDiff == 0) today[i][j] = 0; + else today[i][j] = -1; + toFill++; + if (toFill > dim) { + monthDiff++; + if (monthDiff == 0) dim = daysInMonth; + else if (monthDiff == 1) dim = daysInNextMonth; + toFill = 1; + } + j++; + if (j == 7) { + j = 0; + i++; + } + } + var cal = []; + for (var i = 0; i < 6; i++) { + var arr = []; + for (var j = 0; j < 7; j++) { + arr.push({ + day: calendar[i][j], + today: today[i][j] + }); + } + cal.push(arr); + } + + return cal; +} + +export default getCalendarLayout; + +// export function getCalendarLayout(d, highlight) { +// if (!d) d = new Date(); +// var calendar = [...Array(6)].map(() => Array(7)); +// var today = [...Array(6)].map(() => Array(7)); +// const year = d.getFullYear(); +// const month = d.getMonth() + 1; +// const day = d.getDate(); +// const weekdayOfMonthFirst = new Date(`${year}-${month}-01`).getDay(); +// const leapYear = (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)); +// const daysInMonth = (((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) ? 31 : ((month == 2 && leapYear) ? 29 : ((month == 2 && !leapYear) ? 28 : 30))); +// const daysInNextMonth = ((month == 1 && leapYear) ? 29 : ((month == 1 && !leapYear) ? 28 : ((month == 7 || month == 12) ? 31 : (((month <= 6 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) ? 30 : 31)))); +// const daysInLastMonth = ((month == 3 && leapYear) ? 29 : ((month == 3 && !leapYear) ? 28 : ((month == 1 || month == 8) ? 31 : ((month <= 7 && month % 2 == 1) || (month >= 9 && month % 2 == 0)) ? 30 : 31))); +// var monthDiff = (weekdayOfMonthFirst == 1 ? 0 : -1); +// var dim = daysInLastMonth; +// var toFill = (weekdayOfMonthFirst == 1 ? 1 : (weekdayOfMonthFirst == 0 ? (daysInLastMonth - 5) : (daysInLastMonth + 2 - weekdayOfMonthFirst))); +// var i = 0, j = 0; +// while (i < 6 && j < 7) { +// calendar[i][j] = toFill; +// if (toFill == day && monthDiff == 0 && highlight) today[i][j] = 1; +// else if (monthDiff == 0) today[i][j] = 0; +// else today[i][j] = -1; +// toFill++; +// if (toFill > dim) { +// monthDiff++; +// if (monthDiff == 0) dim = daysInMonth; +// else if (monthDiff == 1) dim = daysInNextMonth; +// toFill = 1; +// } +// j++; +// if (j == 7) { +// j = 0; +// i++; +// } +// } +// var cal = []; +// for (var i = 0; i < 6; i++) { +// var arr = []; +// for (var j = 0; j < 7; j++) { +// arr.push({ +// day: calendar[i][j], +// today: today[i][j] +// }); +// } +// cal.push(arr); +// } + +// return cal; +// } + +// export default getCalendarLayout; \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/color_generation/applycolor.sh b/desktops/hyprland/ags/scripts/color_generation/applycolor.sh new file mode 100755 index 0000000..4e301f3 --- /dev/null +++ b/desktops/hyprland/ags/scripts/color_generation/applycolor.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash + +cd "$HOME/.config/ags" || exit + +# filelist=$(ls 'images/svg/template/' | grep -v /) + +# cat scss/_material.scss +colornames=$(cat scss/_material.scss | cut -d: -f1) +colorstrings=$(cat scss/_material.scss | cut -d: -f2 | cut -d ' ' -f2 | cut -d ";" -f1) +IFS=$'\n' +# filearr=( $filelist ) # Get colors +colorlist=( $colornames ) # Array of color names +colorvalues=( $colorstrings ) # Array of color values + +transparentize() { + local hex="$1" + local alpha="$2" + local red green blue + + red=$((16#${hex:1:2})) + green=$((16#${hex:3:2})) + blue=$((16#${hex:5:2})) + + printf 'rgba(%d, %d, %d, %.2f)\n' "$red" "$green" "$blue" "$alpha" +} + +get_light_dark() { + lightdark="" + if [ ! -f ~/.cache/ags/user/colormode.txt ]; then + echo "" > ~/.cache/ags/user/colormode.txt + else + lightdark=$(cat ~/.cache/ags/user/colormode.txt) # either "" or "-l" + fi + echo "$lightdark" +} + +# apply_svgs() { +# for i in "${!filearr[@]}"; do # Loop through folders +# colorvalue=$(echo "$colorscss" | grep "${filearr[$i]}" | awk '{print $2}' | cut -d ";" -f1) +# for file in images/svg/template/"${filearr[$i]}"/*; do # Loop through files +# cp "$file" images/svg/ +# sed -i "s/black/$colorvalue/g" images/svg/"${file##*/}" +# done +# done +# } + +apply_gtklock() { + # Check if scripts/templates/gtklock/main.scss exists + if [ ! -f "scripts/templates/gtklock/main.scss" ]; then + echo "SCSS not found. Fallback to CSS." + else + sassc ~/.config/ags/scripts/templates/gtklock/main.scss ~/.config/gtklock/style.css + return + fi + + # Check if scripts/templates/gtklock/style.css exists + if [ ! -f "scripts/templates/gtklock/style.css" ]; then + echo "Template file not found for Gtklock. Skipping that." + return + fi + # Copy template + cp "scripts/templates/gtklock/style.css" "$HOME/.config/gtklock/style.css" + # Apply colors + for i in "${!colorlist[@]}"; do + sed -i "s/${colorlist[$i]};/${colorvalues[$i]};/g" "$HOME/.config/gtklock/style.css" + done +} + +apply_fuzzel() { + # Check if scripts/templates/fuzzel/fuzzel.ini exists + if [ ! -f "scripts/templates/fuzzel/fuzzel.ini" ]; then + echo "Template file not found for Fuzzel. Skipping that." + return + fi + # Copy template + cp "scripts/templates/fuzzel/fuzzel.ini" "$HOME/.config/fuzzel/fuzzel.ini" + # Apply colors + for i in "${!colorlist[@]}"; do + sed -i "s/${colorlist[$i]}ff/${colorvalues[$i]#\#}ff/g" "$HOME/.config/fuzzel/fuzzel.ini" + sed -i "s/${colorlist[$i]}cc/${colorvalues[$i]#\#}cc/g" "$HOME/.config/fuzzel/fuzzel.ini" + done +} + +apply_foot() { + # Check if scripts/templates/foot/foot.ini exists + if [ ! -f "scripts/templates/foot/foot.ini" ]; then + echo "Template file not found for Foot. Skipping that." + return + fi + # Copy template + cp "scripts/templates/foot/foot.ini" "$HOME/.config/foot/foot.ini" + # Apply colors + for i in "${!colorlist[@]}"; do + sed -i "s/${colorlist[$i]} #/${colorvalues[$i]#\#}/g" "$HOME/.config/foot/foot.ini" # note: ff because theyre opaque + done +} + +apply_hyprland() { + # Check if scripts/templates/hypr/colors.conf exists + if [ ! -f "scripts/templates/hypr/colors.conf" ]; then + echo "Template file not found for Hyprland colors. Skipping that." + return + fi + # Copy template + cp "scripts/templates/hypr/colors.conf" "$HOME/.config/hypr/colors.conf" + # Apply colors + for i in "${!colorlist[@]}"; do + sed -i "s/(${colorlist[$i]}/(${colorvalues[$i]#\#}/g" "$HOME/.config/hypr/colors.conf" + done +} + +apply_gtk() { # Using gradience-cli + lightdark=$(get_light_dark) + + background=$(cat scss/_material.scss | grep "background" | awk '{print $2}' | cut -d ";" -f1) + secondaryContainer=$(cat scss/_material.scss | grep "secondaryContainer" | awk '{print $2}' | cut -d ";" -f1) + window_bg_color=$(transparentize "$background" 0.9) + card_bg_color=$(transparentize "$background" 0.2) + headerbar_border_color=$(transparentize "$secondaryContainer" 0.12) + + # Copy template + cp "scripts/templates/gradience/preset_template.json" "scripts/templates/gradience/preset.json" + + # Apply colors + for i in "${!colorlist[@]}"; do + sed -i "s/\"${colorlist[$i]}\"/\"${colorvalues[$i]}\"/g" "scripts/templates/gradience/preset.json" + done + sed -i "s|\"\$windowBgColor\"|\"$window_bg_color\"|g" "scripts/templates/gradience/preset.json" + sed -i "s|\"\$cardBgColor\"|\"$card_bg_color\"|g" "scripts/templates/gradience/preset.json" + sed -i "s|\"\$headerbarBorderColor\"|\"$headerbar_border_color\"|g" "scripts/templates/gradience/preset.json" + + gradience-cli apply -p scripts/templates/gradience/preset.json --gtk both + + # Set light/dark preference + # And set GTK theme manually as Gradience defaults to light adw-gtk3 + # (which is unreadable when broken when you use dark mode) + if [ "$lightdark" = "-l" ]; then + gsettings set org.gnome.desktop.interface color-scheme 'prefer-light' + gsettings set org.gnome.desktop.interface gtk-application-prefer-dark-theme false + gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3 + else + gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' + gsettings set org.gnome.desktop.interface gtk-application-prefer-dark-theme true + gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3-dark + fi +} + +# apply_svgs +apply_gtklock +apply_fuzzel +apply_foot +apply_hyprland +apply_gtk diff --git a/desktops/hyprland/ags/scripts/color_generation/colorgen.sh b/desktops/hyprland/ags/scripts/color_generation/colorgen.sh new file mode 100755 index 0000000..4da2eb4 --- /dev/null +++ b/desktops/hyprland/ags/scripts/color_generation/colorgen.sh @@ -0,0 +1,55 @@ +#!/usr/bin/bash + +# check if no arguments +if [ $# -eq 0 ]; then + echo "Usage: colorgen.sh /path/to/image (--apply)" + exit 1 +fi + +# check if the file ~/.cache/ags/user/colormode.txt exists. if not, create it. else, read it to $lightdark +lightdark="" +if [ ! -f "$HOME/.cache/ags/user/colormode.txt" ]; then + echo "" > "$HOME/.cache/ags/user/colormode.txt" +else + lightdark=$(cat "$HOME/.cache/ags/user/colormode.txt") # either "" or "-l" +fi +# check if the file ~/.cache/ags/user/colorbackend.txt exists. if not, create it. else, read it to $lightdark +backend="material" +if [ ! -f "$HOME/.cache/ags/user/colorbackend.txt" ]; then + echo "material" > "$HOME/.cache/ags/user/colorbackend.txt" +else + backend=$(cat "$HOME/.cache/ags/user/colorbackend.txt") # either "" or "-l" +fi + +cd "$HOME/.config/ags/scripts/" || exit +if [ "$backend" = "material" ]; then + color_generation/generate_colors_material.py --path "$1" "$lightdark" > $HOME/.cache/ags/user/generated_colors.txt + if [ "$2" = "--apply" ]; then + cp $HOME/.cache/ags/user/generated_colors.txt "$HOME/.config/ags/scss/_material.scss" + color_generation/applycolor.sh + fi +elif [ "$backend" = "pywal" ]; then + # clear and generate + wal -c + echo wal -i "$1" -n -t -s -e "$lightdark" -q + wal -i "$1" -n -t -s -e $lightdark -q + # copy scss + cp "$HOME/.cache/wal/colors.scss" $HOME/.cache/ags/user/generated_colors.txt + + cat color_generation/pywal_to_material.scss >> $HOME/.cache/ags/user/generated_colors.txt + if [ "$2" = "--apply" ]; then + sassc $HOME/.cache/ags/user/generated_colors.txt $HOME/.cache/ags/user/generated_colors_classes.scss --style compact + sed -i "s/ { color//g" $HOME/.cache/ags/user/generated_colors_classes.scss + sed -i "s/\./$/g" $HOME/.cache/ags/user/generated_colors_classes.scss + sed -i "s/}//g" $HOME/.cache/ags/user/generated_colors_classes.scss + if [ "$lightdark" = "-l" ]; then + printf "\n"'$darkmode: false;'"\n" >> $HOME/.cache/ags/user/generated_colors_classes.scss + else + printf "\n"'$darkmode: true;'"\n" >> $HOME/.cache/ags/user/generated_colors_classes.scss + fi + + cp $HOME/.cache/ags/user/generated_colors_classes.scss "$HOME/.config/ags/scss/_material.scss" + + color_generation/applycolor.sh + fi +fi diff --git a/desktops/hyprland/ags/scripts/color_generation/generate_colors_material.py b/desktops/hyprland/ags/scripts/color_generation/generate_colors_material.py new file mode 100755 index 0000000..d26ecf8 --- /dev/null +++ b/desktops/hyprland/ags/scripts/color_generation/generate_colors_material.py @@ -0,0 +1,93 @@ +#!/bin/python3 +from material_color_utilities_python import * +from pathlib import Path +import sys +import subprocess + +img = 0 +newtheme=0 +if len(sys.argv) > 1 and sys.argv[1] == '--path': + img = Image.open(sys.argv[2]) + basewidth = 64 + wpercent = (basewidth/float(img.size[0])) + hsize = int((float(img.size[1])*float(wpercent))) + img = img.resize((basewidth,hsize),Image.Resampling.LANCZOS) + newtheme = themeFromImage(img) +elif len(sys.argv) > 1 and sys.argv[1] == '--color': + colorstr = sys.argv[2] + newtheme = themeFromSourceColor(argbFromHex(colorstr)) +else: + imagePath = subprocess.check_output("swww query | awk -F 'image: ' '{print $2}'", shell=True) + imagePath = imagePath[:-1].decode("utf-8") + img = Image.open(imagePath) + basewidth = 64 + wpercent = (basewidth/float(img.size[0])) + hsize = int((float(img.size[1])*float(wpercent))) + img = img.resize((basewidth,hsize),Image.Resampling.LANCZOS) + newtheme = themeFromImage(img) + +colorscheme=0 +if("-l" in sys.argv): + colorscheme = newtheme.get('schemes').get('light') + print('$darkmode: false;') +else: + colorscheme = newtheme.get('schemes').get('dark') + print('$darkmode: true;') + +primary = colorscheme.get_primary() +onPrimary = colorscheme.get_onPrimary() +primaryContainer = colorscheme.get_primaryContainer() +onPrimaryContainer = colorscheme.get_onPrimaryContainer() +secondary = colorscheme.get_secondary() +onSecondary = colorscheme.get_onSecondary() +secondaryContainer = colorscheme.get_secondaryContainer() +onSecondaryContainer = colorscheme.get_onSecondaryContainer() +tertiary = colorscheme.get_tertiary() +onTertiary = colorscheme.get_onTertiary() +tertiaryContainer = colorscheme.get_tertiaryContainer() +onTertiaryContainer = colorscheme.get_onTertiaryContainer() +error = colorscheme.get_error() +onError = colorscheme.get_onError() +errorContainer = colorscheme.get_errorContainer() +onErrorContainer = colorscheme.get_onErrorContainer() +background = colorscheme.get_background() +onBackground = colorscheme.get_onBackground() +surface = colorscheme.get_surface() +onSurface = colorscheme.get_onSurface() +surfaceVariant = colorscheme.get_surfaceVariant() +onSurfaceVariant = colorscheme.get_onSurfaceVariant() +outline = colorscheme.get_outline() +shadow = colorscheme.get_shadow() +inverseSurface = colorscheme.get_inverseSurface() +inverseOnSurface = colorscheme.get_inverseOnSurface() +inversePrimary = colorscheme.get_inversePrimary() + + +print('$primary: ' + hexFromArgb(primary) + ';') +print('$onPrimary: ' + hexFromArgb(onPrimary) + ';') +print('$primaryContainer: ' + hexFromArgb(primaryContainer) + ';') +print('$onPrimaryContainer: ' + hexFromArgb(onPrimaryContainer) + ';') +print('$secondary: ' + hexFromArgb(secondary) + ';') +print('$onSecondary: ' + hexFromArgb(onSecondary) + ';') +print('$secondaryContainer: ' + hexFromArgb(secondaryContainer) + ';') +print('$onSecondaryContainer: ' + hexFromArgb(onSecondaryContainer) + ';') +print('$tertiary: ' + hexFromArgb(tertiary) + ';') +print('$onTertiary: ' + hexFromArgb(onTertiary) + ';') +print('$tertiaryContainer: ' + hexFromArgb(tertiaryContainer) + ';') +print('$onTertiaryContainer: ' + hexFromArgb(onTertiaryContainer) + ';') +print('$error: ' + hexFromArgb(error) + ';') +print('$onError: ' + hexFromArgb(onError) + ';') +print('$errorContainer: ' + hexFromArgb(errorContainer) + ';') +print('$onErrorContainer: ' + hexFromArgb(onErrorContainer) + ';') +print('$colorbarbg: ' + hexFromArgb(background) + ';') +print('$background: ' + hexFromArgb(background) + ';') +print('$onBackground: ' + hexFromArgb(onBackground) + ';') +print('$surface: ' + hexFromArgb(surface) + ';') +print('$onSurface: ' + hexFromArgb(onSurface) + ';') +print('$surfaceVariant: ' + hexFromArgb(surfaceVariant) + ';') +print('$onSurfaceVariant: ' + hexFromArgb(onSurfaceVariant) + ';') +print('$outline: ' + hexFromArgb(outline) + ';') +print('$shadow: ' + hexFromArgb(shadow) + ';') +print('$inverseSurface: ' + hexFromArgb(inverseSurface) + ';') +print('$inverseOnSurface: ' + hexFromArgb(inverseOnSurface) + ';') +print('$inversePrimary: ' + hexFromArgb(inversePrimary) + ';') \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/color_generation/pywal_to_material.scss b/desktops/hyprland/ags/scripts/color_generation/pywal_to_material.scss new file mode 100755 index 0000000..ff049b2 --- /dev/null +++ b/desktops/hyprland/ags/scripts/color_generation/pywal_to_material.scss @@ -0,0 +1,57 @@ +$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; +$colorbarbg: $color0; +$background: $color0; +$onBackground: $color7; +$surface: $color0; +$onSurface: $color7; +$surfaceVariant: $color1; +$onSurfaceVariant: $color7; +$outline: $color7; +$shadow: #000000; +$inverseSurface: invert($surface); +$inverseOnSurface: invert($onSurface); +$inversePrimary: invert($primary); + +.primary { color: $primary; } +.onPrimary { color: $onPrimary; } +.primaryContainer { color: $primaryContainer; } +.onPrimaryContainer { color: $onPrimaryContainer; } +.secondary { color: $secondary; } +.onSecondary { color: $onSecondary; } +.secondaryContainer { color: $secondaryContainer; } +.onSecondaryContainer { color: $onSecondaryContainer; } +.tertiary { color: $tertiary; } +.onTertiary { color: $onTertiary; } +.tertiaryContainer { color: $tertiaryContainer; } +.onTertiaryContainer { color: $tertiaryContainer; } +.error { color: $error; } +.onError { color: $onError; } +.errorContainer { color: $errorContainer; } +.onErrorContainer { color: $onErrorContainer; } +.colorbarbg { color: $colorbarbg; } +.background { color: $background; } +.onBackground { color: $onBackground; } +.surface { color: $surface; } +.onSurface { color: $onSurface; } +.surfaceVariant { color: $surfaceVariant; } +.onSurfaceVariant { color: $onSurfaceVariant; } +.outline { color: $outline; } +.shadow { color: $shadow; } +.inverseSurface { color: $inverseSurface; } +.inverseOnSurface { color: $inverseOnSurface; } +.inversePrimary { color: $inversePrimary; } diff --git a/desktops/hyprland/ags/scripts/color_generation/switchwall.sh b/desktops/hyprland/ags/scripts/color_generation/switchwall.sh new file mode 100755 index 0000000..4590974 --- /dev/null +++ b/desktops/hyprland/ags/scripts/color_generation/switchwall.sh @@ -0,0 +1,27 @@ +#!/usr/bin/bash +# Switches sww wallpaper +# Requires: coreutils, xrandr, hyprland + +# Select and set image (hyprland) +cd "$HOME/Pictures" +imgpath=$(yad --width 1200 --height 800 --file --title='Choose wallpaper') +screensizey=$(xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2 | head -1) +cursorposx=$(hyprctl cursorpos -j | gojq '.x') +cursorposy=$(hyprctl cursorpos -j | gojq '.y') +cursorposy_inverted=$(( screensizey - cursorposy )) + +if [ "$imgpath" == '' ]; then + echo 'Aborted' + exit 0 +fi + +echo Sending "$imgpath" to swww. Cursor pos: ["$cursorposx, $cursorposy_inverted"] & +# Change swww wallpaper +swww img "$imgpath" --transition-step 100 --transition-fps 60 \ +--transition-type grow --transition-angle 30 --transition-duration 1 \ +--transition-pos "$cursorposx, $cursorposy_inverted" & + +# Generate colors for ags n stuff +"$HOME"/.config/ags/scripts/color_generation/colorgen.sh "${imgpath}" --apply +sassc "$HOME"/.config/ags/scss/main.scss "$HOME"/.config/ags/style.css +ags run-js "App.resetCss(); App.applyCss('${HOME}/.config/ags/style.css');" & diff --git a/desktops/hyprland/ags/scripts/dumptows b/desktops/hyprland/ags/scripts/dumptows new file mode 100755 index 0000000..273e968 Binary files /dev/null and b/desktops/hyprland/ags/scripts/dumptows differ diff --git a/desktops/hyprland/ags/scripts/dumptows.cpp b/desktops/hyprland/ags/scripts/dumptows.cpp new file mode 100755 index 0000000..6548af7 --- /dev/null +++ b/desktops/hyprland/ags/scripts/dumptows.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nlohmann/json.hpp" +using namespace std; +using json = nlohmann::json; + +int workspace_a, workspace_b; +string clients; +json clientjson; +vector windows_a, windows_b; +bool output = false; + +string exec(const char* cmd) { + array buffer; + string result; + unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) { + throw runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; +} + +void tryAddApp(const json& client) { + if (int(client["workspace"]["id"]) == workspace_a) + windows_a.push_back(client["address"]); + else if (int(client["workspace"]["id"]) == workspace_b) + windows_b.push_back(client["address"]); +} + +void getApps() { + // Get clients + clients = exec("hyprctl clients -j | gojq -c -M"); + clientjson = json::parse(clients); + + // Access the values + for (json client : clientjson) { + tryAddApp(client); + } +} + +void dumptoWorkspace() { + for (string address : windows_a) { + string cmd = "hyprctl dispatch movetoworkspacesilent " + + to_string(workspace_b) + ",address:" + address; + if (output) cout << cmd << '\n'; + exec(&cmd[0]); + } +} + +int main(int argc, char* argv[]) { + ios::sync_with_stdio(false); + + if (argc < 3) { + cout << "Usage: dumptows [WORKSPACE_NUMBER_1] [WORKSPACE_NUMBER_2]" + << endl; + return 0; + } + if (argc == 4 && string(argv[3]) == "--output") output = true; + + workspace_a = stoi(string(argv[1])); + workspace_b = stoi(string(argv[2])); + if (workspace_a <= 0 || workspace_b <= 0 || workspace_a == workspace_b) { + cout << "Nahhh that's stupid" << endl; + return 0; + } + + getApps(); + dumptoWorkspace(); +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/indicator.js b/desktops/hyprland/ags/scripts/indicator.js new file mode 100644 index 0000000..fcb8948 --- /dev/null +++ b/desktops/hyprland/ags/scripts/indicator.js @@ -0,0 +1,40 @@ +import { Service, Utils } from '../imports.js'; +const { exec, execAsync } = Utils; + +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); + +class IndicatorService extends Service { + static { + Service.register( + this, + { 'popup': ['double'], }, + ); + } + + _delay = 1500; + _count = 0; + + popup(value) { + this.emit('popup', value); + this._count++; + Utils.timeout(this._delay, () => { + this._count--; + + if (this._count === 0) + this.emit('popup', -1); + }); + } + + connectWidget(widget, callback) { + connect(this, widget, callback, 'popup'); + } +} + +// the singleton instance +const service = new IndicatorService(); + +// make it global for easy use with cli +globalThis['indicator'] = service; + +// export to use in other modules +export default service; \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/record-script.sh b/desktops/hyprland/ags/scripts/record-script.sh new file mode 100755 index 0000000..57f4508 --- /dev/null +++ b/desktops/hyprland/ags/scripts/record-script.sh @@ -0,0 +1,14 @@ +#!/usr/bin/bash + +cd ~/Videos || exit +if [[ "$(pidof wf-recorder)" == "" ]]; then + notify-send "Starting recording" './recording_'"$(date '+%Y_%m_%_d..%H.%M.%S')"'.mp4' -a 'record-script.sh' + if [[ "$1" == "--sound" ]]; then + wf-recorder -t -f './recording_'"$(date '+%Y_%m_%_d..%H.%M.%S')"'.mp4' --geometry "$(slurp)" --audio=alsa_output.pci-0000_08_00.6.analog-stereo.monitor + else + wf-recorder -t -f './recording_'"$(date '+%Y_%m_%_d..%H.%M.%S')"'.mp4' --geometry "$(slurp)" + fi +else + /usr/bin/kill --signal SIGINT wf-recorder + notify-send "Recording Stopped" "Stopped" -a 'record-script.sh' +fi diff --git a/desktops/hyprland/ags/scripts/scripts.js b/desktops/hyprland/ags/scripts/scripts.js new file mode 100644 index 0000000..a5449ae --- /dev/null +++ b/desktops/hyprland/ags/scripts/scripts.js @@ -0,0 +1,107 @@ +import { App, Service, Utils, Widget } from '../imports.js'; +const { exec, execAsync, CONFIG_DIR } = Utils; + +export const deflisten = function (name, command, transformer = (a) => a) { + const { Service } = ags; + const GObject = imports.gi.GObject; + + const v = GObject.registerClass( + { + GTypeName: name, + Properties: { + state: GObject.ParamSpec.string( + "state", + "State", + "Read-Write string state.", + GObject.ParamFlags.READWRITE, + "" + ), + }, + Signals: { + [`${name}-changed`]: {}, + }, + }, + class Subclass extends Service { + get state() { + return this._state || ""; + } + + set state(value) { + this._state = value; + this.emit("changed"); + } + + get proc() { + return this._proc || null; + } + + set proc(value) { + this._proc = value; + } + + start = () => { + this.proc = Utils.subprocess(command, (line) => { + this.state = transformer(line); + }); + } + + stop = () => { + this.proc.force_exit(); + this.proc = null; + } + + constructor() { + super(); + this.proc = Utils.subprocess(command, (line) => { + this.state = transformer(line); + }); + } + } + ); + + class State { + static { + globalThis[name] = this; + } + + static instance = new v(); + + static get state() { + return State.instance.state; + } + + static set state(value) { + State.instance.state = value; + } + + static start() { + State.instance.start(); + } + static stop() { + State.instance.stop(); + } + } + + return State; +} + +export async function switchWall() { + try { + path = exec(`bash -c 'cd ~/Pictures && yad --width 1200 --height 800 --file --title="Choose wallpaper"'`); + screensizey = JSON.parse(exec(`hyprctl monitors -j`))[0]['height']; + cursorposx = exec(`bash -c 'hyprctl cursorpos -j | gojq ".x"'`); + cursorposy = exec(`bash -c 'hyprctl cursorpos -j | gojq ".y"'`); + cursorposy_inverted = screensizey - cursorposy; + // print all those + if (path == '') { + print('Switch wallpaper: Aborted'); + return; + } + print(`Sending ${path} to swww. Cursor pos: [${cursorposx}, ${cursorposy_inverted}]`); + exec(`swww img ${path} --transition-step 230 --transition-fps 60 --transition-type grow --transition-angle 30 --transition-duration 1 --transition-pos "${cursorposx}, ${cursorposy_inverted}"`); + exec(CONFIG_DIR + `/scripts/colorgen.sh ${path} --apply`); + imports.scss.scss.setupScss(); + } catch (error) { + print(error); + } +} diff --git a/desktops/hyprland/ags/scripts/swapws b/desktops/hyprland/ags/scripts/swapws new file mode 100755 index 0000000..b0dc8d3 Binary files /dev/null and b/desktops/hyprland/ags/scripts/swapws differ diff --git a/desktops/hyprland/ags/scripts/swapws.cpp b/desktops/hyprland/ags/scripts/swapws.cpp new file mode 100755 index 0000000..ead7b34 --- /dev/null +++ b/desktops/hyprland/ags/scripts/swapws.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nlohmann/json.hpp" +using namespace std; +using json = nlohmann::json; + +int workspace_a, workspace_b; +string clients; +json clientjson; +vector windows_a, windows_b; +bool output = false; + +string exec(const char* cmd) { + array buffer; + string result; + unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) { + throw runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; +} + +void tryAddApp(const json& client) { + if (int(client["workspace"]["id"]) == workspace_a) + windows_a.push_back(client["address"]); + else if (int(client["workspace"]["id"]) == workspace_b) + windows_b.push_back(client["address"]); +} + +void getApps() { + // Get clients + clients = exec("hyprctl clients -j | gojq -c -M"); + clientjson = json::parse(clients); + + // Access the values + for (json client : clientjson) { + tryAddApp(client); + } +} + +void swapWorkspaces() { + for (string address : windows_a) { + string cmd = "hyprctl dispatch movetoworkspacesilent " + + to_string(workspace_b) + ",address:" + address; + if (output) cout << cmd << '\n'; + exec(&cmd[0]); + } + for (string address : windows_b) { + string cmd = "hyprctl dispatch movetoworkspacesilent " + + to_string(workspace_a) + ",address:" + address; + if (output) cout << cmd << '\n'; + exec(&cmd[0]); + } +} + +int main(int argc, char* argv[]) { + ios::sync_with_stdio(false); + + if (argc < 3) { + cout << "Usage: swapws [WORKSPACE_NUMBER_1] [WORKSPACE_NUMBER_2]" + << endl; + return 0; + } + if (argc == 4 && string(argv[3]) == "--output") output = true; + + workspace_a = stoi(string(argv[1])); + workspace_b = stoi(string(argv[2])); + if (workspace_a <= 0 || workspace_b <= 0 || workspace_a == workspace_b) { + cout << "Nahhh that's stupid" << endl; + return 0; + } + + getApps(); + swapWorkspaces(); +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/templates/foot/foot.ini b/desktops/hyprland/ags/scripts/templates/foot/foot.ini new file mode 100755 index 0000000..fa4e2a8 --- /dev/null +++ b/desktops/hyprland/ags/scripts/templates/foot/foot.ini @@ -0,0 +1,156 @@ +# -*- conf -*- + +shell=fish +# term=foot (or xterm-256color if built with -Dterminfo=disabled) +term=xterm-256color +# login-shell=no + +# app-id=foot +title=foot +# locked-title=no + +font=JetBrainsMono Nerd Font:size=12 +# font-bold= +# font-italic= +# font-bold-italic= +# line-height= +letter-spacing=0 +# horizontal-letter-offset=0 +# vertical-letter-offset=0 +# underline-offset= +# box-drawings-uses-font-glyphs=no +dpi-aware=no + +# initial-window-size-pixels=700x500 # Or, +# initial-window-size-chars= +# initial-window-mode=windowed +pad=25x25 # optionally append 'center' +# resize-delay-ms=100 + +# notify=notify-send -a ${app-id} -i ${app-id} ${title} ${body} + +bold-text-in-bright=no +# word-delimiters=,│`|:"'()[]{}<> +# selection-target=primary +# workers= + +[bell] +# urgent=no +# notify=no +# command= +# command-focused=no + +[scrollback] +lines=10000 +# multiplier=3.0 +# indicator-position=relative +# indicator-format= + +[url] +# launch=xdg-open ${url} +# label-letters=sadfjklewcmpgh +# osc8-underline=url-mode +# protocols=http, https, ftp, ftps, file, gemini, gopher +# uri-characters=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,~:;/?#@!$&%*+="' + +[cursor] +style=beam +# color=111111 dcdccc +color=$background # $onBackground # +# blink=no +beam-thickness=1.5 +# underline-thickness= + +[mouse] +# hide-when-typing=no +# alternate-scroll-mode=yes + +[colors] +alpha=1 +background=$background # +foreground=$onBackground # +regular0=$background # +regular1=$error # +regular2=$inversePrimary # +regular3=$onPrimaryContainer # +regular4=$onPrimaryContainer # +regular5=$onSecondaryContainer # +regular6=$primary # +regular7=$onSurfaceVariant # +bright0=$background # +bright1=$error # +bright2=$inversePrimary # +bright3=$onPrimaryContainer # +bright4=$onPrimaryContainer # +bright5=$onSecondaryContainer # +bright6=$primary # +bright7=$onSurfaceVariant # + +[csd] +# preferred=server +# size=26 +# font= +# color= +# button-width=26 +# button-color= +# button-minimize-color= +# button-maximize-color= +# button-close-color= + +[key-bindings] +scrollback-up-page=Page_Up +# scrollback-up-half-page=none +# scrollback-up-line=none +scrollback-down-page=Page_Down +# scrollback-down-half-page=none +# scrollback-down-line=none +clipboard-copy=Control+c +clipboard-paste=Control+v +# primary-paste=Shift+Insert +search-start=Control+f +# font-increase=Control+plus Control+equal Control+KP_Add +# font-decrease=Control+minus Control+KP_Subtract +# font-reset=Control+0 Control+KP_0 +# spawn-terminal=Control+Shift+n +# minimize=none +# maximize=none +# fullscreen=none +# pipe-visible=[sh -c "xurls | fuzzel | xargs -r firefox"] none +# pipe-scrollback=[sh -c "xurls | fuzzel | xargs -r firefox"] none +# pipe-selected=[xargs -r firefox] none +# show-urls-launch=Control+Shift+u +# show-urls-copy=none + +[search-bindings] +# cancel=Control+g Control+c Escape +# commit=Return +# find-prev=Control+r +# find-next=Control+s +# cursor-left=Left Control+b +# cursor-left-word=Control+Left Mod1+b +# cursor-right=Right Control+f +# cursor-right-word=Control+Right Mod1+f +# cursor-home=Home Control+a +# cursor-end=End Control+e +# delete-prev=BackSpace +# delete-prev-word=Control+BackSpace +# delete-next=Delete +# delete-next-word=Mod1+d Control+Delete +# extend-to-word-boundary=Control+w +# extend-to-next-whitespace=Control+Shift+w +# clipboard-paste=Control+v Control+y +# primary-paste=Shift+Insert + +[url-bindings] +# cancel=Control+g Control+c Control+d Escape +# toggle-url-visible=t + +[mouse-bindings] +# primary-paste=BTN_MIDDLE +# select-begin=BTN_LEFT +# select-begin-block=Control+BTN_LEFT +# select-extend=BTN_RIGHT +# select-extend-character-wise=Control+BTN_RIGHT +# select-word=BTN_LEFT-2 +# select-word-whitespace=Control+BTN_LEFT-2 +# select-row=BTN_LEFT-3 diff --git a/desktops/hyprland/ags/scripts/templates/fuzzel/fuzzel.ini b/desktops/hyprland/ags/scripts/templates/fuzzel/fuzzel.ini new file mode 100755 index 0000000..9edf143 --- /dev/null +++ b/desktops/hyprland/ags/scripts/templates/fuzzel/fuzzel.ini @@ -0,0 +1,21 @@ +font=Lexend +terminal=foot -e +prompt=">> " +layer=overlay + +[colors] +background=$backgroundcc +text=$onBackgroundff +selection=$surfaceVariantff +selection-text=$onSurfaceVariantff +border=$surfaceVariantff +match=$primaryff +selection-match=$primaryff + + +[border] +radius=17 +width=2 + +[dmenu] +exit-immediately-if-empty=yes diff --git a/desktops/hyprland/ags/scripts/templates/gradience/preset.json b/desktops/hyprland/ags/scripts/templates/gradience/preset.json new file mode 100644 index 0000000..c24826a --- /dev/null +++ b/desktops/hyprland/ags/scripts/templates/gradience/preset.json @@ -0,0 +1,139 @@ +{ + "name": "Material-blue-light", + "variables": { + "theme_fg_color": "#AEE5FA", + "theme_text_color": "#AEE5FA", + "theme_bg_color": "#1a1b26", + "theme_base_color": "#1a1b26", + "theme_selected_bg_color": "#AEE5FA", + "theme_selected_fg_color": "rgba(0, 0, 0, 0.87)", + "insensitive_bg_color": "#1a1b26", + "insensitive_fg_color": "rgba(192, 202, 245, 0.5)", + "insensitive_base_color": "#24283b", + "theme_unfocused_fg_color": "#AEE5FA", + "theme_unfocused_text_color": "#c0caf5", + "theme_unfocused_bg_color": "#1a1b26", + "theme_unfocused_base_color": "#1a1b26", + "theme_unfocused_selected_bg_color": "#a9b1d6", + "theme_unfocused_selected_fg_color": "rgba(0, 0, 0, 0.87)", + "unfocused_insensitive_color": "rgba(192, 202, 245, 0.5)", + "borders": "rgba(192, 202, 245, 0.12)", + "unfocused_borders": "rgba(192, 202, 245, 0.12)", + "warning_color": "#FDD633", + "error_color": "#BA1B1B", + "success_color": "#81C995", + "wm_title": "#AEE5FA", + "wm_unfocused_title": "rgba(192, 202, 245, 0.7)", + "wm_highlight": "rgba(192, 202, 245, 0.1)", + "wm_bg": "#1a1b26", + "wm_unfocused_bg": "#1a1b26", + "wm_button_close_icon": "#1a1b26", + "wm_button_close_hover_bg": "#a9b1d6", + "wm_button_close_active_bg": "#c7c7c7", + "content_view_bg": "#1a1b26", + "placeholder_text_color": "silver", + "text_view_bg": "#1d1d1d", + "budgie_tasklist_indicator_color": "#90D1F6", + "budgie_tasklist_indicator_color_active": "#90D1F6", + "budgie_tasklist_indicator_color_active_window": "#999999", + "budgie_tasklist_indicator_color_attention": "#FDD633", + "STRAWBERRY_100": "#FF9262", + "STRAWBERRY_300": "#FF793E", + "STRAWBERRY_500": "#F15D22", + "STRAWBERRY_700": "#CF3B00", + "STRAWBERRY_900": "#AC1800", + "ORANGE_100": "#FFDB91", + "ORANGE_300": "#FFCA40", + "ORANGE_500": "#FAA41A", + "ORANGE_700": "#DE8800", + "ORANGE_900": "#C26C00", + "BANANA_100": "#FFFFA8", + "BANANA_300": "#FFFA7D", + "BANANA_500": "#FFCE51", + "BANANA_700": "#D1A023", + "BANANA_900": "#A27100", + "LIME_100": "#A2F3BE", + "LIME_300": "#8ADBA6", + "LIME_500": "#73C48F", + "LIME_700": "#479863", + "LIME_900": "#1C6D38", + "BLUEBERRY_100": "#94A6FF", + "BLUEBERRY_300": "#6A7CE0", + "BLUEBERRY_500": "#3F51B5", + "BLUEBERRY_700": "#213397", + "BLUEBERRY_900": "#031579", + "GRAPE_100": "#D25DE6", + "GRAPE_300": "#B84ACB", + "GRAPE_500": "#9C27B0", + "GRAPE_700": "#830E97", + "GRAPE_900": "#6A007E", + "COCOA_100": "#9F9792", + "COCOA_300": "#7B736E", + "COCOA_500": "#574F4A", + "COCOA_700": "#463E39", + "COCOA_900": "#342C27", + "SILVER_100": "#EEE", + "SILVER_300": "#CCC", + "SILVER_500": "#AAA", + "SILVER_700": "#888", + "SILVER_900": "#666", + "SLATE_100": "#888", + "SLATE_300": "#666", + "SLATE_500": "#444", + "SLATE_700": "#222", + "SLATE_900": "#111", + "BLACK_100": "#474341", + "BLACK_300": "#403C3A", + "BLACK_500": "#393634", + "BLACK_700": "#33302F", + "BLACK_900": "#2B2928", + "accent_bg_color": "#006874", + "accent_fg_color": "#ffffff", + "accent_color": "#006874", + "destructive_bg_color": "#ba1b1b", + "destructive_fg_color": "#ffffff", + "destructive_color": "#ba1b1b", + "success_bg_color": "#81C995", + "success_fg_color": "rgba(0, 0, 0, 0.87)", + "warning_bg_color": "#FDD633", + "warning_fg_color": "rgba(0, 0, 0, 0.87)", + "error_bg_color": "#ba1b1b", + "error_fg_color": "#ffffff", + "window_bg_color": "rgba(251, 253, 253, 0.90)", + "window_fg_color": "#191c1d", + "view_bg_color": "#fbfdfd", + "view_fg_color": "#191c1d", + "headerbar_bg_color": "mix(@dialog_bg_color, @window_bg_color, 0.5)", + "headerbar_fg_color": "#051f23", + "headerbar_border_color": "rgba(205, 231, 236, 0.12)", + "headerbar_backdrop_color": "@headerbar_bg_color", + "headerbar_shade_color": "rgba(0, 0, 0, 0.09)", + "card_bg_color": "rgba(251, 253, 253, 0.20)", + "card_fg_color": "#051f23", + "card_shade_color": "rgba(0, 0, 0, 0.09)", + "dialog_bg_color": "#cde7ec", + "dialog_fg_color": "#051f23", + "popover_bg_color": "#cde7ec", + "popover_fg_color": "#051f23", + "thumbnail_bg_color": "#1a1b26", + "thumbnail_fg_color": "#AEE5FA", + "shade_color": "rgba(0, 0, 0, 0.36)", + "scrollbar_outline_color": "rgba(0, 0, 0, 0.5)" + }, + "palette": { + "blue_": {}, + "green_": {}, + "yellow_": {}, + "orange_": {}, + "red_": {}, + "purple_": {}, + "brown_": {}, + "light_": {}, + "dark_": {} + }, + "custom_css": { + "gtk4": "", + "gtk3": "" + }, + "plugins": {} +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/templates/gradience/preset_template.json b/desktops/hyprland/ags/scripts/templates/gradience/preset_template.json new file mode 100644 index 0000000..8e2bf9c --- /dev/null +++ b/desktops/hyprland/ags/scripts/templates/gradience/preset_template.json @@ -0,0 +1,139 @@ +{ + "name": "Material-blue-light", + "variables": { + "theme_fg_color": "#AEE5FA", + "theme_text_color": "#AEE5FA", + "theme_bg_color": "#1a1b26", + "theme_base_color": "#1a1b26", + "theme_selected_bg_color": "#AEE5FA", + "theme_selected_fg_color": "rgba(0, 0, 0, 0.87)", + "insensitive_bg_color": "#1a1b26", + "insensitive_fg_color": "rgba(192, 202, 245, 0.5)", + "insensitive_base_color": "#24283b", + "theme_unfocused_fg_color": "#AEE5FA", + "theme_unfocused_text_color": "#c0caf5", + "theme_unfocused_bg_color": "#1a1b26", + "theme_unfocused_base_color": "#1a1b26", + "theme_unfocused_selected_bg_color": "#a9b1d6", + "theme_unfocused_selected_fg_color": "rgba(0, 0, 0, 0.87)", + "unfocused_insensitive_color": "rgba(192, 202, 245, 0.5)", + "borders": "rgba(192, 202, 245, 0.12)", + "unfocused_borders": "rgba(192, 202, 245, 0.12)", + "warning_color": "#FDD633", + "error_color": "#BA1B1B", + "success_color": "#81C995", + "wm_title": "#AEE5FA", + "wm_unfocused_title": "rgba(192, 202, 245, 0.7)", + "wm_highlight": "rgba(192, 202, 245, 0.1)", + "wm_bg": "#1a1b26", + "wm_unfocused_bg": "#1a1b26", + "wm_button_close_icon": "#1a1b26", + "wm_button_close_hover_bg": "#a9b1d6", + "wm_button_close_active_bg": "#c7c7c7", + "content_view_bg": "#1a1b26", + "placeholder_text_color": "silver", + "text_view_bg": "#1d1d1d", + "budgie_tasklist_indicator_color": "#90D1F6", + "budgie_tasklist_indicator_color_active": "#90D1F6", + "budgie_tasklist_indicator_color_active_window": "#999999", + "budgie_tasklist_indicator_color_attention": "#FDD633", + "STRAWBERRY_100": "#FF9262", + "STRAWBERRY_300": "#FF793E", + "STRAWBERRY_500": "#F15D22", + "STRAWBERRY_700": "#CF3B00", + "STRAWBERRY_900": "#AC1800", + "ORANGE_100": "#FFDB91", + "ORANGE_300": "#FFCA40", + "ORANGE_500": "#FAA41A", + "ORANGE_700": "#DE8800", + "ORANGE_900": "#C26C00", + "BANANA_100": "#FFFFA8", + "BANANA_300": "#FFFA7D", + "BANANA_500": "#FFCE51", + "BANANA_700": "#D1A023", + "BANANA_900": "#A27100", + "LIME_100": "#A2F3BE", + "LIME_300": "#8ADBA6", + "LIME_500": "#73C48F", + "LIME_700": "#479863", + "LIME_900": "#1C6D38", + "BLUEBERRY_100": "#94A6FF", + "BLUEBERRY_300": "#6A7CE0", + "BLUEBERRY_500": "#3F51B5", + "BLUEBERRY_700": "#213397", + "BLUEBERRY_900": "#031579", + "GRAPE_100": "#D25DE6", + "GRAPE_300": "#B84ACB", + "GRAPE_500": "#9C27B0", + "GRAPE_700": "#830E97", + "GRAPE_900": "#6A007E", + "COCOA_100": "#9F9792", + "COCOA_300": "#7B736E", + "COCOA_500": "#574F4A", + "COCOA_700": "#463E39", + "COCOA_900": "#342C27", + "SILVER_100": "#EEE", + "SILVER_300": "#CCC", + "SILVER_500": "#AAA", + "SILVER_700": "#888", + "SILVER_900": "#666", + "SLATE_100": "#888", + "SLATE_300": "#666", + "SLATE_500": "#444", + "SLATE_700": "#222", + "SLATE_900": "#111", + "BLACK_100": "#474341", + "BLACK_300": "#403C3A", + "BLACK_500": "#393634", + "BLACK_700": "#33302F", + "BLACK_900": "#2B2928", + "accent_bg_color": "$primary", + "accent_fg_color": "$onPrimary", + "accent_color": "$primary", + "destructive_bg_color": "$error", + "destructive_fg_color": "$onError", + "destructive_color": "$error", + "success_bg_color": "#81C995", + "success_fg_color": "rgba(0, 0, 0, 0.87)", + "warning_bg_color": "#FDD633", + "warning_fg_color": "rgba(0, 0, 0, 0.87)", + "error_bg_color": "$error", + "error_fg_color": "$onError", + "window_bg_color": "$windowBgColor", + "window_fg_color": "$onBackground", + "view_bg_color": "$surface", + "view_fg_color": "$onSurface", + "headerbar_bg_color": "mix(@dialog_bg_color, @window_bg_color, 0.5)", + "headerbar_fg_color": "$onSecondaryContainer", + "headerbar_border_color": "$headerbarBorderColor", + "headerbar_backdrop_color": "@headerbar_bg_color", + "headerbar_shade_color": "rgba(0, 0, 0, 0.09)", + "card_bg_color": "$cardBgColor", + "card_fg_color": "$onSecondaryContainer", + "card_shade_color": "rgba(0, 0, 0, 0.09)", + "dialog_bg_color": "$secondaryContainer", + "dialog_fg_color": "$onSecondaryContainer", + "popover_bg_color": "$secondaryContainer", + "popover_fg_color": "$onSecondaryContainer", + "thumbnail_bg_color": "#1a1b26", + "thumbnail_fg_color": "#AEE5FA", + "shade_color": "rgba(0, 0, 0, 0.36)", + "scrollbar_outline_color": "rgba(0, 0, 0, 0.5)" + }, + "palette": { + "blue_": {}, + "green_": {}, + "yellow_": {}, + "orange_": {}, + "red_": {}, + "purple_": {}, + "brown_": {}, + "light_": {}, + "dark_": {} + }, + "custom_css": { + "gtk4": "", + "gtk3": "" + }, + "plugins": {} +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/templates/gtklock/main.scss b/desktops/hyprland/ags/scripts/templates/gtklock/main.scss new file mode 100755 index 0000000..00a279a --- /dev/null +++ b/desktops/hyprland/ags/scripts/templates/gtklock/main.scss @@ -0,0 +1,86 @@ +// Could just sed but scss is way less hacky +@import '../../../scss/_material.scss'; // Which is ~/.config/ags/scss/_material.scss + +* { + all: unset; + border: 0rem; +} + +window { + background-color: transparentize($background, 0.9); + background-size: cover; + background-repeat: no-repeat; + background-position: center; +} + +#window-box { + border-radius: 1.5rem; + padding: 1.5rem; +} + +#input-label { + font-size: 1.5rem; + color: transparent; + background-color: transparent; + margin: -20rem; // bye bye +} + +#input-field { + background-color: $secondaryContainer; + color: $onSecondaryContainer; + caret-color: $onSecondaryContainer; + border-radius: 999px; + font-size: 1.3rem; + padding: 0.341rem 1.364rem; + margin: 0.477rem; + box-shadow: 2px 2px 4px rgba(22, 22, 22, 0.5); + min-height: 2.727rem; +} + +#unlock-button { + margin: -20rem; // bye bye + color: transparent; + background-color: transparent; +} + +#error-label { + color: $error; +} + +#clock-label { + font-family: 'Lexend'; + font-size: 6rem; + border-radius: 1.2rem; + padding: 0.5rem; + margin: 0.6rem; + margin-top: -35rem; // higher clock position + color: $onSecondaryContainer; + text-shadow: 1px 1px 2px rgba(22, 22, 30, 0.5); +} + +// #user-image {} + +// #powerbar-box {} + +#poweroff-button, +#reboot-button,, +#suspend-button { + background-color: $secondaryContainer; + color: $onSecondaryContainer; + min-width: 3rem; + min-height: 3rem; + margin: 0.341rem; + border-radius: 9999px; +} + +#poweroff-button:hover, +#reboot-button:hover, +#suspend-button:hover { + background-color: mix($secondaryContainer, white, 80%); +} + +#poweroff-button:active, +#reboot-button:active, +#suspend-button:active { + background-color: mix($secondaryContainer, white, 70%); +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/templates/gtklock/style.css b/desktops/hyprland/ags/scripts/templates/gtklock/style.css new file mode 100755 index 0000000..995d316 --- /dev/null +++ b/desktops/hyprland/ags/scripts/templates/gtklock/style.css @@ -0,0 +1,102 @@ +/* Gtklock css */ + +* { + all: unset; + border: 0px; +} + +window { + background: rgba(0, 0, 0, 0.5); + background-size: cover; + background-repeat: no-repeat; + background-position: center; +} + +#window-box { + border-radius: 1.5rem; + padding: 1.5rem; + border: 0px solid black; +} + +#input-label { + font-size: 1.5rem; + color: transparent; + background-color: transparent; + margin: -20rem; +} + +#input-field { + background-color: $secondaryContainer; + color: $onSecondaryContainer; + caret-color: $onSecondaryContainer; + border-radius: 999px; + font-size: 1.3rem; + padding: 0.341rem 1.364rem; + margin: 0.477rem; + box-shadow: 2px 2px 4px rgba(22, 22, 22, 0.5); + min-height: 2.727rem; +} + +#unlock-button { + margin: -20rem; + color: transparent; + background-color: transparent; +} + +#error-label { + color: $error; +} + +#clock-label { + font-family: 'Lexend'; + font-size: 6rem; + border-radius: 1.2rem; + padding: 0.5rem; + margin: 0.6rem; + margin-top: -35rem; + color: $onSecondaryContainer; + text-shadow: 1px 1px 2px rgba(22, 22, 30, 0.5); +} + +#user-image {} + +#powerbar-box {} + +#poweroff-button { + background-color: $secondaryContainer; + color: $onSecondaryContainer; + min-width: 3rem; + min-height: 3rem; + margin: 10px; + border-radius: 99px; +} + +#suspend-button { + background-color: $secondaryContainer; + color: $onSecondaryContainer; + min-width: 3rem; + min-height: 3rem; + margin: 10px; + border-radius: 99px; +} + +#reboot-button { + background-color: $secondaryContainer; + color: $onSecondaryContainer; + min-width: 3rem; + min-height: 3rem; + margin: 10px; + border-radius: 99px; +} + +#poweroff-button:hover, +#reboot-button:hover, +#suspend-button:hover { + background: rgba(200, 200, 200, 0.3); +} + +#poweroff-button:active, +#reboot-button:active, +#suspend-button:active { + background: rgba(200, 200, 200, 0.5); +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/templates/hypr/colors.conf b/desktops/hyprland/ags/scripts/templates/hypr/colors.conf new file mode 100755 index 0000000..58d55d4 --- /dev/null +++ b/desktops/hyprland/ags/scripts/templates/hypr/colors.conf @@ -0,0 +1,5 @@ +# Auto generated color theme for image at: [Local wallpaper] +general { + col.active_border = rgba($primaryAA) 45deg + col.inactive_border = rgba(555555FF) +} diff --git a/desktops/hyprland/ags/scripts/todo.js b/desktops/hyprland/ags/scripts/todo.js new file mode 100644 index 0000000..5fd332b --- /dev/null +++ b/desktops/hyprland/ags/scripts/todo.js @@ -0,0 +1,86 @@ +const { Gio, Gdk, Gtk } = imports.gi; +import { Service, Utils } from '../imports.js'; +const { exec, execAsync } = Utils; + +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); +function fileExists(filePath) { + let file = Gio.File.new_for_path(filePath); + return file.query_exists(null); +} + +class TodoService extends Service { + static { + Service.register( + this, + { 'updated': [], }, + ); + } + + _todoPath = ''; + _todoJson = []; + + refresh(value) { + this.emit('updated', value); + } + + connectWidget(widget, callback) { + this.connect(widget, callback, 'updated'); + } + + get todo_json() { + return this._todoJson; + } + + add(content) { + this._todoJson.push({ content, done: false }); + Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath) + .catch(print); + this.emit('updated'); + } + + check(index) { + this._todoJson[index].done = true; + Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath) + .catch(print); + this.emit('updated'); + } + + uncheck(index) { + this._todoJson[index].done = false; + Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath) + .catch(print); + this.emit('updated'); + } + + remove(index) { + this._todoJson.splice(index, 1); + Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath) + .catch(print); + this.emit('updated'); + } + + constructor() { + super(); + this._todoPath = `${App.configDir}/../../.cache/ags/user/todo.json`; + if (!fileExists(this._todoPath)) { // No? create file with empty array + Utils.exec(`bash -c 'mkdir -p ~/.cache/ags/user'`); + Utils.exec(`touch ${this._todoPath}`); + Utils.writeFile("[]", this._todoPath).then(() => { + this._todoJson = JSON.parse(Utils.readFile(this._todoPath)) + }).catch(print); + } + else { + const fileContents = Utils.readFile(this._todoPath); + this._todoJson = JSON.parse(fileContents); + } + } +} + +// the singleton instance +const service = new TodoService(); + +// make it global for easy use with cli +globalThis.todo = service; + +// export to use in other modules +export default service; \ No newline at end of file diff --git a/desktops/hyprland/ags/scripts/utils/find_firefox_profile.sh b/desktops/hyprland/ags/scripts/utils/find_firefox_profile.sh new file mode 100755 index 0000000..7b305b7 --- /dev/null +++ b/desktops/hyprland/ags/scripts/utils/find_firefox_profile.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd ~/.mozilla/firefox/ +if [[ $(grep '\[Profile[^0]\]' profiles.ini) ]] +then PROFPATH=$(grep -E '^\[Profile|^Path|^Default' profiles.ini | grep -1 '^Default=1' | grep '^Path' | cut -c6-) +else PROFPATH=$(grep 'Path=' profiles.ini | sed 's/^Path=//') +fi + +echo "$HOME/.mozilla/firefox/$PROFPATH" diff --git a/desktops/hyprland/ags/scss/_bar.scss b/desktops/hyprland/ags/scss/_bar.scss new file mode 100644 index 0000000..17aff34 --- /dev/null +++ b/desktops/hyprland/ags/scss/_bar.scss @@ -0,0 +1,471 @@ +// Made to be pixel-perfect with 11pt font size +// 1rem = 11pt = 14.6666666667px + +// Init +$black: black; +$white: white; +$notchSecondaryContainer: $secondaryContainer; +$notchOnSecondaryContainer: $onSecondaryContainer; +$notchPrimary: $primary; +$notchOnPrimary: $onPrimary; + +// Check dark mode. Set colors accordingly for the fake snotch that's always black +@if $darkmode ==true { + $notchSecondaryContainer: $secondaryContainer; + $notchOnSecondaryContainer: $onSecondaryContainer; + $notchPrimary: $primary; + $notchOnPrimary: $onPrimary; +} + +@else { + $notchSecondaryContainer: $onSecondaryContainer; + $notchOnSecondaryContainer: $secondaryContainer; + $notchPrimary: $primaryContainer; + $notchOnPrimary: $onPrimaryContainer; +} + +.bar-bg { + background-color: $t_background; + min-height: 2.727rem; +} + +.bar-sidespace { + min-width: 1.5rem; +} + +.bar-group-margin { + padding: 0.2rem; +} + +.bar-group { + // @include elevation-border; + background-color: $t_surface; +} + +.bar-group-center { + border-bottom-left-radius: 1.364rem; + border-bottom-right-radius: 1.364rem; + padding: 0.2rem; + // background-color: $t_surface; + background-color: $black; // Hard code: fake notch +} + +.corner-bar-group { + border-radius: 1.364rem; // Half of bar height + border-width: 0.068rem; + // background-color: $t_surface; + background-color: $black; // Hard code: fake notch +} + +.bar-group-pad { + padding: 0rem 1.023rem; +} + +.bar-group-pad-less { + padding: 0rem 0.681rem; +} + +.bar-group-pad-system { + padding-left: 1.023rem; + padding-right: 0.547rem; +} + +.bar-group-pad-music { + padding-right: 1.023rem; + // padding-left: 0.273rem; +} + +.bar-group-pad-left { + padding-left: 1.364rem; + padding-right: 0.681rem; +} + +.bar-group-pad-right { + padding-left: 0.681rem; + padding-right: 1.364rem; +} + +.bar-group-pad-leftonly { + padding-left: 0.681rem; +} + +.bar-group-pad-rightonly { + padding-right: 0.681rem; +} + +.bar-group-standalone { + border-radius: 1.364rem; + -gtk-outline-radius: 1.364rem; +} + +.bar-group-round { + border-radius: 10rem; + -gtk-outline-radius: 10rem; +} + +.bar-group-middle { + border-radius: 0.477rem; + -gtk-outline-radius: 0.477rem; +} + +.bar-group-left { + border-radius: 0.477rem; + -gtk-outline-radius: 0.477rem; + border-top-left-radius: 1.364rem; + border-bottom-left-radius: 1.364rem; +} + +.bar-group-right { + border-radius: 0.477rem; + -gtk-outline-radius: 0.477rem; + border-top-right-radius: 1.364rem; + border-bottom-right-radius: 1.364rem; +} + +.bar-ws-width { + min-width: 18.614rem; +} + +.bar-separator { + @include full-rounding; + min-width: 0.341rem; + min-height: 0.341rem; + background-color: mix($t_surface, $t_onSurface, 90%); + margin: 0rem 0.341rem; +} + +.bar-clock { + @include titlefont; + font-size: 1.2727rem; + color: $onBackground; +} + +.bar-date { + @include titlefont; + font-size: 1rem; + color: $onBackground; +} + +.bar-ws { + min-height: 1.636rem; + min-width: 1.772rem; + font-size: 1.091rem; + @include mainfont; + border-top: 0.068rem solid; + border-bottom: 0.068rem solid; + border-color: transparent; + color: $white; +} + +.bar-ws-active-box { + transition: 300ms cubic-bezier(0.05, 0.7, 0.1, 1); +} + +.bar-ws-active { + min-height: 1.5rem; + min-width: 1.5rem; + font-size: 1.091rem; + @include mainfont; + + background-color: $notchPrimary; + color: $notchOnPrimary; + border-radius: 999px; + margin: 0.068rem; + // background-color: red; +} + +.bar-ws-active-middledecor { + min-width: 0.682rem; + min-height: 0.682rem; + border-radius: 9999px; + background-color: $black; + margin: 0rem 0.409rem; +} + +.bar-ws-occupied { + background-color: $notchSecondaryContainer; + color: $notchOnSecondaryContainer; + min-width: 1.772rem; + border-top: 0.068rem solid $notchOnSecondaryContainer; + border-bottom: 0.068rem solid $notchOnSecondaryContainer; +} + +.bar-ws-occupied-left { + background-color: $notchSecondaryContainer; + color: $notchOnSecondaryContainer; + min-width: 1.704rem; + border-top-left-radius: 999px; + border-bottom-left-radius: 999px; + + border-left: 0.068rem solid $notchOnSecondaryContainer; + border-top: 0.068rem solid $notchOnSecondaryContainer; + border-bottom: 0.068rem solid $notchOnSecondaryContainer; + border-right: 0px solid transparent; +} + +.bar-ws-occupied-right { + background-color: $notchSecondaryContainer; + color: $notchOnSecondaryContainer; + min-width: 1.704rem; + border-top-right-radius: 999px; + border-bottom-right-radius: 999px; + + border-right: 0.068rem solid $notchOnSecondaryContainer; + border-top: 0.068rem solid $notchOnSecondaryContainer; + border-bottom: 0.068rem solid $notchOnSecondaryContainer; + border-left: 0px solid transparent; +} + +.bar-ws-occupied-left-right { + @include full-rounding; + background-color: $notchSecondaryContainer; + color: $notchOnSecondaryContainer; + min-width: 1.636rem; + border: 0.068rem solid $notchOnSecondaryContainer; +} + +.bar-ws-empty { + color: $onBackground; + border-color: transparent; +} + +.bar-batt { + @include full-rounding; + padding: 0rem 0.341rem; + background-color: $t_secondaryContainer; + color: $t_onSecondaryContainer; + // border: 1px solid $onSecondaryContainer; +} + +.bar-sidemodule { + min-width: 26rem; +} + +.bar-batt-low { + background-color: $error; + color: $errorContainer; +} + +.bar-batt-full { + background-color: $successContainer; + color: $onSuccessContainer; +} + +.bar-batt-prog-low { + background-color: $error; + color: $errorContainer; +} + +.bar-batt-prog-full { + background-color: $successContainer; + color: $onSuccessContainer; +} + +.bar-music-playstate { + min-height: 1.770rem; + min-width: 1.770rem; + border-radius: 10rem; + margin-left: 0.273rem; + background-color: $secondaryContainer; + color: $onSecondaryContainer; +} + +.bar-music-circprog { + @include fluent_decel_long; + margin-left: 0.273rem; + min-width: 0.068rem; // 1px + min-height: 1.770rem; + padding: 0rem; + background-color: $secondaryContainer; + color: $onSecondaryContainer; +} + +.bar-music-playstate-playing { + min-height: 1.770rem; + min-width: 1.770rem; + border-radius: 10rem; + margin-left: 0.273rem; + background-color: $secondaryContainer; + color: $onSecondaryContainer; + // border: 1px solid $onSecondaryContainer; +} + +.bar-music-playstate-txt { + transition: 100ms cubic-bezier(0.05, 0.7, 0.1, 1); + @include icon-material; + font-size: 1.568rem; + margin: -0.1rem 0rem; + margin-left: 0.2rem; + margin-right: 0.17rem; +} + +.bar-music-cover { + background-position: center; + background-repeat: no-repeat; + background-size: 100% auto; + min-width: 11.932rem; +} + +.bar-music-extended-bg { + border-radius: 1.364rem; + min-width: 34.091rem; +} + +.bar-music-extended-ctl-bg { + border-radius: 1.364rem; + background-color: rgba(30, 30, 30, 0.6); +} + +.bar-music-bottom-bg { + border-radius: 1.364rem; + min-width: 34.091rem; +} + +.bar-music-bottom-ctl-bg { + border-radius: 1.364rem; + background-color: rgba(30, 30, 30, 0.6); +} + +.bar-music-extended-textbox { + margin: 1.023rem; +} + +.bar-music-bottom-cover { + border-radius: 10rem; +} + +.bar-music-hide-false { + @include md3_decel; + transition-duration: 100ms; + opacity: 1; +} + +.bar-music-hide-true { + @include md3_accel; + transition-duration: 100ms; + opacity: 0; +} + +.bar-music-btn { + font-size: 1.364rem; + border-radius: 10rem; + min-height: 2.591rem; + min-width: 2.591rem; +} + +.bar-music-btn:hover { + background-color: $hovercolor; +} + +.bar-prog-batt { + min-height: 0.955rem; + min-width: 0.068rem; + padding: 0rem; + border-radius: 10rem; + + trough { + min-height: 0.954rem; + min-width: 0.068rem; + border-radius: 10rem; + } + + progress { + min-height: 0.680rem; + min-width: 0.680rem; + margin: 0rem 0.137rem; + border-radius: 10rem; + background-color: $t_onSecondaryContainer; + } +} + +.bar-prog-batt-low { + progress { + background-color: $errorContainer; + } +} + +.bar-prog-batt-full { + progress { + background-color: $onSuccessContainer; + } +} + +.bar-batt-chargestate { + border-radius: 10rem; + background-color: transparent; +} + +.bar-batt-chargestate-charging { + border-radius: 10rem; + min-width: 0.681rem; + min-height: 0.681rem; + background-color: $t_onSecondaryContainer; +} + +.bar-batt-chargestate-low { + background-color: $errorContainer; +} + +.bar-batt-chargestate-full { + background-color: $onSuccessContainer; +} + +.bar-batt-percentage { + font-size: 1rem; + margin-top: -0.068rem; + font-weight: 500; +} + +.corner { + background-color: $t_background; + @include large-rounding; +} + +.corner-black { + background-color: $black; // Hard code because fake screen corner + @include large-rounding; +} + +.bar-topdesc { + margin-top: -0.136rem; + margin-bottom: -0.341rem; + color: $subtext; +} + +.bar-space-button { + padding: 0.341rem; +} + +.bar-space-button>box:first-child { + @include full-rounding; + padding: 0rem 0.682rem; +} + +.bar-space-button:hover>box:first-child { + background-color: $hovercolor; +} + +.bar-space-button:active>box:first-child { + background-color: $activecolor; +} + +.bar-space-button-leftmost { + box { + margin: 0rem 0.682rem; + } +} + +.bar-space-area-rightmost>box { + padding-right: 2.386rem; +} + +.bar-systray { + @include full-rounding; + min-height: 1.909rem; + min-width: 1.909rem; +} + +.bar-systray-item { + @include full-rounding; + min-width: 1.909rem; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_cheatsheet.scss b/desktops/hyprland/ags/scss/_cheatsheet.scss new file mode 100644 index 0000000..363b386 --- /dev/null +++ b/desktops/hyprland/ags/scss/_cheatsheet.scss @@ -0,0 +1,51 @@ +.cheatsheet-bg { + @include large-rounding; + @include elevation-border; + @include elevation2; + margin-bottom: 0.682rem; + background-color: $t_background; + padding: 1.364rem; +} + +.cheatsheet-key { + @include techfont; + min-height: 1.364rem; + min-width: 1.364rem; + margin: 0.17rem; + padding: 0.136rem 0.205rem; + border-radius: 0.409rem; + -gtk-outline-radius: 0.409rem; + color: $primary; + border: 0.068rem solid $primary; + box-shadow: 0rem 0.136rem 0rem $primary; + font-weight: 500; +} + +.cheatsheet-key-notkey { + min-height: 1.364rem; + padding: 0.136rem 0.205rem; + margin: 0.17rem; + color: $onPrimaryContainer; +} + +// .cheatsheet-action {} + +.cheatsheet-closebtn { + @include md3_decel; + @include full-rounding; + min-width: 2.386rem; + min-height: 2.386rem; +} + +.cheatsheet-closebtn:hover { + background-color: $surfaceVariant; +} + +.cheatsheet-closebtn:active { + background-color: mix($surfaceVariant, $onSurfaceVariant, 70%); +} + +.cheatsheet-category-title { + @include titlefont; + font-size: 1.705rem; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_colors.scss b/desktops/hyprland/ags/scss/_colors.scss new file mode 100755 index 0000000..3128315 --- /dev/null +++ b/desktops/hyprland/ags/scss/_colors.scss @@ -0,0 +1,79 @@ +// Transparent version +$transparentize_amount: 0.3; +$transparentize_surface_amount_less: 0.6; +$transparentize_surface_amount_less_less: 0.35; +$transparentize_surface_amount: 0.7; +$transparentize_surface_amount_more: 0.8; +$transparentize_surface_amount_subtract_surface: $transparentize_surface_amount - $transparentize_amount; + +@if $darkmode ==true { + // Less transparency + $transparentize_amount: 0.15; + $transparentize_surface_amount_less: 0.5; + $transparentize_surface_amount_less_less: 0.3; + $transparentize_surface_amount: 0.6; + $transparentize_surface_amount_more: 0.7; + $transparentize_surface_amount_subtract_surface: $transparentize_surface_amount - $transparentize_amount; +} + +// Extended material +$success: #4f6354; +$onSuccess: #ffffff; +$successContainer: #d1e8d5; +$onSuccessContainer: #0c1f13; + +@if $darkmode ==true { + // Dark variant + $success: #b5ccba; + $onSuccess: #213528; + $successContainer: #374b3e; + $onSuccessContainer: #d1e9d6; +} + +// Transparent material +$t_primary: transparentize($primary, $transparentize_amount); +$t_onPrimary: transparentize($onPrimary, $transparentize_amount); +$t_primaryContainer: transparentize($primaryContainer, $transparentize_amount); +$t_onPrimaryContainer: transparentize($onPrimaryContainer, $transparentize_amount); +$t_secondary: transparentize($secondary, $transparentize_amount); +$t_onSecondary: transparentize($onSecondary, $transparentize_amount); +$t_secondaryContainer: transparentize($secondaryContainer, $transparentize_amount); +$l_t_secondaryContainer: transparentize($secondaryContainer, $transparentize_surface_amount_less); +$t_onSecondaryContainer: transparentize($onSecondaryContainer, $transparentize_amount); +$t_t_t_onSecondaryContainer: transparentize($onSecondaryContainer, 0.93); +$t_tertiary: transparentize($tertiary, $transparentize_amount); +$t_onTertiary: transparentize($onTertiary, $transparentize_amount); +$t_tertiaryContainer: transparentize($tertiaryContainer, $transparentize_amount); +$t_onTertiaryContainer: transparentize($onTertiaryContainer, $transparentize_amount); +$t_error: transparentize($error, $transparentize_amount); +$t_onError: transparentize($onError, $transparentize_amount); +$t_errorContainer: transparentize($errorContainer, $transparentize_amount); +$t_onErrorContainer: transparentize($onErrorContainer, $transparentize_amount); +$t_colorbarbg: transparentize($colorbarbg, $transparentize_amount); +$t_background: transparentize($background, $transparentize_amount); +$t_t_background: transparentize($background, $transparentize_surface_amount_more); +$t_onBackground: transparentize($onBackground, $transparentize_amount); +$t_surface: transparentize($surface, $transparentize_surface_amount); +$t_t_surface: transparentize($surface, $transparentize_surface_amount_more); +$t_onSurface: transparentize($onSurface, $transparentize_surface_amount); +$t_surfaceVariant: transparentize($surfaceVariant, $transparentize_surface_amount); +$t_onSurfaceVariant: transparentize($onSurfaceVariant, $transparentize_surface_amount); +$t_t_surfaceVariant: transparentize($surfaceVariant, $transparentize_surface_amount_more); +$l_t_surfaceVariant: transparentize($surfaceVariant, $transparentize_surface_amount_less); +$l_l_t_surfaceVariant: transparentize($surfaceVariant, $transparentize_surface_amount_less_less); +$t_outline: transparentize($outline, $transparentize_amount); +$t_shadow: transparentize($shadow, $transparentize_amount); +$t_inverseSurface: transparentize($inverseSurface, $transparentize_amount); +$t_inverseOnSurface: transparentize($inverseOnSurface, $transparentize_amount); +$t_inversePrimary: transparentize($inversePrimary, $transparentize_amount); +// Transparent material (extended) +$t_success: transparentize($error, $transparentize_amount); +$t_onSuccess: transparentize($onError, $transparentize_amount); +$t_successContainer: transparentize($errorContainer, $transparentize_amount); +$t_onSuccessContainer: transparentize($onErrorContainer, $transparentize_amount); + +// Common stuff +$hovercolor: mix($t_surface, $t_onSurface, 50%); +$activecolor: mix($t_surface, $t_onSurface, 30%); +$subtext: mix($onBackground, $background, 70%); +$actiontext: mix($onBackground, $background, 85%); \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_common.scss b/desktops/hyprland/ags/scss/_common.scss new file mode 100644 index 0000000..1edca48 --- /dev/null +++ b/desktops/hyprland/ags/scss/_common.scss @@ -0,0 +1,43 @@ +*:focus { + // box-shadow: inset 0rem 0rem 2px $t_onSurface; +} + +.menu { + padding: 0.681rem; + background: $surfaceVariant; + color: $onSurfaceVariant; + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; +} + +.menubar>menuitem { + border-radius: 0.545rem; + -gtk-outline-radius: 0.545rem; + min-width: 13.636rem; + min-height: 2.727rem; +} + +.menu>menuitem { + padding: 0.4em 1.5rem; + background: transparent; + transition: 0.2s ease background; + border-radius: 0.545rem; + -gtk-outline-radius: 0.545rem; +} + +.menu>menuitem:hover { + background-color: mix($surfaceVariant, $onSurfaceVariant, 90%); +} + +.separator-line { + background-color: $surfaceVariant; + min-width: 0.068rem; + min-height: 0.068rem; +} + +tooltip { + @include large-rounding; + background-color: $surfaceVariant; + color: $onSurfaceVariant; + border: 1px solid $onSurfaceVariant; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_lib.scss b/desktops/hyprland/ags/scss/_lib.scss new file mode 100644 index 0000000..2c9101d --- /dev/null +++ b/desktops/hyprland/ags/scss/_lib.scss @@ -0,0 +1,695 @@ +// Common colors +$hovercolor: rgba(128, 128, 128, 0.3); +$activecolor: rgba(128, 128, 128, 0.7); +$rounding_small: 0.818rem; +$rounding_mediumsmall: 0.955rem; +$rounding_medium: 1.159rem; +$rounding_mediumlarge: 1.364rem; +$rounding_large: 1.705rem; + +.test { + background-image: linear-gradient(45deg, + #F4D609 0%, #F4D609 10%, #212121 10%, #212121 20%, + #F4D609 20%, #F4D609 30%, #212121 30%, #212121 40%, + #F4D609 40%, #F4D609 50%, #212121 50%, #212121 60%, + #F4D609 60%, #F4D609 70%, #212121 70%, #212121 80%, + #F4D609 80%, #F4D609 90%, #212121 90%, #212121 100%); + background-repeat: repeat; +} + +.test-size { + min-height: 3rem; + min-width: 3rem; +} + +// Common rules +@mixin small-rounding { + border-radius: $rounding_small; // 10px + -gtk-outline-radius: $rounding_small; // 10px +} + +@mixin normal-rounding { + border-radius: $rounding_medium; // small-rounding + 5px + -gtk-outline-radius: $rounding_medium; // small-rounding + 5px +} + +@mixin large-rounding { + border-radius: $rounding_large; // normal-rounding + 10px + -gtk-outline-radius: $rounding_large; // normal-rounding + 10px +} + +@mixin full-rounding { + border-radius: 9999px; + -gtk-outline-radius: 9999px; +} + +@mixin titlefont { + // Geometric sans-serif + font-family: + 'Gabarito', + 'Lexend', + sans-serif; +} + +.txt-title { + @include titlefont; + font-size: 2.045rem; +} + +.txt-title-small { + @include titlefont; + font-size: 1.364rem; +} + +@mixin mainfont { + // Other clean sans-serif + font-family: + 'AR One Sans', + 'Inter', + 'Roboto', + 'Noto Sans', + sans-serif; + // font-weight: 500; +} + +@mixin icon-material { + // Material Design Icons + font-family: + 'Material Symbols Rounded', + 'Material Symbols Outlined', + 'Material Symbols Sharp'; +} + +@mixin icon-nerd { + // Nerd Fonts + font-family: + 'SpaceMono Nerd Font', + 'JetBrainsMono Nerd Font', + monospace; +} + +@mixin techfont { + // Monospace for sys info n stuff. Doesn't have to be a nerd font, but it's cool. + font-family: 'JetBrains Mono Nerd Font', 'JetBrains Mono NL', 'SpaceMono Nerd Font', monospace; +} + +.techfont { + @include techfont; +} + +@mixin subtext { + color: $subtext; +} + +@mixin actiontext { + color: $actiontext; +} + +@mixin elevation-safe { + background: $surface; + color: $onSurface; + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.69); + margin: 7px; +} + +$elevation2_margin: 7px; + +@mixin elevation2 { + box-shadow: 0px 2px 3px transparentize($shadow, 0.55); + margin: $elevation2_margin; +} + +@mixin elevation2-margin { + margin: $elevation2_margin; +} + +@mixin elevation2-padding { + padding: $elevation2_margin; +} + +@mixin elevation3 { + box-shadow: 0px 2px 5px $shadow; + margin: 7px; +} + +@mixin md3_decel { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); +} + +@mixin md3_decel_fast { + transition: 170ms cubic-bezier(0.05, 0.7, 0.1, 1); +} + +@mixin md3_accel { + transition: 150ms cubic-bezier(0.3, 0, 0.8, 0.15); +} + +@mixin md3_accel_fast { + transition: 100ms cubic-bezier(0.3, 0, 0.8, 0.15); +} + +@mixin fluent_decel { + // Used for small transitions, as this looks clear + transition: 200ms cubic-bezier(0.1, 1, 0, 1); +} + +@mixin fluent_decel_long { + // Used for small transitions, as this looks clear + transition: 1000ms cubic-bezier(0.1, 1, 0, 1); +} + +@mixin fluent_accel { + transition: 150ms cubic-bezier(0.42, 0, 1, 1); +} + +@mixin noanim { + transition: 0ms; +} + +@mixin anim-enter { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); +} + +@mixin anim-exit { + transition: 150ms cubic-bezier(0.3, 0, 0.8, 0.15); +} + +@keyframes flyin-top { + from { + margin-top: -2.795rem; + } + + to { + margin-top: 0rem; + } +} + +@keyframes flyin-bottom { + from { + margin-top: 4.841rem; + margin-bottom: -4.841rem; + } + + to { + margin-bottom: 0rem; + margin-top: 0rem; + } +} + +@function tint($color, $percentage) { + @return mix(rgb(245, 250, 255), $color, $percentage); +} + +@function shade($color, $percentage) { + @return mix(rgb(0, 0, 0), $color, $percentage); +} + +.no-anim { + @include noanim; +} + +.txt { + color: $onBackground; +} + +.txt-shadow { + text-shadow: 1px 2px 8px rgba(0, 0, 0, 0.69); + margin: 10px; +} + +.txt-badonkers { + @include mainfont; + font-size: 3rem; +} + +.txt-tiddies { + @include mainfont; + font-size: 2.7273rem; +} + +.txt-hugeass { + @include mainfont; + font-size: 1.8182rem; +} + +.txt-larger { + @include mainfont; + font-size: 1.6363rem; +} + +.txt-large { + //16pt + @include mainfont; + font-size: 1.4545rem; +} + +.txt-norm { + //14pt + @include mainfont; + font-size: 1.2727rem; +} + +.txt-small { + //12pt + @include mainfont; + font-size: 1.0909rem; +} + +.txt-smallie { + //11pt + @include mainfont; + font-size: 1rem; +} + +.txt-smaller { + //10pt + @include mainfont; + font-size: 0.9091rem; +} + +.txt-smaller-offset { + margin-top: -0.136rem; +} + +.txt-tiny { + @include mainfont; + font-size: 0.7273rem; +} + +.txt-subtext { + @include subtext; +} + +.txt-action { + @include actiontext; +} + +.txt-semibold { + font-weight: 500; +} + +.txt-bold { + font-weight: bold; +} + +.titlefont { + @include titlefont; +} + +.mainfont { + @include mainfont; +} + +.icon-material { + @include icon-material; +} + +.separator-circle { + @include full-rounding; + background-color: $onSurface; + margin: 0rem 0.682rem; + min-width: 0.545rem; + min-height: 0.545rem; +} + +$overlay1: mix($onSurface, rgba(0, 0, 0, 0), 25%); +$overlay2: mix($onSurface, rgba(0, 0, 0, 0), 40%); + +.spacing-v-15>box { + margin-bottom: 1.023rem; +} + +.spacing-v-15>box:last-child { + margin-bottom: 0rem; +} + +.spacing-v-15>scrolledwindow { + margin-bottom: 1.023rem; +} + +.spacing-v-15>scrolledwindow:last-child { + margin-bottom: 0rem; +} + +.spacing-v-15>revealer { + margin-bottom: 1.023rem; +} + +.spacing-v-15>revealer:last-child { + margin-bottom: 0rem; +} + +.spacing-v-15>label { + margin-bottom: 1.023rem; +} + +.spacing-v-15>label:last-child { + margin-bottom: 0rem; +} + +.spacing-h-15>box { + margin-right: 1.023rem; +} + +.spacing-h-15>box:last-child { + margin-right: 0rem; +} + +.spacing-h-15>stack { + margin-right: 1.023rem; +} + +.spacing-h-15>stack:last-child { + margin-right: 0rem; +} + +.spacing-h-15>label { + margin-right: 1.023rem; +} + +.spacing-h-15>label:last-child { + margin-right: 0rem; +} + +.spacing-h-15>button { + margin-right: 1.023rem; +} + +.spacing-h-15>button:last-child { + margin-right: 0rem; +} + +.spacing-v-5>box { + margin-bottom: 0.341rem; +} + +.spacing-v-5>box:last-child { + margin-bottom: 0rem; +} + +.spacing-v-5>label { + margin-bottom: 0.341rem; +} + +.spacing-v-5>label:last-child { + margin-bottom: 0rem; +} + +.spacing-v-5>button { + margin-bottom: 0.341rem; +} + +.spacing-v-5>button:last-child { + margin-bottom: 0rem; +} + +.spacing-v-5-revealer>revealer>box { + margin-bottom: 0.341rem; +} + +.spacing-v-5-revealer>revealer:last-child>box { + margin-bottom: 0rem; +} + +.spacing-h-5>box { + margin-right: 0.341rem; +} + +.spacing-h-5>box:last-child { + margin-right: 0rem; +} + +.spacing-h-5>button { + margin-right: 0.341rem; +} + +.spacing-h-5>button:last-child { + margin-right: 0rem; +} + +.spacing-h-5>label { + margin-right: 0.341rem; +} + +.spacing-h-5>label:last-child { + margin-right: 0rem; +} + +.spacing-h-5>widget>box { + margin-right: 0.341rem; +} + +.spacing-h-5>widget:last-child>box { + margin-right: 0rem; +} + +.spacing-h-5>progressbar { + margin-right: 0.341rem; +} + +.spacing-h-5>progressbar:last-child { + margin-right: 0rem; +} + +.spacing-h-5>scrolledwindow { + margin-right: 0.341rem; +} + +.spacing-h-5>scrolledwindow:last-child { + margin-right: 0rem; +} + +.spacing-h-5>scrollbar { + margin-right: 0.341rem; +} + +.spacing-h-5>scrollbar:last-child { + margin-right: 0rem; +} + +.spacing-v-minus5>box { + margin-bottom: -0.341rem; +} + +.spacing-v-minus5>box:last-child { + margin-bottom: 0rem; +} + +.spacing-v-minus5>label { + margin-bottom: -0.341rem; +} + +.spacing-v-minus5>label:last-child { + margin-bottom: 0rem; +} + +.spacing-h-10>box { + margin-right: 0.682rem; +} + +.spacing-h-10>box:last-child { + margin-right: 0rem; +} + +.spacing-h-10>flowboxchild>button { + margin-right: 0.682rem; +} + +.spacing-h-10>flowboxchild:last-child>button { + margin-right: 0rem; +} + +.spacing-h-10>label { + margin-right: 0.682rem; +} + +.spacing-h-10>label:last-child { + margin-right: 0rem; +} + +.spacing-h-10>revealer { + margin-right: 0.682rem; +} + +.spacing-h-10>revealer:last-child { + margin-right: 0rem; +} + +.spacing-h-10>overlay { + margin-right: 0.682rem; +} + +.spacing-h-10>overlay:last-child { + margin-right: 0rem; +} + +.spacing-h-10>button { + margin-right: 0.682rem; +} + +.spacing-h-10>button:last-child { + margin-right: 0rem; +} + +.spacing-h-10>label { + margin-right: 0.682rem; +} + +.spacing-h-10>label:last-child { + margin-right: 0rem; +} + +.spacing-h-10>widget { + margin-right: 0.682rem; +} + +.spacing-h-10>widget:last-child { + margin-right: 0rem; +} + +.spacing-h-10>stack { + margin-right: 0.682rem; +} + +.spacing-h-10>stack:last-child { + margin-right: 0rem; +} + +.spacing-v-10>box { + margin-bottom: 0.682rem; +} + +.spacing-v-10>box:last-child { + margin-bottom: 0rem; +} + +.spacing-v-10>button { + margin-bottom: 0.682rem; +} + +.spacing-v-10>button:last-child { + margin-bottom: 0rem; +} + +.anim-enter { + @include anim-enter; +} + +.anim-exit { + @include anim-exit; +} + +@mixin elevation-border-softer { + border-top: 1px solid mix($t_t_surface, $t_onSurface, 80%); + border-left: 1px solid mix($t_t_surface, $t_onSurface, 80%); + border-right: 1px solid mix($t_t_surface, $t_onSurface, 85%); + border-bottom: 1px solid mix($t_t_surface, $t_onSurface, 85%); +} + +@mixin elevation-border { + border-top: 1px solid mix($t_t_surface, $onSurface, 90%); + border-left: 1px solid mix($t_t_surface, $onSurface, 90%); + border-right: 1px solid mix($t_t_surface, $onSurface, 95%); + border-bottom: 1px solid mix($t_t_surface, $onSurface, 95%); +} + +@mixin elevation-border-heavier { + border-top: 1px solid mix($t_t_surface, $onSurface, 80%); + border-left: 1px solid mix($t_t_surface, $onSurface, 80%); + border-right: 1px solid mix($t_t_surface, $onSurface, 85%); + border-bottom: 1px solid mix($t_t_surface, $onSurface, 85%); +} + +@mixin elevation-border-transparent { + border-top: 1px solid transparent; +} + +@mixin button-minsize { + min-width: 2.727rem; + min-height: 2.727rem; +} + +.button-minsize { + @include button-minsize; +} + +@mixin group-padding { + padding: 0.341rem; +} + +.group-padding { + @include group-padding; +} + +.margin-right-5 { + margin-right: 0.341rem; +} + +.margin-left-5 { + margin-left: 0.341rem; +} + +.margin-top-5 { + margin-top: 0.341rem; +} + +.margin-bottom-5 { + margin-bottom: 0.341rem; +} + +.margin-right-10 { + margin-right: 0.682rem; +} + +.margin-left-10 { + margin-left: 0.682rem; +} + +.margin-top-10 { + margin-top: 0.682rem; +} + +.margin-bottom-10 { + margin-bottom: 0.682rem; +} + +.invisible { + opacity: 0; + background-color: transparent; + color: transparent; +} + +.spacing-h--5>box { + margin-right: -0.341rem; +} + +.spacing-h--5>box:last-child { + margin-right: 0rem; +} + +.spacing-v--5>box { + margin-bottom: -0.341rem; +} + +.spacing-v--5>box:last-child { + margin-bottom: 0rem; +} + +.spacing-h--20>box { + margin-left: -1.364rem; +} + +.spacing-h--20>box:first-child { + margin-left: 0rem; +} + +$white: white; +$black: black; + +.instant { + transition: 0ms; +} + +.md3_decel { + @include md3_decel; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_material.scss b/desktops/hyprland/ags/scss/_material.scss new file mode 100755 index 0000000..152f9b5 --- /dev/null +++ b/desktops/hyprland/ags/scss/_material.scss @@ -0,0 +1,29 @@ +$darkmode: false; +$primary: #006874; +$onPrimary: #ffffff; +$primaryContainer: #8ef1ff; +$onPrimaryContainer: #001f24; +$secondary: #4a6266; +$onSecondary: #ffffff; +$secondaryContainer: #cde7ec; +$onSecondaryContainer: #051f23; +$tertiary: #525e7d; +$onTertiary: #ffffff; +$tertiaryContainer: #d8e2ff; +$onTertiaryContainer: #0e1b36; +$error: #ba1b1b; +$onError: #ffffff; +$errorContainer: #ffdad4; +$onErrorContainer: #410001; +$colorbarbg: #fbfdfd; +$background: #fbfdfd; +$onBackground: #191c1d; +$surface: #fbfdfd; +$onSurface: #191c1d; +$surfaceVariant: #dbe4e6; +$onSurfaceVariant: #3f484a; +$outline: #6f797b; +$shadow: #000000; +$inverseSurface: #2d3132; +$inverseOnSurface: #eff1f1; +$inversePrimary: #4fd8ea; diff --git a/desktops/hyprland/ags/scss/_notifications.scss b/desktops/hyprland/ags/scss/_notifications.scss new file mode 100644 index 0000000..40e743d --- /dev/null +++ b/desktops/hyprland/ags/scss/_notifications.scss @@ -0,0 +1,126 @@ +$notif_surface: $t_background; + +@mixin notif-rounding { + @include small-rounding; +} + +.notif-low { + @include notif-rounding; + background-color: $l_l_t_surfaceVariant; + color: $onSurfaceVariant; + padding: $rounding_small; + padding-right: $rounding_small + 0.545rem; +} + +.notif-normal { + @include notif-rounding; + background-color: $l_l_t_surfaceVariant; + color: $onSurfaceVariant; + padding: $rounding_small; + padding-right: $rounding_small + 0.545rem; +} + +.notif-critical { + @include notif-rounding; + background-color: $error; + color: $onError; + padding: $rounding_small; + padding-right: $rounding_small + 0.545rem; +} + +.popup-notif-low { + @include notif-rounding; + min-width: 30.682rem; + background-color: $notif_surface; + color: $onSurfaceVariant; + padding: $rounding_small; + padding-right: $rounding_small + 0.545rem; +} + +.popup-notif-normal { + @include notif-rounding; + min-width: 30.682rem; + background-color: $notif_surface; + color: $onSurfaceVariant; + padding: $rounding_small; + padding-right: $rounding_small + 0.545rem; +} + +.popup-notif-critical { + @include notif-rounding; + min-width: 30.682rem; + background-color: $error; + color: $onError; + padding: $rounding_small; + padding-right: $rounding_small + 0.545rem; +} + +.notif-body-low { + color: mix($onSurfaceVariant, $surfaceVariant, 67%); +} + +.notif-body-normal { + color: mix($onSurfaceVariant, $surfaceVariant, 67%); +} + +.notif-body-critical { + color: mix($onError, $error, 67%); +} + +.notif-icon { + @include full-rounding; + min-width: 3.409rem; + min-height: 3.409rem; +} + +.notif-icon-material { + background-color: $t_secondaryContainer; + color: $onSecondaryContainer; +} + +.notif-icon-material-low { + background-color: $t_secondaryContainer; + color: $onSecondaryContainer; +} + +.notif-icon-material-normal { + background-color: $t_secondaryContainer; + color: $onSecondaryContainer; +} + +.notif-icon-material-critical { + background-color: $t_errorContainer; + color: $onErrorContainer; +} + +.notif-close-btn { + @include notif-rounding; + padding: 0rem 0.136rem; +} + +.notif-close-btn:hover { + background: $hovercolor; +} + +.notif-close-btn:active { + background: $activecolor; +} + +.notif-closeall-btn { + @include notif-rounding; + padding: 0.341rem 0.341rem; +} + +.notif-closeall-btn:hover { + background-color: $hovercolor; +} + +.notif-closeall-btn:active { + background-color: $activecolor; +} + +.osd-notif { + @include notif-rounding; + background-color: transparentize($background, $transparentize_surface_amount_subtract_surface); + min-width: 30.682rem; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_osd.scss b/desktops/hyprland/ags/scss/_osd.scss new file mode 100644 index 0000000..33ced71 --- /dev/null +++ b/desktops/hyprland/ags/scss/_osd.scss @@ -0,0 +1,59 @@ +.osd-bg { + min-width: 8.864rem; + min-height: 3.409rem; +} + +.osd-value { + background-color: $t_background; + border-radius: 1.023rem; + padding: 0.625rem 1.023rem; + padding-top: 0.313rem; + margin: 10px; + @include elevation2; +} + +.osd-progress { + min-height: 0.955rem; + min-width: 0.068rem; + padding: 0rem; + border-radius: 10rem; + @include fluent_decel; + + trough { + min-height: 0.954rem; + min-width: 0.068rem; + border-radius: 10rem; + background-color: $onPrimaryContainer; + } + + progress { + @include fluent_decel; + min-height: 0.680rem; + min-width: 0.680rem; + margin: 0rem 0.137rem; + border-radius: 10rem; + background-color: $primaryContainer; + } +} + +.osd-icon { + color: $onPrimaryContainer; +} + +.osd-label { + font-size: 1.023rem; + font-weight: 500; + color: $onBackground; + margin-top: 0.341rem; +} + +.osd-value-txt { + @include titlefont; + font-size: 1.688rem; + font-weight: 500; + color: $onBackground; +} + +.osd-notifs { + padding-top: 0.313rem; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_osk.scss b/desktops/hyprland/ags/scss/_osk.scss new file mode 100644 index 0000000..22e62e5 --- /dev/null +++ b/desktops/hyprland/ags/scss/_osk.scss @@ -0,0 +1,97 @@ +$osk_key_height: 2.5rem; +$osk_key_width: 2.5rem; +$osk_key_padding: 0.188rem; +$osk_key_rounding: 0.682rem; +$osk_key_fontsize: 1.091rem; + +.osk-window { + @include md3_decel_fast; + @include large-rounding; + @include elevation-border; + @include elevation2; + // min-height: 29.591rem; + // min-width: 50rem; + background-color: $t_background; + padding: 1.023rem; +} + +.osk-show { + @include md3_decel_fast; +} + +.osk-hide { + margin-top: 30.682rem; + margin-bottom: -30.682rem; + // opacity: 0; + @include md3_accel_fast; +} + +.osk-key { + border-radius: $osk_key_rounding; + background-color: $t_surfaceVariant; + color: $onSurfaceVariant; + padding: $osk_key_padding; + font-weight: 500; + font-size: $osk_key_fontsize; +} + +.osk-key:hover { + background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 90%); +} + +.osk-key:active { + background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 70%); + font-size: $osk_key_fontsize; +} + +.osk-key-active { + background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 70%); +} + +.osk-key-normal { + min-width: $osk_key_width; + min-height: $osk_key_height; +} + +.osk-key-fn { + min-width: $osk_key_width * 1.005; + min-height: $osk_key_height / 2; +} + +.osk-key-tab { + min-width: $osk_key_width * 1.6; + min-height: $osk_key_height; +} + +.osk-key-caps { + min-width: $osk_key_width * 1.9; + min-height: $osk_key_height; +} + +.osk-key-shift { + min-width: $osk_key_width * 2.5; + min-height: $osk_key_height; +} + +.osk-key-control { + min-width: $osk_key_width * 1.3; + min-height: $osk_key_height; +} + +.osk-control-button { + border-radius: $osk_key_rounding; + background-color: $t_surfaceVariant; + color: $onSurfaceVariant; + font-weight: 500; + font-size: $osk_key_fontsize; + padding: 0.682rem; +} + +.osk-control-button:hover { + background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 90%); +} + +.osk-control-button:active { + background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 70%); + font-size: $osk_key_fontsize; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_overview.scss b/desktops/hyprland/ags/scss/_overview.scss new file mode 100644 index 0000000..4232d36 --- /dev/null +++ b/desktops/hyprland/ags/scss/_overview.scss @@ -0,0 +1,130 @@ +.overview-search-box { + @include md3_decel; + @include large-rounding; + @include elevation-border; + @include elevation2; + min-width: 13.636rem; + min-height: 3.409rem; + padding: 0rem 1.364rem; + padding-right: 2.864rem; + background-color: $t_background; + color: $onBackground; + + selection { + background-color: $secondary; + color: $onSecondary; + } + caret-color: transparent; +} + +.overview-search-box-extended { + min-width: 25.909rem; + caret-color: $onSecondaryContainer; +} + +.overview-search-prompt { + color: $subtext; +} + +.overview-search-icon { + margin: 0rem 1.023rem; +} + +.overview-search-prompt-box { + margin-left: -18.545rem; + margin-right: $elevation2_margin + 1px; +} + +.overview-search-icon-box { + margin-left: -18.545rem; + margin-right: $elevation2_margin + 1px; +} + +.overview-search-results { + // min-height: 2.813rem; + // min-height: 37.5rem; + @include large-rounding; + @include elevation-border; + @include elevation2; + min-width: 28.773rem; + padding: 0.682rem; + background-color: $t_background; + color: $onBackground; +} + +.overview-search-results-icon { + margin: 0rem 0.682rem; + font-size: 2.386rem; +} +.overview-search-results-txt { + margin-right: 0.682rem; +} + +.overview-search-results-txt-cmd { + margin-right: 0.682rem; + @include techfont; + font-size: 1.227rem; +} + +.overview-search-result-btn { + @include normal-rounding; + padding: 0.341rem; + min-width: 2.386rem; + min-height: 2.386rem; + + caret-color: transparent; +} + +.overview-search-result-btn:focus, +.overview-search-result-btn:hover { + background-color: $hovercolor; +} + +.overview-search-result-btn:active { + background-color: $activecolor; +} + +.overview-tasks { + @include large-rounding; + @include elevation-border; + @include elevation2; + padding: 0.341rem; + background-color: $t_background; + color: $onBackground; +} + +.overview-tasks-workspace { + @include normal-rounding; + // @include elevation-border; + margin: 0.341rem; + background-color: mix($t_t_surface, $t_onSurface, 93%); +} + +.overview-tasks-window { + @include normal-rounding; + // @include elevation-border-softer; + @include md3_decel; + background-color: $l_t_secondaryContainer; + color: $onSecondaryContainer; + border: 0.068rem solid $t_t_t_onSecondaryContainer; +} + +.overview-tasks-window:hover { + background-color: mix($l_t_secondaryContainer, $primary, 95%); +} + +.overview-tasks-window:focus { + background-color: mix($l_t_secondaryContainer, $primary, 95%); +} + +.overview-tasks-window:active { + background-color: mix($l_t_secondaryContainer, $primary, 90%); +} + +.overview-tasks-window-selected { + background-color: mix($l_t_secondaryContainer, $primary, 90%); +} + +.overview-tasks-window-dragging { + opacity: 0.2; +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/_session.scss b/desktops/hyprland/ags/scss/_session.scss new file mode 100644 index 0000000..d302207 --- /dev/null +++ b/desktops/hyprland/ags/scss/_session.scss @@ -0,0 +1,36 @@ +.session-bg { + margin-top: -2.727rem; + background-color: mix($t_t_background, $background, 70%); +} + +.session-button { + @include large-rounding; + min-width: 8.182rem; + min-height: 8.182rem; + background-color: $surfaceVariant; + color: $onSurfaceVariant; + font-size: 3rem; +} + +.session-button-focused { + background-color: $secondaryContainer; + color: $onSecondaryContainer; +} + +.session-button-desc { + background-color: mix($surface, $surfaceVariant, 50%); + color: mix($onSurface, $onSurfaceVariant, 50%); + border-bottom-left-radius: $rounding_large; + border-bottom-right-radius: $rounding_large; + padding: 0.205rem 0.341rem; + font-weight: 700; +} + +.session-button-cancel { + @include large-rounding; + min-width: 8.182rem; + min-height: 5.455rem; + background-color: $surfaceVariant; + color: $onSurfaceVariant; + font-size: 3rem; +} diff --git a/desktops/hyprland/ags/scss/_sidebars.scss b/desktops/hyprland/ags/scss/_sidebars.scss new file mode 100644 index 0000000..8f165f6 --- /dev/null +++ b/desktops/hyprland/ags/scss/_sidebars.scss @@ -0,0 +1,389 @@ +// sideleft sideright stuff + +.sidebar-right { + @include md3_decel; + @include large-rounding; + @include elevation-border; + @include elevation2; + // min-width: 29.591rem; + // min-height: 29.591rem; + background-color: $t_background; + padding: 1.023rem; +} + +.sideright-show { + @include md3_decel; +} + +.sideright-hide { + margin-right: -30.682rem; + // opacity: 0; + @include md3_accel; +} + +.sidebar-left { + @include md3_decel; + @include large-rounding; + @include elevation-border; + @include elevation2; + // min-width: 29.591rem; // COMMENT THIS LATER IF TEXT WRAP IS USED + // min-height: 29.591rem; + background-color: $t_background; + padding: 1.023rem; +} + +.sideleft-show { + @include md3_decel; +} + +.sideleft-hide { + margin-left: -30.682rem; + // opacity: 0; + @include md3_accel; +} + +.sidebar-group { + @include normal-rounding; + // @include elevation-border; + @include group-padding; + background-color: $t_surface; +} + +.sidebar-group-nopad { + @include normal-rounding; + // @include elevation-border; + background-color: $t_surface; +} + +.sidebar-group-invisible { + @include group-padding; +} + +.sidebar-group-invisible-morehorizpad { + padding: 0.341rem 0.682rem; +} + +.sidebar-iconbutton { + @include full-rounding; + @include md3_decel; + color: $onSurface; + min-width: 2.727rem; + min-height: 2.727rem; +} + +.sidebar-iconbutton:hover { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%); +} + +.sidebar-iconbutton:active { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%); +} + +.sidebar-button { + @include md3_decel; + padding: 0rem $rounding_small; + background-color: $t_secondaryContainer; + color: $onSecondaryContainer; +} + +.sidebar-button-nopad { + @include md3_decel; + background-color: $t_secondaryContainer; + color: $onSecondaryContainer; +} + +.sidebar-button:hover { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%); +} + +.sidebar-button:active { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%); +} + +.sidebar-button-nopad:hover { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%); +} + +.sidebar-button-nopad:active { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%); +} + +.sidebar-button-left { + border-top-left-radius: $rounding_small; + border-bottom-left-radius: $rounding_small; +} + +.sidebar-button-right { + border-top-right-radius: $rounding_small; + border-bottom-right-radius: $rounding_mediumsmall; +} + +.sidebar-button-alone { + @include small-rounding; +} + +.sidebar-button-alone-normal { + @include small-rounding; +} + +.sidebar-button-active { + background-color: $primary; + color: $onPrimary; +} + +.sidebar-button-active:hover { + background-color: mix($primary, $hovercolor, 90%); +} + +.sidebar-button-active:active { + background-color: mix($primary, $hovercolor, 70%); +} + +.sidebar-buttons-separator { + min-width: 0.068rem; + min-height: 0.068rem; + background-color: $onSurfaceVariant; +} + +.sidebar-navrail { + // background-color: $t_surface; + padding: 0rem $rounding_medium; +} + +.sidebar-navrail-btn>box>label { + @include full-rounding; + @include md3_decel; +} + +.sidebar-navrail-btn:hover>box>label:first-child { + background-color: mix($t_surfaceVariant, $onSurfaceVariant, 90%); +} + +.sidebar-navrail-btn:active>box>label:first-child { + background-color: mix($surfaceVariant, $onSurfaceVariant, 75%); +} + +.sidebar-navrail-btn-active>box>label:first-child { + background-color: $secondaryContainer; + color: $onSecondaryContainer; +} + +.sidebar-navrail-btn-active:hover>box>label:first-child { + background-color: mix($secondaryContainer, $hovercolor, 90%); + color: mix($onSecondaryContainer, $hovercolor, 90%); +} + +.sidebar-sysinfo-grouppad { + padding: 1.159rem; +} + +.sidebar-memory-ram-circprog { + @include fluent_decel_long; + min-width: $rounding_small; + min-height: 4.091rem; + padding: 0.409rem; + background-color: $secondaryContainer; + color: $onSecondaryContainer; + font-size: 0px; +} + +.sidebar-memory-swap-circprog { + @include fluent_decel_long; + min-width: $rounding_small; + min-height: 2.255rem; + padding: 0.409rem; + margin: 0.918rem; + background-color: $secondaryContainer; + color: $onSecondaryContainer; + font-size: 0px; +} + +.sidebar-cpu-circprog { + min-width: $rounding_small; + min-height: 3.409rem; + padding: 0.409rem; + background-color: $secondaryContainer; + color: $onSecondaryContainer; + @include fluent_decel_long; + font-size: 0px; +} + +// .sidebar-sysinfo-txt { +// font-size: 1.0909rem; +// @include techfont; +// } + +.sidebar-viewport { + @include small-rounding; +} + +.sidebar-scrollbar { + trough { + @include full-rounding; + min-width: 0.545rem; + background-color: transparent; + } + + slider { + @include full-rounding; + min-width: 0.273rem; + min-height: 2.045rem; + background-color: $t_onSurfaceVariant; + } + + slider:hover { + background-color: mix($t_onSurfaceVariant, $onSurfaceVariant, 80%); + } + + slider:active { + background-color: mix($onSurfaceVariant, $surfaceVariant, 50%); + } +} + +.sidebar-calendar-btn { + @include full-rounding; + @include md3_decel; + min-height: 2.523rem; + min-width: 2.523rem; + color: $onSurface; +} + +.sidebar-calendar-btn:hover { + background-color: $hovercolor; +} + +.sidebar-calendar-btn:active { + background-color: $activecolor; +} + +.sidebar-calendar-btn-txt { + margin-left: -10.341rem; + margin-right: -10.341rem; +} + +.sidebar-calendar-btn-today { + background-color: $primary; + color: $onPrimary; +} + +.sidebar-calendar-btn-today:hover { + background-color: mix($primary, $hovercolor, 90%); +} + +.sidebar-calendar-btn-today:active { + background-color: mix($primary, $hovercolor, 70%); +} + +.sidebar-calendar-btn-othermonth { + color: mix($onSurface, $surface, 50%); +} + +.sidebar-calendar-header { + margin: 0.341rem; +} + +.sidebar-calendar-monthyear-btn { + @include full-rounding; + padding: 0rem 0.682rem; + background-color: $t_surfaceVariant; + color: $onSurfaceVariant; +} + +.sidebar-calendar-monthyear-btn:hover { + background-color: mix($t_surfaceVariant, $onSurfaceVariant, 95%); + color: mix($onSurfaceVariant, $surfaceVariant, 95%); +} + +.sidebar-calendar-monthyear-btn:active { + background-color: mix($surfaceVariant, $onSurfaceVariant, 85%); + color: mix($onSurfaceVariant, $surfaceVariant, 85%); +} + +.sidebar-calendar-monthshift-btn { + @include full-rounding; + min-width: 2.045rem; + min-height: 2.045rem; + background-color: $t_surfaceVariant; + color: $onSurfaceVariant; +} + +.sidebar-calendar-monthshift-btn:hover { + background-color: mix($t_surfaceVariant, $onSurfaceVariant, 95%); + color: mix($onSurfaceVariant, $surfaceVariant, 95%); +} + +.sidebar-calendar-monthshift-btn:active { + background-color: mix($surfaceVariant, $onSurfaceVariant, 85%); + color: mix($onSurfaceVariant, $surfaceVariant, 85%); +} + +.sidebar-todo-selector-tab { + @include small-rounding; + transition: 0ms; + min-height: 2.5rem; + color: $onSurface; +} + +.sidebar-todo-selector-tab:hover { + background-color: mix($t_surfaceVariant, $onSurfaceVariant, 90%); +} + +.sidebar-todo-selector-tab:active { + background-color: mix($surfaceVariant, $onSurfaceVariant, 75%); +} + +.sidebar-todo-selector-tab-active>box>label { + color: $primary; +} + +.sidebar-todo-selector-highlight-offset { + margin-top: -0.205rem; + margin-bottom: 0.205rem; +} + +.sidebar-todo-selector-highlight { + transition: 180ms ease-in-out; // Doesn't look that good, but it syncs with GtkStack animation of the actual todo widget content + color: $primary; + padding: 0rem 2.045rem; + min-height: 0.205rem; +} + +.sidebar-todo-item-action { + border-radius: 9999px; + min-width: 1.705rem; + min-height: 1.705rem; +} + +.sidebar-todo-item-action:hover { + background-color: mix($t_surface, $t_onSurface, 95%); +} + +.sidebar-todo-item-action:active { + background-color: mix($t_surface, $t_onSurface, 85%); +} + +.sidebar-clipboard-item { + border-radius: $rounding_small; + min-height: 2.045rem; + padding: 0.341rem; + background-color: $t_secondaryContainer; + color: $onSecondaryContainer; +} + +.sidebar-clipboard-item:hover { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 90%); +} + +.sidebar-clipboard-item:active { + background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%); +} + +// cool gradient background from amberol +// .main-window { +// background: linear-gradient(127deg, alpha(@background_color_0, .55), alpha(@background_color_0, 0) 70.71%), +// linear-gradient(217deg, alpha(@background_color_1, .55), alpha(@background_color_1, 0) 70.71%), +// linear-gradient(336deg, alpha(@background_color_2, .55), alpha(@background_color_2, 0) 70.71%); +// transition-property: background; +// transition-duration: 250ms; +// transition-timing-function: ease; +// } \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/main.scss b/desktops/hyprland/ags/scss/main.scss new file mode 100644 index 0000000..d16a772 --- /dev/null +++ b/desktops/hyprland/ags/scss/main.scss @@ -0,0 +1,28 @@ +// Reset +* { + all: unset; +} + +// Colors +@import './material'; // Material colors +@import './colors'; // Global color definitions. Uses material colors as base. +@import './lib'; // Global mixins and functions +@import './common'; // Context menu n stuff + +// Components +@import './bar'; +@import './cheatsheet'; +@import './sidebars'; +@import './osd'; +@import './overview'; +@import './osk'; +@import './session'; +@import './notifications'; + +// Classes for interaction +.growingRadial { + transition: 300ms cubic-bezier(0.2, 0.0, 0, 1.0); +} +.fadingRadial { + transition: 50ms cubic-bezier(0.2, 0.0, 0, 1.0); +} \ No newline at end of file diff --git a/desktops/hyprland/ags/scss/scss.js b/desktops/hyprland/ags/scss/scss.js new file mode 100644 index 0000000..b627c0e --- /dev/null +++ b/desktops/hyprland/ags/scss/scss.js @@ -0,0 +1,30 @@ +const { App, Service, Utils } = ags; +const { execAsync, CONFIG_DIR } = Utils; + +async function setupScss() { + try { + await execAsync(['sassc', `${CONFIG_DIR}/scss/main.scss`, `${CONFIG_DIR}/style.css`]); + App.resetCss(); + App.applyCss(`${CONFIG_DIR}/style.css`); + } catch (error) { + print(error); + } +} + +class ThemeService extends Service { + static { Service.register(this); } + + constructor() { + super(); + this.setup(); + } + + setup() { + setupScss(); + } +} + +var Theme = class Theme { + static { globalThis['Theme'] = this; } + static instance = new ThemeService(); +}; \ No newline at end of file diff --git a/desktops/hyprland/ags/style.css b/desktops/hyprland/ags/style.css new file mode 100644 index 0000000..c8d5fc0 --- /dev/null +++ b/desktops/hyprland/ags/style.css @@ -0,0 +1,1537 @@ +* { + all: unset; } + +.test { + background-image: linear-gradient(45deg, #F4D609 0%, #F4D609 10%, #212121 10%, #212121 20%, #F4D609 20%, #F4D609 30%, #212121 30%, #212121 40%, #F4D609 40%, #F4D609 50%, #212121 50%, #212121 60%, #F4D609 60%, #F4D609 70%, #212121 70%, #212121 80%, #F4D609 80%, #F4D609 90%, #212121 90%, #212121 100%); + background-repeat: repeat; } + +.test-size { + min-height: 3rem; + min-width: 3rem; } + +.txt-title { + font-family: 'Gabarito', 'Lexend', sans-serif; + font-size: 2.045rem; } + +.txt-title-small { + font-family: 'Gabarito', 'Lexend', sans-serif; + font-size: 1.364rem; } + +.techfont { + font-family: 'JetBrains Mono Nerd Font', 'JetBrains Mono NL', 'SpaceMono Nerd Font', monospace; } + +@keyframes flyin-top { + from { + margin-top: -2.795rem; } + to { + margin-top: 0rem; } } + +@keyframes flyin-bottom { + from { + margin-top: 4.841rem; + margin-bottom: -4.841rem; } + to { + margin-bottom: 0rem; + margin-top: 0rem; } } + +.no-anim { + transition: 0ms; } + +.txt { + color: #191c1d; } + +.txt-shadow { + text-shadow: 1px 2px 8px rgba(0, 0, 0, 0.69); + margin: 10px; } + +.txt-badonkers { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 3rem; } + +.txt-tiddies { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 2.7273rem; } + +.txt-hugeass { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 1.8182rem; } + +.txt-larger { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 1.6363rem; } + +.txt-large { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 1.4545rem; } + +.txt-norm { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 1.2727rem; } + +.txt-small { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 1.0909rem; } + +.txt-smallie { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 1rem; } + +.txt-smaller { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 0.9091rem; } + +.txt-smaller-offset { + margin-top: -0.136rem; } + +.txt-tiny { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + font-size: 0.7273rem; } + +.txt-subtext { + color: #5d6060; } + +.txt-action { + color: #3b3e3f; } + +.txt-semibold { + font-weight: 500; } + +.txt-bold { + font-weight: bold; } + +.titlefont { + font-family: 'Gabarito', 'Lexend', sans-serif; } + +.mainfont { + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; } + +.icon-material { + font-family: 'Material Symbols Rounded', 'Material Symbols Outlined', 'Material Symbols Sharp'; } + +.separator-circle { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + background-color: #191c1d; + margin: 0rem 0.682rem; + min-width: 0.545rem; + min-height: 0.545rem; } + +.spacing-v-15 > box { + margin-bottom: 1.023rem; } + +.spacing-v-15 > box:last-child { + margin-bottom: 0rem; } + +.spacing-v-15 > scrolledwindow { + margin-bottom: 1.023rem; } + +.spacing-v-15 > scrolledwindow:last-child { + margin-bottom: 0rem; } + +.spacing-v-15 > revealer { + margin-bottom: 1.023rem; } + +.spacing-v-15 > revealer:last-child { + margin-bottom: 0rem; } + +.spacing-v-15 > label { + margin-bottom: 1.023rem; } + +.spacing-v-15 > label:last-child { + margin-bottom: 0rem; } + +.spacing-h-15 > box { + margin-right: 1.023rem; } + +.spacing-h-15 > box:last-child { + margin-right: 0rem; } + +.spacing-h-15 > stack { + margin-right: 1.023rem; } + +.spacing-h-15 > stack:last-child { + margin-right: 0rem; } + +.spacing-h-15 > label { + margin-right: 1.023rem; } + +.spacing-h-15 > label:last-child { + margin-right: 0rem; } + +.spacing-h-15 > button { + margin-right: 1.023rem; } + +.spacing-h-15 > button:last-child { + margin-right: 0rem; } + +.spacing-v-5 > box { + margin-bottom: 0.341rem; } + +.spacing-v-5 > box:last-child { + margin-bottom: 0rem; } + +.spacing-v-5 > label { + margin-bottom: 0.341rem; } + +.spacing-v-5 > label:last-child { + margin-bottom: 0rem; } + +.spacing-v-5 > button { + margin-bottom: 0.341rem; } + +.spacing-v-5 > button:last-child { + margin-bottom: 0rem; } + +.spacing-v-5-revealer > revealer > box { + margin-bottom: 0.341rem; } + +.spacing-v-5-revealer > revealer:last-child > box { + margin-bottom: 0rem; } + +.spacing-h-5 > box { + margin-right: 0.341rem; } + +.spacing-h-5 > box:last-child { + margin-right: 0rem; } + +.spacing-h-5 > button { + margin-right: 0.341rem; } + +.spacing-h-5 > button:last-child { + margin-right: 0rem; } + +.spacing-h-5 > label { + margin-right: 0.341rem; } + +.spacing-h-5 > label:last-child { + margin-right: 0rem; } + +.spacing-h-5 > widget > box { + margin-right: 0.341rem; } + +.spacing-h-5 > widget:last-child > box { + margin-right: 0rem; } + +.spacing-h-5 > progressbar { + margin-right: 0.341rem; } + +.spacing-h-5 > progressbar:last-child { + margin-right: 0rem; } + +.spacing-h-5 > scrolledwindow { + margin-right: 0.341rem; } + +.spacing-h-5 > scrolledwindow:last-child { + margin-right: 0rem; } + +.spacing-h-5 > scrollbar { + margin-right: 0.341rem; } + +.spacing-h-5 > scrollbar:last-child { + margin-right: 0rem; } + +.spacing-v-minus5 > box { + margin-bottom: -0.341rem; } + +.spacing-v-minus5 > box:last-child { + margin-bottom: 0rem; } + +.spacing-v-minus5 > label { + margin-bottom: -0.341rem; } + +.spacing-v-minus5 > label:last-child { + margin-bottom: 0rem; } + +.spacing-h-10 > box { + margin-right: 0.682rem; } + +.spacing-h-10 > box:last-child { + margin-right: 0rem; } + +.spacing-h-10 > flowboxchild > button { + margin-right: 0.682rem; } + +.spacing-h-10 > flowboxchild:last-child > button { + margin-right: 0rem; } + +.spacing-h-10 > label { + margin-right: 0.682rem; } + +.spacing-h-10 > label:last-child { + margin-right: 0rem; } + +.spacing-h-10 > revealer { + margin-right: 0.682rem; } + +.spacing-h-10 > revealer:last-child { + margin-right: 0rem; } + +.spacing-h-10 > overlay { + margin-right: 0.682rem; } + +.spacing-h-10 > overlay:last-child { + margin-right: 0rem; } + +.spacing-h-10 > button { + margin-right: 0.682rem; } + +.spacing-h-10 > button:last-child { + margin-right: 0rem; } + +.spacing-h-10 > label { + margin-right: 0.682rem; } + +.spacing-h-10 > label:last-child { + margin-right: 0rem; } + +.spacing-h-10 > widget { + margin-right: 0.682rem; } + +.spacing-h-10 > widget:last-child { + margin-right: 0rem; } + +.spacing-h-10 > stack { + margin-right: 0.682rem; } + +.spacing-h-10 > stack:last-child { + margin-right: 0rem; } + +.spacing-v-10 > box { + margin-bottom: 0.682rem; } + +.spacing-v-10 > box:last-child { + margin-bottom: 0rem; } + +.spacing-v-10 > button { + margin-bottom: 0.682rem; } + +.spacing-v-10 > button:last-child { + margin-bottom: 0rem; } + +.anim-enter { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); } + +.anim-exit { + transition: 150ms cubic-bezier(0.3, 0, 0.8, 0.15); } + +.button-minsize { + min-width: 2.727rem; + min-height: 2.727rem; } + +.group-padding { + padding: 0.341rem; } + +.margin-right-5 { + margin-right: 0.341rem; } + +.margin-left-5 { + margin-left: 0.341rem; } + +.margin-top-5 { + margin-top: 0.341rem; } + +.margin-bottom-5 { + margin-bottom: 0.341rem; } + +.margin-right-10 { + margin-right: 0.682rem; } + +.margin-left-10 { + margin-left: 0.682rem; } + +.margin-top-10 { + margin-top: 0.682rem; } + +.margin-bottom-10 { + margin-bottom: 0.682rem; } + +.invisible { + opacity: 0; + background-color: transparent; + color: transparent; } + +.spacing-h--5 > box { + margin-right: -0.341rem; } + +.spacing-h--5 > box:last-child { + margin-right: 0rem; } + +.spacing-v--5 > box { + margin-bottom: -0.341rem; } + +.spacing-v--5 > box:last-child { + margin-bottom: 0rem; } + +.spacing-h--20 > box { + margin-left: -1.364rem; } + +.spacing-h--20 > box:first-child { + margin-left: 0rem; } + +.instant { + transition: 0ms; } + +.md3_decel { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); } + +.menu { + padding: 0.681rem; + background: #dbe4e6; + color: #3f484a; + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; } + +.menubar > menuitem { + border-radius: 0.545rem; + -gtk-outline-radius: 0.545rem; + min-width: 13.636rem; + min-height: 2.727rem; } + +.menu > menuitem { + padding: 0.4em 1.5rem; + background: transparent; + transition: 0.2s ease background; + border-radius: 0.545rem; + -gtk-outline-radius: 0.545rem; } + +.menu > menuitem:hover { + background-color: #cbd4d6; } + +.separator-line { + background-color: #dbe4e6; + min-width: 0.068rem; + min-height: 0.068rem; } + +tooltip { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + background-color: #dbe4e6; + color: #3f484a; + border: 1px solid #3f484a; } + +.bar-bg { + background-color: rgba(251, 253, 253, 0.7); + min-height: 2.727rem; } + +.bar-sidespace { + min-width: 1.5rem; } + +.bar-group-margin { + padding: 0.2rem; } + +.bar-group { + background-color: rgba(251, 253, 253, 0.3); } + +.bar-group-center { + border-bottom-left-radius: 1.364rem; + border-bottom-right-radius: 1.364rem; + padding: 0.2rem; + background-color: black; } + +.corner-bar-group { + border-radius: 1.364rem; + border-width: 0.068rem; + background-color: black; } + +.bar-group-pad { + padding: 0rem 1.023rem; } + +.bar-group-pad-less { + padding: 0rem 0.681rem; } + +.bar-group-pad-system { + padding-left: 1.023rem; + padding-right: 0.547rem; } + +.bar-group-pad-music { + padding-right: 1.023rem; } + +.bar-group-pad-left { + padding-left: 1.364rem; + padding-right: 0.681rem; } + +.bar-group-pad-right { + padding-left: 0.681rem; + padding-right: 1.364rem; } + +.bar-group-pad-leftonly { + padding-left: 0.681rem; } + +.bar-group-pad-rightonly { + padding-right: 0.681rem; } + +.bar-group-standalone { + border-radius: 1.364rem; + -gtk-outline-radius: 1.364rem; } + +.bar-group-round { + border-radius: 10rem; + -gtk-outline-radius: 10rem; } + +.bar-group-middle { + border-radius: 0.477rem; + -gtk-outline-radius: 0.477rem; } + +.bar-group-left { + border-radius: 0.477rem; + -gtk-outline-radius: 0.477rem; + border-top-left-radius: 1.364rem; + border-bottom-left-radius: 1.364rem; } + +.bar-group-right { + border-radius: 0.477rem; + -gtk-outline-radius: 0.477rem; + border-top-right-radius: 1.364rem; + border-bottom-right-radius: 1.364rem; } + +.bar-ws-width { + min-width: 18.614rem; } + +.bar-separator { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-width: 0.341rem; + min-height: 0.341rem; + background-color: rgba(228, 231, 231, 0.3); + margin: 0rem 0.341rem; } + +.bar-clock { + font-family: 'Gabarito', 'Lexend', sans-serif; + font-size: 1.2727rem; + color: #191c1d; } + +.bar-date { + font-family: 'Gabarito', 'Lexend', sans-serif; + font-size: 1rem; + color: #191c1d; } + +.bar-ws { + min-height: 1.636rem; + min-width: 1.772rem; + font-size: 1.091rem; + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + border-top: 0.068rem solid; + border-bottom: 0.068rem solid; + border-color: transparent; + color: white; } + +.bar-ws-active-box { + transition: 300ms cubic-bezier(0.05, 0.7, 0.1, 1); } + +.bar-ws-active { + min-height: 1.5rem; + min-width: 1.5rem; + font-size: 1.091rem; + font-family: 'AR One Sans', 'Inter', 'Roboto', 'Noto Sans', sans-serif; + background-color: #8ef1ff; + color: #001f24; + border-radius: 999px; + margin: 0.068rem; } + +.bar-ws-active-middledecor { + min-width: 0.682rem; + min-height: 0.682rem; + border-radius: 9999px; + background-color: black; + margin: 0rem 0.409rem; } + +.bar-ws-occupied { + background-color: #051f23; + color: #cde7ec; + min-width: 1.772rem; + border-top: 0.068rem solid #cde7ec; + border-bottom: 0.068rem solid #cde7ec; } + +.bar-ws-occupied-left { + background-color: #051f23; + color: #cde7ec; + min-width: 1.704rem; + border-top-left-radius: 999px; + border-bottom-left-radius: 999px; + border-left: 0.068rem solid #cde7ec; + border-top: 0.068rem solid #cde7ec; + border-bottom: 0.068rem solid #cde7ec; + border-right: 0px solid transparent; } + +.bar-ws-occupied-right { + background-color: #051f23; + color: #cde7ec; + min-width: 1.704rem; + border-top-right-radius: 999px; + border-bottom-right-radius: 999px; + border-right: 0.068rem solid #cde7ec; + border-top: 0.068rem solid #cde7ec; + border-bottom: 0.068rem solid #cde7ec; + border-left: 0px solid transparent; } + +.bar-ws-occupied-left-right { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + background-color: #051f23; + color: #cde7ec; + min-width: 1.636rem; + border: 0.068rem solid #cde7ec; } + +.bar-ws-empty { + color: #191c1d; + border-color: transparent; } + +.bar-batt { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + padding: 0rem 0.341rem; + background-color: rgba(205, 231, 236, 0.7); + color: rgba(5, 31, 35, 0.7); } + +.bar-sidemodule { + min-width: 26rem; } + +.bar-batt-low { + background-color: #ba1b1b; + color: #ffdad4; } + +.bar-batt-full { + background-color: #d1e8d5; + color: #0c1f13; } + +.bar-batt-prog-low { + background-color: #ba1b1b; + color: #ffdad4; } + +.bar-batt-prog-full { + background-color: #d1e8d5; + color: #0c1f13; } + +.bar-music-playstate { + min-height: 1.770rem; + min-width: 1.770rem; + border-radius: 10rem; + margin-left: 0.273rem; + background-color: #cde7ec; + color: #051f23; } + +.bar-music-circprog { + transition: 1000ms cubic-bezier(0.1, 1, 0, 1); + margin-left: 0.273rem; + min-width: 0.068rem; + min-height: 1.770rem; + padding: 0rem; + background-color: #cde7ec; + color: #051f23; } + +.bar-music-playstate-playing { + min-height: 1.770rem; + min-width: 1.770rem; + border-radius: 10rem; + margin-left: 0.273rem; + background-color: #cde7ec; + color: #051f23; } + +.bar-music-playstate-txt { + transition: 100ms cubic-bezier(0.05, 0.7, 0.1, 1); + font-family: 'Material Symbols Rounded', 'Material Symbols Outlined', 'Material Symbols Sharp'; + font-size: 1.568rem; + margin: -0.1rem 0rem; + margin-left: 0.2rem; + margin-right: 0.17rem; } + +.bar-music-cover { + background-position: center; + background-repeat: no-repeat; + background-size: 100% auto; + min-width: 11.932rem; } + +.bar-music-extended-bg { + border-radius: 1.364rem; + min-width: 34.091rem; } + +.bar-music-extended-ctl-bg { + border-radius: 1.364rem; + background-color: rgba(30, 30, 30, 0.6); } + +.bar-music-bottom-bg { + border-radius: 1.364rem; + min-width: 34.091rem; } + +.bar-music-bottom-ctl-bg { + border-radius: 1.364rem; + background-color: rgba(30, 30, 30, 0.6); } + +.bar-music-extended-textbox { + margin: 1.023rem; } + +.bar-music-bottom-cover { + border-radius: 10rem; } + +.bar-music-hide-false { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + transition-duration: 100ms; + opacity: 1; } + +.bar-music-hide-true { + transition: 150ms cubic-bezier(0.3, 0, 0.8, 0.15); + transition-duration: 100ms; + opacity: 0; } + +.bar-music-btn { + font-size: 1.364rem; + border-radius: 10rem; + min-height: 2.591rem; + min-width: 2.591rem; } + +.bar-music-btn:hover { + background-color: rgba(128, 128, 128, 0.3); } + +.bar-prog-batt { + min-height: 0.955rem; + min-width: 0.068rem; + padding: 0rem; + border-radius: 10rem; } + .bar-prog-batt trough { + min-height: 0.954rem; + min-width: 0.068rem; + border-radius: 10rem; } + .bar-prog-batt progress { + min-height: 0.680rem; + min-width: 0.680rem; + margin: 0rem 0.137rem; + border-radius: 10rem; + background-color: rgba(5, 31, 35, 0.7); } + +.bar-prog-batt-low progress { + background-color: #ffdad4; } + +.bar-prog-batt-full progress { + background-color: #0c1f13; } + +.bar-batt-chargestate { + border-radius: 10rem; + background-color: transparent; } + +.bar-batt-chargestate-charging { + border-radius: 10rem; + min-width: 0.681rem; + min-height: 0.681rem; + background-color: rgba(5, 31, 35, 0.7); } + +.bar-batt-chargestate-low { + background-color: #ffdad4; } + +.bar-batt-chargestate-full { + background-color: #0c1f13; } + +.bar-batt-percentage { + font-size: 1rem; + margin-top: -0.068rem; + font-weight: 500; } + +.corner { + background-color: rgba(251, 253, 253, 0.7); + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; } + +.corner-black { + background-color: black; + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; } + +.bar-topdesc { + margin-top: -0.136rem; + margin-bottom: -0.341rem; + color: #5d6060; } + +.bar-space-button { + padding: 0.341rem; } + +.bar-space-button > box:first-child { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + padding: 0rem 0.682rem; } + +.bar-space-button:hover > box:first-child { + background-color: rgba(128, 128, 128, 0.3); } + +.bar-space-button:active > box:first-child { + background-color: rgba(128, 128, 128, 0.7); } + +.bar-space-button-leftmost box { + margin: 0rem 0.682rem; } + +.bar-space-area-rightmost > box { + padding-right: 2.386rem; } + +.bar-systray { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-height: 1.909rem; + min-width: 1.909rem; } + +.bar-systray-item { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-width: 1.909rem; } + +.cheatsheet-bg { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid rgba(138, 141, 141, 0.28); + border-left: 1px solid rgba(138, 141, 141, 0.28); + border-right: 1px solid rgba(178, 181, 181, 0.24); + border-bottom: 1px solid rgba(178, 181, 181, 0.24); + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; + margin-bottom: 0.682rem; + background-color: rgba(251, 253, 253, 0.7); + padding: 1.364rem; } + +.cheatsheet-key { + font-family: 'JetBrains Mono Nerd Font', 'JetBrains Mono NL', 'SpaceMono Nerd Font', monospace; + min-height: 1.364rem; + min-width: 1.364rem; + margin: 0.17rem; + padding: 0.136rem 0.205rem; + border-radius: 0.409rem; + -gtk-outline-radius: 0.409rem; + color: #006874; + border: 0.068rem solid #006874; + box-shadow: 0rem 0.136rem 0rem #006874; + font-weight: 500; } + +.cheatsheet-key-notkey { + min-height: 1.364rem; + padding: 0.136rem 0.205rem; + margin: 0.17rem; + color: #001f24; } + +.cheatsheet-closebtn { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-width: 2.386rem; + min-height: 2.386rem; } + +.cheatsheet-closebtn:hover { + background-color: #dbe4e6; } + +.cheatsheet-closebtn:active { + background-color: #acb5b7; } + +.cheatsheet-category-title { + font-family: 'Gabarito', 'Lexend', sans-serif; + font-size: 1.705rem; } + +.sidebar-right { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid rgba(138, 141, 141, 0.28); + border-left: 1px solid rgba(138, 141, 141, 0.28); + border-right: 1px solid rgba(178, 181, 181, 0.24); + border-bottom: 1px solid rgba(178, 181, 181, 0.24); + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; + background-color: rgba(251, 253, 253, 0.7); + padding: 1.023rem; } + +.sideright-show { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); } + +.sideright-hide { + margin-right: -30.682rem; + transition: 150ms cubic-bezier(0.3, 0, 0.8, 0.15); } + +.sidebar-left { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid rgba(138, 141, 141, 0.28); + border-left: 1px solid rgba(138, 141, 141, 0.28); + border-right: 1px solid rgba(178, 181, 181, 0.24); + border-bottom: 1px solid rgba(178, 181, 181, 0.24); + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; + background-color: rgba(251, 253, 253, 0.7); + padding: 1.023rem; } + +.sideleft-show { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); } + +.sideleft-hide { + margin-left: -30.682rem; + transition: 150ms cubic-bezier(0.3, 0, 0.8, 0.15); } + +.sidebar-group { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + padding: 0.341rem; + background-color: rgba(251, 253, 253, 0.3); } + +.sidebar-group-nopad { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + background-color: rgba(251, 253, 253, 0.3); } + +.sidebar-group-invisible { + padding: 0.341rem; } + +.sidebar-group-invisible-morehorizpad { + padding: 0.341rem 0.682rem; } + +.sidebar-iconbutton { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + color: #191c1d; + min-width: 2.727rem; + min-height: 2.727rem; } + +.sidebar-iconbutton:hover { + background-color: rgba(165, 191, 196, 0.7); } + +.sidebar-iconbutton:active { + background-color: rgba(125, 151, 156, 0.7); } + +.sidebar-button { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + padding: 0rem 0.818rem; + background-color: rgba(205, 231, 236, 0.7); + color: #051f23; } + +.sidebar-button-nopad { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + background-color: rgba(205, 231, 236, 0.7); + color: #051f23; } + +.sidebar-button:hover { + background-color: rgba(165, 191, 196, 0.7); } + +.sidebar-button:active { + background-color: rgba(125, 151, 156, 0.7); } + +.sidebar-button-nopad:hover { + background-color: rgba(165, 191, 196, 0.7); } + +.sidebar-button-nopad:active { + background-color: rgba(125, 151, 156, 0.7); } + +.sidebar-button-left { + border-top-left-radius: 0.818rem; + border-bottom-left-radius: 0.818rem; } + +.sidebar-button-right { + border-top-right-radius: 0.818rem; + border-bottom-right-radius: 0.955rem; } + +.sidebar-button-alone { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; } + +.sidebar-button-alone-normal { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; } + +.sidebar-button-active { + background-color: #006874; + color: #ffffff; } + +.sidebar-button-active:hover { + background-color: rgba(2, 104, 116, 0.93); } + +.sidebar-button-active:active { + background-color: rgba(9, 106, 117, 0.79); } + +.sidebar-buttons-separator { + min-width: 0.068rem; + min-height: 0.068rem; + background-color: #3f484a; } + +.sidebar-navrail { + padding: 0rem 1.159rem; } + +.sidebar-navrail-btn > box > label { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); } + +.sidebar-navrail-btn:hover > box > label:first-child { + background-color: rgba(159, 168, 170, 0.37); } + +.sidebar-navrail-btn:active > box > label:first-child { + background-color: #b4bdbf; } + +.sidebar-navrail-btn-active > box > label:first-child { + background-color: #cde7ec; + color: #051f23; } + +.sidebar-navrail-btn-active:hover > box > label:first-child { + background-color: rgba(204, 229, 234, 0.93); + color: rgba(7, 33, 37, 0.93); } + +.sidebar-sysinfo-grouppad { + padding: 1.159rem; } + +.sidebar-memory-ram-circprog { + transition: 1000ms cubic-bezier(0.1, 1, 0, 1); + min-width: 0.818rem; + min-height: 4.091rem; + padding: 0.409rem; + background-color: #cde7ec; + color: #051f23; + font-size: 0px; } + +.sidebar-memory-swap-circprog { + transition: 1000ms cubic-bezier(0.1, 1, 0, 1); + min-width: 0.818rem; + min-height: 2.255rem; + padding: 0.409rem; + margin: 0.918rem; + background-color: #cde7ec; + color: #051f23; + font-size: 0px; } + +.sidebar-cpu-circprog { + min-width: 0.818rem; + min-height: 3.409rem; + padding: 0.409rem; + background-color: #cde7ec; + color: #051f23; + transition: 1000ms cubic-bezier(0.1, 1, 0, 1); + font-size: 0px; } + +.sidebar-viewport { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; } + +.sidebar-scrollbar trough { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-width: 0.545rem; + background-color: transparent; } + +.sidebar-scrollbar slider { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-width: 0.273rem; + min-height: 2.045rem; + background-color: rgba(63, 72, 74, 0.3); } + +.sidebar-scrollbar slider:hover { + background-color: rgba(63, 72, 74, 0.44); } + +.sidebar-scrollbar slider:active { + background-color: #8d9698; } + +.sidebar-calendar-btn { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + min-height: 2.523rem; + min-width: 2.523rem; + color: #191c1d; } + +.sidebar-calendar-btn:hover { + background-color: rgba(128, 128, 128, 0.3); } + +.sidebar-calendar-btn:active { + background-color: rgba(128, 128, 128, 0.7); } + +.sidebar-calendar-btn-txt { + margin-left: -10.341rem; + margin-right: -10.341rem; } + +.sidebar-calendar-btn-today { + background-color: #006874; + color: #ffffff; } + +.sidebar-calendar-btn-today:hover { + background-color: rgba(2, 104, 116, 0.93); } + +.sidebar-calendar-btn-today:active { + background-color: rgba(9, 106, 117, 0.79); } + +.sidebar-calendar-btn-othermonth { + color: #8a8d8d; } + +.sidebar-calendar-header { + margin: 0.341rem; } + +.sidebar-calendar-monthyear-btn { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + padding: 0rem 0.682rem; + background-color: rgba(219, 228, 230, 0.3); + color: #3f484a; } + +.sidebar-calendar-monthyear-btn:hover { + background-color: rgba(183, 192, 194, 0.335); + color: #475052; } + +.sidebar-calendar-monthyear-btn:active { + background-color: #c4cdcf; + color: #565f61; } + +.sidebar-calendar-monthshift-btn { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-width: 2.045rem; + min-height: 2.045rem; + background-color: rgba(219, 228, 230, 0.3); + color: #3f484a; } + +.sidebar-calendar-monthshift-btn:hover { + background-color: rgba(183, 192, 194, 0.335); + color: #475052; } + +.sidebar-calendar-monthshift-btn:active { + background-color: #c4cdcf; + color: #565f61; } + +.sidebar-todo-selector-tab { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + transition: 0ms; + min-height: 2.5rem; + color: #191c1d; } + +.sidebar-todo-selector-tab:hover { + background-color: rgba(159, 168, 170, 0.37); } + +.sidebar-todo-selector-tab:active { + background-color: #b4bdbf; } + +.sidebar-todo-selector-tab-active > box > label { + color: #006874; } + +.sidebar-todo-selector-highlight-offset { + margin-top: -0.205rem; + margin-bottom: 0.205rem; } + +.sidebar-todo-selector-highlight { + transition: 180ms ease-in-out; + color: #006874; + padding: 0rem 2.045rem; + min-height: 0.205rem; } + +.sidebar-todo-item-action { + border-radius: 9999px; + min-width: 1.705rem; + min-height: 1.705rem; } + +.sidebar-todo-item-action:hover { + background-color: rgba(240, 242, 242, 0.3); } + +.sidebar-todo-item-action:active { + background-color: rgba(217, 219, 219, 0.3); } + +.sidebar-clipboard-item { + border-radius: 0.818rem; + min-height: 2.045rem; + padding: 0.341rem; + background-color: rgba(205, 231, 236, 0.7); + color: #051f23; } + +.sidebar-clipboard-item:hover { + background-color: rgba(185, 211, 216, 0.7); } + +.sidebar-clipboard-item:active { + background-color: rgba(165, 191, 196, 0.7); } + +.osd-bg { + min-width: 8.864rem; + min-height: 3.409rem; } + +.osd-value { + background-color: rgba(251, 253, 253, 0.7); + border-radius: 1.023rem; + padding: 0.625rem 1.023rem; + padding-top: 0.313rem; + margin: 10px; + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; } + +.osd-progress { + min-height: 0.955rem; + min-width: 0.068rem; + padding: 0rem; + border-radius: 10rem; + transition: 200ms cubic-bezier(0.1, 1, 0, 1); } + .osd-progress trough { + min-height: 0.954rem; + min-width: 0.068rem; + border-radius: 10rem; + background-color: #001f24; } + .osd-progress progress { + transition: 200ms cubic-bezier(0.1, 1, 0, 1); + min-height: 0.680rem; + min-width: 0.680rem; + margin: 0rem 0.137rem; + border-radius: 10rem; + background-color: #8ef1ff; } + +.osd-icon { + color: #001f24; } + +.osd-label { + font-size: 1.023rem; + font-weight: 500; + color: #191c1d; + margin-top: 0.341rem; } + +.osd-value-txt { + font-family: 'Gabarito', 'Lexend', sans-serif; + font-size: 1.688rem; + font-weight: 500; + color: #191c1d; } + +.osd-notifs { + padding-top: 0.313rem; } + +.overview-search-box { + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid rgba(138, 141, 141, 0.28); + border-left: 1px solid rgba(138, 141, 141, 0.28); + border-right: 1px solid rgba(178, 181, 181, 0.24); + border-bottom: 1px solid rgba(178, 181, 181, 0.24); + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; + min-width: 13.636rem; + min-height: 3.409rem; + padding: 0rem 1.364rem; + padding-right: 2.864rem; + background-color: rgba(251, 253, 253, 0.7); + color: #191c1d; + caret-color: transparent; } + .overview-search-box selection { + background-color: #4a6266; + color: #ffffff; } + +.overview-search-box-extended { + min-width: 25.909rem; + caret-color: #051f23; } + +.overview-search-prompt { + color: #5d6060; } + +.overview-search-icon { + margin: 0rem 1.023rem; } + +.overview-search-prompt-box { + margin-left: -18.545rem; + margin-right: 8px; } + +.overview-search-icon-box { + margin-left: -18.545rem; + margin-right: 8px; } + +.overview-search-results { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid rgba(138, 141, 141, 0.28); + border-left: 1px solid rgba(138, 141, 141, 0.28); + border-right: 1px solid rgba(178, 181, 181, 0.24); + border-bottom: 1px solid rgba(178, 181, 181, 0.24); + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; + min-width: 28.773rem; + padding: 0.682rem; + background-color: rgba(251, 253, 253, 0.7); + color: #191c1d; } + +.overview-search-results-icon { + margin: 0rem 0.682rem; + font-size: 2.386rem; } + +.overview-search-results-txt { + margin-right: 0.682rem; } + +.overview-search-results-txt-cmd { + margin-right: 0.682rem; + font-family: 'JetBrains Mono Nerd Font', 'JetBrains Mono NL', 'SpaceMono Nerd Font', monospace; + font-size: 1.227rem; } + +.overview-search-result-btn { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + padding: 0.341rem; + min-width: 2.386rem; + min-height: 2.386rem; + caret-color: transparent; } + +.overview-search-result-btn:focus, +.overview-search-result-btn:hover { + background-color: rgba(128, 128, 128, 0.3); } + +.overview-search-result-btn:active { + background-color: rgba(128, 128, 128, 0.7); } + +.overview-tasks { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid rgba(138, 141, 141, 0.28); + border-left: 1px solid rgba(138, 141, 141, 0.28); + border-right: 1px solid rgba(178, 181, 181, 0.24); + border-bottom: 1px solid rgba(178, 181, 181, 0.24); + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; + padding: 0.341rem; + background-color: rgba(251, 253, 253, 0.7); + color: #191c1d; } + +.overview-tasks-workspace { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + margin: 0.341rem; + background-color: rgba(232, 234, 234, 0.207); } + +.overview-tasks-window { + border-radius: 1.159rem; + -gtk-outline-radius: 1.159rem; + transition: 200ms cubic-bezier(0.05, 0.7, 0.1, 1); + background-color: rgba(205, 231, 236, 0.4); + color: #051f23; + border: 0.068rem solid rgba(5, 31, 35, 0.07); } + +.overview-tasks-window:hover { + background-color: rgba(169, 209, 215, 0.43); } + +.overview-tasks-window:focus { + background-color: rgba(169, 209, 215, 0.43); } + +.overview-tasks-window:active { + background-color: rgba(142, 192, 199, 0.46); } + +.overview-tasks-window-selected { + background-color: rgba(142, 192, 199, 0.46); } + +.overview-tasks-window-dragging { + opacity: 0.2; } + +.osk-window { + transition: 170ms cubic-bezier(0.05, 0.7, 0.1, 1); + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + border-top: 1px solid rgba(138, 141, 141, 0.28); + border-left: 1px solid rgba(138, 141, 141, 0.28); + border-right: 1px solid rgba(178, 181, 181, 0.24); + border-bottom: 1px solid rgba(178, 181, 181, 0.24); + box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); + margin: 7px; + background-color: rgba(251, 253, 253, 0.7); + padding: 1.023rem; } + +.osk-show { + transition: 170ms cubic-bezier(0.05, 0.7, 0.1, 1); } + +.osk-hide { + margin-top: 30.682rem; + margin-bottom: -30.682rem; + transition: 100ms cubic-bezier(0.3, 0, 0.8, 0.15); } + +.osk-key { + border-radius: 0.682rem; + background-color: rgba(219, 228, 230, 0.3); + color: #3f484a; + padding: 0.188rem; + font-weight: 500; + font-size: 1.091rem; } + +.osk-key:hover { + background-color: rgba(203, 212, 214, 0.3); } + +.osk-key:active { + background-color: rgba(172, 181, 183, 0.3); + font-size: 1.091rem; } + +.osk-key-active { + background-color: rgba(172, 181, 183, 0.3); } + +.osk-key-normal { + min-width: 2.5rem; + min-height: 2.5rem; } + +.osk-key-fn { + min-width: 2.5125rem; + min-height: 1.25rem; } + +.osk-key-tab { + min-width: 4rem; + min-height: 2.5rem; } + +.osk-key-caps { + min-width: 4.75rem; + min-height: 2.5rem; } + +.osk-key-shift { + min-width: 6.25rem; + min-height: 2.5rem; } + +.osk-key-control { + min-width: 3.25rem; + min-height: 2.5rem; } + +.osk-control-button { + border-radius: 0.682rem; + background-color: rgba(219, 228, 230, 0.3); + color: #3f484a; + font-weight: 500; + font-size: 1.091rem; + padding: 0.682rem; } + +.osk-control-button:hover { + background-color: rgba(203, 212, 214, 0.3); } + +.osk-control-button:active { + background-color: rgba(172, 181, 183, 0.3); + font-size: 1.091rem; } + +.session-bg { + margin-top: -2.727rem; + background-color: rgba(251, 253, 253, 0.44); } + +.session-button { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + min-width: 8.182rem; + min-height: 8.182rem; + background-color: #dbe4e6; + color: #3f484a; + font-size: 3rem; } + +.session-button-focused { + background-color: #cde7ec; + color: #051f23; } + +.session-button-desc { + background-color: #ebf1f2; + color: #2c3234; + border-bottom-left-radius: 1.705rem; + border-bottom-right-radius: 1.705rem; + padding: 0.205rem 0.341rem; + font-weight: 700; } + +.session-button-cancel { + border-radius: 1.705rem; + -gtk-outline-radius: 1.705rem; + min-width: 8.182rem; + min-height: 5.455rem; + background-color: #dbe4e6; + color: #3f484a; + font-size: 3rem; } + +.notif-low { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + background-color: rgba(219, 228, 230, 0.65); + color: #3f484a; + padding: 0.818rem; + padding-right: 1.363rem; } + +.notif-normal { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + background-color: rgba(219, 228, 230, 0.65); + color: #3f484a; + padding: 0.818rem; + padding-right: 1.363rem; } + +.notif-critical { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + background-color: #ba1b1b; + color: #ffffff; + padding: 0.818rem; + padding-right: 1.363rem; } + +.popup-notif-low { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + min-width: 30.682rem; + background-color: rgba(251, 253, 253, 0.7); + color: #3f484a; + padding: 0.818rem; + padding-right: 1.363rem; } + +.popup-notif-normal { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + min-width: 30.682rem; + background-color: rgba(251, 253, 253, 0.7); + color: #3f484a; + padding: 0.818rem; + padding-right: 1.363rem; } + +.popup-notif-critical { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + min-width: 30.682rem; + background-color: #ba1b1b; + color: #ffffff; + padding: 0.818rem; + padding-right: 1.363rem; } + +.notif-body-low { + color: #727b7d; } + +.notif-body-normal { + color: #727b7d; } + +.notif-body-critical { + color: #e8b4b4; } + +.notif-icon { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + min-width: 3.409rem; + min-height: 3.409rem; } + +.notif-icon-material { + background-color: rgba(205, 231, 236, 0.7); + color: #051f23; } + +.notif-icon-material-low { + background-color: rgba(205, 231, 236, 0.7); + color: #051f23; } + +.notif-icon-material-normal { + background-color: rgba(205, 231, 236, 0.7); + color: #051f23; } + +.notif-icon-material-critical { + background-color: rgba(255, 218, 212, 0.7); + color: #410001; } + +.notif-close-btn { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + padding: 0rem 0.136rem; } + +.notif-close-btn:hover { + background: rgba(128, 128, 128, 0.3); } + +.notif-close-btn:active { + background: rgba(128, 128, 128, 0.7); } + +.notif-closeall-btn { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + padding: 0.341rem 0.341rem; } + +.notif-closeall-btn:hover { + background-color: rgba(128, 128, 128, 0.3); } + +.notif-closeall-btn:active { + background-color: rgba(128, 128, 128, 0.7); } + +.osd-notif { + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; + background-color: rgba(251, 253, 253, 0.6); + min-width: 30.682rem; } + +.growingRadial { + transition: 300ms cubic-bezier(0.2, 0, 0, 1); } + +.fadingRadial { + transition: 50ms cubic-bezier(0.2, 0, 0, 1); } diff --git a/desktops/hyprland/ags/windows/bar.js b/desktops/hyprland/ags/windows/bar.js new file mode 100644 index 0000000..72a6a3a --- /dev/null +++ b/desktops/hyprland/ags/windows/bar.js @@ -0,0 +1,53 @@ +const { Gdk, Gtk } = imports.gi; +import { App, Service, Utils, Widget } from '../imports.js'; +const { execAsync, exec } = Utils; + +import { ModuleWorkspaces } from "../modules/workspaces.js"; +import { ModuleMusic } from "../modules/music.js"; +import { ModuleSystem } from "../modules/system.js"; +import { ModuleLeftSpace } from "../modules/leftspace.js"; +import { ModuleRightSpace } from "../modules/rightspace.js"; +import { RoundedCorner } from "../modules/lib/roundedcorner.js"; + +const left = Widget.Box({ + className: 'bar-sidemodule', + children: [ModuleMusic()], +}); + +const center = Widget.Box({ + children: [ + RoundedCorner('topright', { className: 'corner-bar-group' }), + ModuleWorkspaces(), + RoundedCorner('topleft', { className: 'corner-bar-group' }), + ], +}); + +const right = Widget.Box({ + className: 'bar-sidemodule', + children: [ModuleSystem()], +}); + +export default () => Widget.Window({ + name: 'bar', + anchor: ['top', 'left', 'right'], + exclusive: true, + visible: true, + child: Widget.CenterBox({ + className: 'bar-bg', + startWidget: ModuleLeftSpace(), + centerWidget: Widget.Box({ + className: 'spacing-h--20', + children: [ + left, + center, + right, + ] + }), + endWidget: ModuleRightSpace(), + setup: (self) => { + const styleContext = self.get_style_context(); + const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); + // execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print); + } + }), +}); diff --git a/desktops/hyprland/ags/windows/cheatsheet.js b/desktops/hyprland/ags/windows/cheatsheet.js new file mode 100644 index 0000000..9783d40 --- /dev/null +++ b/desktops/hyprland/ags/windows/cheatsheet.js @@ -0,0 +1,88 @@ +const { Gdk, Gtk } = imports.gi; +import { Service, Widget } from '../imports.js'; +import { Keybinds } from "../modules/keybinds.js"; +import { setupCursorHover } from "../modules/lib/cursorhover.js"; + +const cheatsheetHeader = () => Widget.CenterBox({ + vertical: false, + startWidget: Widget.Box({}), + centerWidget: Widget.Box({ + vertical: true, + className: "spacing-h-15", + children: [ + Widget.Box({ + halign: 'center', + className: 'spacing-h-5', + children: [ + Widget.Label({ + halign: 'center', + style: 'margin-right: 0.682rem;', + className: 'txt-title txt', + label: 'Cheat sheet', + }), + Widget.Label({ + valign: 'center', + className: "cheatsheet-key txt-small", + label: "", + }), + Widget.Label({ + valign: 'center', + className: "cheatsheet-key-notkey txt-small", + label: "+", + }), + Widget.Label({ + valign: 'center', + className: "cheatsheet-key txt-small", + label: "/", + }) + ] + }), + Widget.Label({ + justify: Gtk.Justification.CENTER, + className: 'txt-small txt', + label: 'Sheet data stored in ~/.config/ags/data/keybinds.js\nChange keybinds in ~/.config/hypr/keybinds.conf' + }), + ] + }), + endWidget: Widget.Button({ + valign: 'start', + halign: 'end', + className: "cheatsheet-closebtn icon-material txt txt-hugeass", + onClicked: () => { + App.toggleWindow('cheatsheet'); + }, + child: Widget.Label({ + className: 'icon-material txt txt-hugeass', + label: 'close' + }), + setup: (button) => setupCursorHover(button), + }), +}); + +const clickOutsideToClose = Widget.EventBox({ + onPrimaryClick: () => App.closeWindow('cheatsheet'), + onSecondaryClick: () => App.closeWindow('cheatsheet'), + onMiddleClick: () => App.closeWindow('cheatsheet'), +}); + +export default () => Widget.Window({ + name: 'cheatsheet', + exclusive: false, + focusable: true, + popup: true, + visible: false, + child: Widget.Box({ + vertical: true, + children: [ + clickOutsideToClose, + Widget.Box({ + vertical: true, + className: "cheatsheet-bg spacing-v-15", + children: [ + cheatsheetHeader(), + Keybinds(), + ] + }), + ], + }) +}); diff --git a/desktops/hyprland/ags/windows/corners.js b/desktops/hyprland/ags/windows/corners.js new file mode 100644 index 0000000..da29111 --- /dev/null +++ b/desktops/hyprland/ags/windows/corners.js @@ -0,0 +1,36 @@ +import { Widget } from '../imports.js'; +import { RoundedCorner } from "../modules/lib/roundedcorner.js"; + +export const CornerTopleft = () => Widget.Window({ + name: 'cornertl', + layer: 'top', + anchor: ['top', 'left'], + exclusive: false, + visible: true, + child: RoundedCorner('topleft', { className: 'corner', }), +}); +export const CornerTopright = () => Widget.Window({ + name: 'cornertr', + layer: 'top', + anchor: ['top', 'right'], + exclusive: false, + visible: true, + child: RoundedCorner('topright', { className: 'corner', }), +}); +export const CornerBottomleft = () => Widget.Window({ + name: 'cornerbl', + layer: 'top', + anchor: ['bottom', 'left'], + exclusive: false, + visible: true, + child: RoundedCorner('bottomleft', { className: 'corner-black', }), +}); +export const CornerBottomright = () => Widget.Window({ + name: 'cornerbr', + layer: 'top', + anchor: ['bottom', 'right'], + exclusive: false, + visible: true, + child: RoundedCorner('bottomright', { className: 'corner-black', }), +}); + diff --git a/desktops/hyprland/ags/windows/lib/popupwindow.js b/desktops/hyprland/ags/windows/lib/popupwindow.js new file mode 100644 index 0000000..a242ae0 --- /dev/null +++ b/desktops/hyprland/ags/windows/lib/popupwindow.js @@ -0,0 +1,27 @@ +import { App, Widget } from '../../imports.js'; +const { Revealer, Box, Window } = Widget; + + +export default ({ + name, + child, + showClassName, + hideClassName, + ...props +}) => Window({ + name, + popup: true, + visible: false, + layer: 'overlay', + ...props, + + child: Box({ + className: `${showClassName} ${hideClassName}`, + connections: [[App, (self, currentName, visible) => { + if (currentName === name) { + self.toggleClassName(hideClassName, !visible); + } + }]], + child: child, + }), +}); \ No newline at end of file diff --git a/desktops/hyprland/ags/windows/osd.js b/desktops/hyprland/ags/windows/osd.js new file mode 100644 index 0000000..662b29e --- /dev/null +++ b/desktops/hyprland/ags/windows/osd.js @@ -0,0 +1,12 @@ +import { Widget } from '../imports.js'; +import Osd from "../modules/onscreendisplay.js"; + +export default (monitor) => Widget.Window({ + name: `indicator${monitor}`, + monitor, + className: 'indicator', + layer: 'overlay', + visible: true, + anchor: ['top'], + child: Osd(), +}); diff --git a/desktops/hyprland/ags/windows/osk.js b/desktops/hyprland/ags/windows/osk.js new file mode 100644 index 0000000..f2ad6f9 --- /dev/null +++ b/desktops/hyprland/ags/windows/osk.js @@ -0,0 +1,12 @@ +const { Gdk, Gtk } = imports.gi; +import { Widget } from '../imports.js'; +import PopupWindow from './lib/popupwindow.js'; +import OnScreenKeyboard from "../modules/onscreenkeyboard.js"; + +export default () => PopupWindow({ + anchor: ['bottom'], + name: 'osk', + showClassName: 'osk-show', + hideClassName: 'osk-hide', + child: OnScreenKeyboard(), +}); diff --git a/desktops/hyprland/ags/windows/overview.js b/desktops/hyprland/ags/windows/overview.js new file mode 100644 index 0000000..1a5464c --- /dev/null +++ b/desktops/hyprland/ags/windows/overview.js @@ -0,0 +1,18 @@ +import { Widget } from '../imports.js'; +import { SearchAndWindows } from "../modules/overview.js"; + +export default () => Widget.Window({ + name: 'overview', + exclusive: false, + focusable: true, + popup: true, + visible: false, + anchor: ['top'], + layer: 'overlay', + child: Widget.Box({ + vertical: true, + children: [ + SearchAndWindows(), + ] + }), +}) diff --git a/desktops/hyprland/ags/windows/session.js b/desktops/hyprland/ags/windows/session.js new file mode 100644 index 0000000..e1bcd91 --- /dev/null +++ b/desktops/hyprland/ags/windows/session.js @@ -0,0 +1,13 @@ +const { Gdk, Gtk } = imports.gi; +import { Widget } from '../imports.js'; +import SessionScreen from "../modules/sessionscreen.js"; + +export default () => Widget.Window({ // On-screen keyboard + name: 'session', + popup: true, + visible: false, + focusable: true, + layer: 'overlay', + // anchor: ['top', 'bottom', 'left', 'right'], + child: SessionScreen(), +}) \ No newline at end of file diff --git a/desktops/hyprland/ags/windows/sideleft.js b/desktops/hyprland/ags/windows/sideleft.js new file mode 100644 index 0000000..27199f7 --- /dev/null +++ b/desktops/hyprland/ags/windows/sideleft.js @@ -0,0 +1,11 @@ +import PopupWindow from './lib/popupwindow.js'; +import SidebarLeft from "../modules/sideleft.js"; + +export default () => PopupWindow({ + focusable: true, + anchor: ['left', 'bottom'], + name: 'sideleft', + showClassName: 'sideleft-show', + hideClassName: 'sideleft-hide', + child: SidebarLeft(), +}); diff --git a/desktops/hyprland/ags/windows/sideright.js b/desktops/hyprland/ags/windows/sideright.js new file mode 100644 index 0000000..f308d25 --- /dev/null +++ b/desktops/hyprland/ags/windows/sideright.js @@ -0,0 +1,12 @@ +import { Widget } from '../imports.js'; +import PopupWindow from './lib/popupwindow.js'; +import SidebarRight from "../modules/sideright.js"; + +export default () => PopupWindow({ + focusable: true, + anchor: ['right', 'top', 'bottom'], + name: 'sideright', + showClassName: 'sideright-show', + hideClassName: 'sideright-hide', + child: SidebarRight(), +}); diff --git a/desktops/hyprland/decoration.nix b/desktops/hyprland/decoration.nix new file mode 100644 index 0000000..dc75c12 --- /dev/null +++ b/desktops/hyprland/decoration.nix @@ -0,0 +1,18 @@ +{ + rounding = 20; + blur = { + enabled = true; + xray = true; + special = false; + + size = 6; + passes = 4; + new_optimizations = "on"; + }; + + drop_shadow = true; + shadow_range = 15; + shadow_offset = "0 2"; + shadow_render_power = 6; + "col.shadow" = "rgba(00000044)"; +} diff --git a/desktops/hyprland/foot.nix b/desktops/hyprland/foot.nix new file mode 100644 index 0000000..622f516 --- /dev/null +++ b/desktops/hyprland/foot.nix @@ -0,0 +1,45 @@ +{ + enable = true; + settings = { + main = { + shell = "fish"; + term = "foot"; + title = "Terminal"; + font = "JetBrainsMono NerdFont:size=12"; + pad = "25x25"; + }; + cursor = { + style = "beam"; + color = "191c1e e1e2e5"; + beam-thickness = 1.5; + }; + colors = { + alpha=1; + background = "191c1e"; + foreground = "e1e2e5"; + regular0 = "191c1e"; + regular1 = "ffb4a9"; + regular2 = "00668b"; + regular3 = "c3e7ff"; + regular4 = "c3e7ff"; + regular5 = "d1e5f4"; + regular6 = "79d0ff"; + regular7 = "c0c7cd"; + bright0 = "191c1e"; + bright1 = "ffb4a9"; + bright2 = "00668b"; + bright3 = "c3e7ff"; + bright4 = "c3e7ff"; + bright5 = "d1e5f4"; + bright6 = "79d0ff"; + bright7 = "c0c7cd"; + }; + key-bindings = { + scrollback-up-page = "Page_Up"; + scrollback-down-page = "Page_Down"; + clipboard-copy = "Control+c"; + clipboard-paste = "Control+v"; + search-start = "Control+f"; + }; + }; +} diff --git a/desktops/hyprland/fuzzel.nix b/desktops/hyprland/fuzzel.nix new file mode 100644 index 0000000..0d5e854 --- /dev/null +++ b/desktops/hyprland/fuzzel.nix @@ -0,0 +1,25 @@ +{ + enable = true; + settings = { + main = { + font = "Lexend"; + terminal = "foot -e"; + prompt = ">> "; + layer = "overlay"; + }; + colors = { + background = "191c1ecc"; + text = "e1e2e5ff"; + selection = "41484dff"; + selection-text = "c0c7cdff"; + border = "41484dff"; + match = "79d0ffff"; + selection-match = "79d0ffff"; + }; + border = { + radius = 17; + width = 2; + }; + dmenu.exit-immediately-if-empty = "yes"; + }; +} diff --git a/desktops/hyprland/hypr/shaders/bluefilter.frag b/desktops/hyprland/hypr/shaders/bluefilter.frag new file mode 100755 index 0000000..5469af9 --- /dev/null +++ b/desktops/hyprland/hypr/shaders/bluefilter.frag @@ -0,0 +1,20 @@ +// vim: set ft=glsl: +// blue light filter shader +// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux + +precision mediump float; +varying vec2 v_texcoord; +uniform sampler2D tex; + +void main() { + + vec4 pixColor = texture2D(tex, v_texcoord); + + // green + pixColor[1] *= 0.855; + + // blue + pixColor[2] *= 0.725; + + gl_FragColor = pixColor; +} diff --git a/desktops/hyprland/hypr/shaders/blur.frag b/desktops/hyprland/hypr/shaders/blur.frag new file mode 100755 index 0000000..700dfb2 --- /dev/null +++ b/desktops/hyprland/hypr/shaders/blur.frag @@ -0,0 +1,39 @@ +precision mediump float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; +uniform float alpha; + +uniform vec2 topLeft; +uniform vec2 fullSize; +uniform float radius; + +uniform int discardOpaque; +uniform int discardAlpha; +uniform float discardAlphaValue; + +uniform int applyTint; +uniform vec3 tint; + +uniform int primitiveMultisample; + +void main() { + + vec4 pixColor = texture2D(tex, v_texcoord); + + if (discardOpaque == 1 && pixColor[3] * alpha == 1.0) + discard; + + if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue) + discard; + + if (applyTint == 1) { + pixColor[0] = pixColor[0] * tint[0]; + pixColor[1] = pixColor[1] * tint[1]; + pixColor[2] = pixColor[2] * tint[2]; + } + + )#" + + ROUNDED_SHADER_FUNC("pixColor") + R"#( + + gl_FragColor = pixColor * alpha; +} \ No newline at end of file diff --git a/desktops/hyprland/hypr/shaders/chromatic_abberation.frag b/desktops/hyprland/hypr/shaders/chromatic_abberation.frag new file mode 100755 index 0000000..5389241 --- /dev/null +++ b/desktops/hyprland/hypr/shaders/chromatic_abberation.frag @@ -0,0 +1,24 @@ +// vim: set ft=glsl: + +precision highp float; +varying highp vec2 v_texcoord; +uniform highp sampler2D tex; + +#define STRENGTH 0.0027 + +void main() { + vec2 center = vec2(0.5, 0.5); + vec2 offset = (v_texcoord - center) * STRENGTH; + + float rSquared = dot(offset, offset); + float distortion = 1.0 + 1.0 * rSquared; + vec2 distortedOffset = offset * distortion; + + vec2 redOffset = vec2(distortedOffset.x, distortedOffset.y); + vec2 blueOffset = vec2(distortedOffset.x, distortedOffset.y); + + vec4 redColor = texture2D(tex, v_texcoord + redOffset); + vec4 blueColor = texture2D(tex, v_texcoord + blueOffset); + + gl_FragColor = vec4(redColor.r, texture2D(tex, v_texcoord).g, blueColor.b, 1.0); +} diff --git a/desktops/hyprland/hypr/shaders/crt.frag b/desktops/hyprland/hypr/shaders/crt.frag new file mode 100755 index 0000000..a37ff9f --- /dev/null +++ b/desktops/hyprland/hypr/shaders/crt.frag @@ -0,0 +1,511 @@ +#version 100 +precision highp float; +varying highp vec2 v_texcoord; +varying highp vec3 v_pos; +uniform highp sampler2D tex; +uniform lowp float time; + +#define BORDER_COLOR vec4(vec3(0.0, 0.0, 0.0), 1.0) // black border +#define BORDER_RADIUS 1.0 // larger vignette radius +#define BORDER_SIZE 0.01 // small border size +#define CHROMATIC_ABERRATION_STRENGTH 0.00 +#define DENOISE_INTENSITY 0.0001 // +#define DISTORTION_AMOUNT 0.00 // moderate distortion amount +#define HDR_BLOOM 0.75 // bloom intensity +#define HDR_BRIGHTNESS 0.011 // brightness +#define HDR_CONTRAST 0.011 // contrast +#define HDR_SATURATION 1.0// saturation +#define LENS_DISTORTION_AMOUNT 0.0 +#define NOISE_THRESHOLD 0.0001 +#define PHOSPHOR_BLUR_AMOUNT 0.77 // Amount of blur for phosphor glow +#define PHOSPHOR_GLOW_AMOUNT 0.77 // Amount of phosphor glow +#define SAMPLING_RADIUS 0.0001 +#define SCANLINE_FREQUENCY 540.0 +#define SCANLINE_THICKNESS 0.0507 +#define SCANLINE_TIME time * 471.24 +#define SHARPNESS 0.25 +#define SUPERSAMPLING_SAMPLES 16.0 +#define VIGNETTE_RADIUS 0.0 // larger vignette radius +#define PI 3.14159265359 +#define TWOPI 6.28318530718 + +vec2 applyBarrelDistortion(vec2 coord, float amt) { + vec2 p = coord.xy / vec2(1.0); + vec2 v = p * 2.0 - vec2(1.0); + float r = dot(v, v); + float k = 1.0 + pow(r, 2.0) * pow(amt, 2.0); + vec2 result = v * k; + return vec2(0.5, 0.5) + 0.5 * result.xy; +} + +vec4 applyColorCorrection(vec4 color) { + color.rgb *= vec3(1.0, 0.79, 0.89); + return vec4(color.rgb, 1.0); +} + +vec4 applyBorder(vec2 tc, vec4 color, float borderSize, vec4 borderColor) { + float dist_x = min(tc.x, 1.0 - tc.x); + float dist_y = min(tc.y, 1.0 - tc.y); + float dist = min(dist_x, dist_y) * -1.0; + float border = smoothstep(borderSize, 0.0, dist); + border += smoothstep(borderSize, 0.0, dist); + return mix(color, borderColor, border); +} + +vec4 applyFakeHDR(vec4 color, float brightness, float contrast, float saturation, float bloom) { + color.rgb = (color.rgb - vec3(0.5)) * exp2(brightness) + vec3(0.5); + vec3 crtfactor = vec3(1.05, 0.92, 1.0); + color.rgb = pow(color.rgb, crtfactor); + // // NTSC + // vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721); + + // // BT.709 + // vec3 lumCoeff = vec3(0.299, 0.587, 0.114); + + // BT.2020 + vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593); + + // // Warm NTSC + // vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865); + + float luminance = dot(color.rgb, lumCoeff); + luminance = pow(luminance, 2.2); + color.rgb = mix(vec3(luminance), color.rgb, saturation); + color.rgb = mix(color.rgb, vec3(1.0), pow(max(0.0, luminance - 1.0 + bloom), 4.0)); + return color; +} + +vec4 applyVignette(vec4 color) { + vec2 center = vec2(0.5, 0.5); // center of screen + float radius = VIGNETTE_RADIUS; // radius of vignette effect + float softness = 1.0; // softness of vignette effect + float intensity = 0.7; // intensity of vignette effect + vec2 offset = v_texcoord - center; // offset from center of screen + float distance = length(offset); // distance from center of screen + float alpha = smoothstep(radius, radius - radius * softness, distance) * intensity; // calculate alpha value for vignette effect + return mix(vec4(0.0, 0.0, 0.0, alpha), color, alpha); // mix black with color using calculated alpha value +} + +vec4 applyPhosphorGlow(vec2 tc, vec4 color, sampler2D tex) { + // Calculate average color value of the texture + vec4 texelColor = color; + float averageColor = (texelColor.r + texelColor.g + texelColor.b) / 3.0; + + // Determine brightness-dependent color factor + float factor = mix( + mix(0.09, + mix(0.005, 0.0075, (averageColor - 0.1) / 0.1), + step(0.01, averageColor)), 0.0005, + step(0.02, averageColor)); + // Apply phosphor glow effect + vec4 sum = vec4(0.0); + vec4 pixels[9]; + pixels[0] = texture2D(tex, tc - vec2(0.001, 0.001)); + pixels[1] = texture2D(tex, tc - vec2(0.001, 0.0)); + pixels[2] = texture2D(tex, tc - vec2(0.001, -0.001)); + pixels[3] = texture2D(tex, tc - vec2(0.0, 0.001)); + pixels[4] = texture2D(tex, tc); + pixels[5] = texture2D(tex, tc + vec2(0.001, 0.001)); + pixels[6] = texture2D(tex, tc + vec2(0.001, 0.0)); + pixels[7] = texture2D(tex, tc + vec2(0.001, -0.001)); + pixels[8] = texture2D(tex, tc + vec2(0.0, 0.001)); + +// Perform operations on input pixels in parallel + sum = pixels[0] + + pixels[1] + + pixels[2] + + pixels[3] + + pixels[4] + + pixels[5] + + pixels[6] + + pixels[7] + + pixels[8]; + sum /= 9.0; + sum += texture2D(tex, tc - vec2(0.01, 0.01)) * 0.001; + sum += texture2D(tex, tc - vec2(0.0, 0.01)) * 0.001; + sum += texture2D(tex, tc - vec2(-0.01, 0.01)) * 0.001; + sum += texture2D(tex, tc - vec2(0.01, 0.0)) * 0.001; + sum += color * PHOSPHOR_BLUR_AMOUNT; + sum += texture2D(tex, tc - vec2(-0.01, 0.0)) * 0.001; + sum += texture2D(tex, tc - vec2(0.01, -0.01)) * 0.001; + sum += texture2D(tex, tc - vec2(0.0, -0.01)) * 0.001; + sum += texture2D(tex, tc - vec2(-0.01, -0.01)) * 0.001; + sum *= PHOSPHOR_GLOW_AMOUNT; + + // Initialize sum_sum_factor to zero + vec4 sum_sum_factor = vec4(0.0); + // Compute sum_j for i = -1 + vec4 sum_j = vec4(0.0); + sum_j += texture2D(tex, tc + vec2(-1, -1) * 0.01); + sum_j += texture2D(tex, tc + vec2(0, -1) * 0.01); + sum_j += texture2D(tex, tc + vec2(1, -1) * 0.01); + sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); + sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); + sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); + sum_sum_factor += sum_j * vec4(0.011); + + // Compute sum_j for i = 0 + sum_j = vec4(0.0); + sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); + sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); + sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); + sum_sum_factor += sum_j * vec4(0.011); + + // Compute sum_j for i = 1 + sum_j = vec4(0.0); + sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); + sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); + sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); + sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); + sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); + sum_sum_factor += sum_j * vec4(0.011); + color += mix(sum_sum_factor * sum_sum_factor * vec4(factor), sum, 0.5); + return color; +} + +vec4 applyAdaptiveSharpen(vec2 tc, vec4 color, sampler2D tex) { + vec4 color_tl = texture2D(tex, tc + vec2(-1.0, -1.0) * 0.5 / 2160.0); + vec4 color_tr = texture2D(tex, tc + vec2(1.0, -1.0) * 0.5 / 2160.0); + vec4 color_bl = texture2D(tex, tc + vec2(-1.0, 1.0) * 0.5 / 2160.0); + vec4 color_br = texture2D(tex, tc + vec2(1.0, 1.0) * 0.5 / 2160.0); + float sharpness = SHARPNESS; + vec3 color_no_alpha = color.rgb; + vec3 color_tl_no_alpha = color_tl.rgb; + vec3 color_tr_no_alpha = color_tr.rgb; + vec3 color_bl_no_alpha = color_bl.rgb; + vec3 color_br_no_alpha = color_br.rgb; + float delta = (dot(color_no_alpha, vec3(0.333333)) + dot(color_tl_no_alpha, vec3(0.333333)) + dot(color_tr_no_alpha, vec3(0.333333)) + dot(color_bl_no_alpha, vec3(0.333333)) + dot(color_br_no_alpha, vec3(0.333333))) * 0.2 - dot(color_no_alpha, vec3(0.333333)); + vec3 sharp_color_no_alpha = color_no_alpha + min(vec3(0.0), vec3(delta * sharpness)); + vec4 sharp_color = vec4(sharp_color_no_alpha, color.a); + return sharp_color; +} + +vec4 applyScanlines(vec2 tc, vec4 color) { + float scanline = (cos(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME) * + sin(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME)) * SCANLINE_THICKNESS; + float alpha = clamp(1.0 - abs(scanline), 0.0, 1.0); + return vec4(color.rgb * alpha, color.a); +} + +vec4 applyChromaticAberration(vec2 uv, vec4 color) { + vec2 center = vec2(0.5, 0.5); // center of the screen + vec2 offset = (uv - center) * CHROMATIC_ABERRATION_STRENGTH; // calculate the offset from the center + + // apply lens distortion + float rSquared = dot(offset, offset); + float distortion = 1.0 + LENS_DISTORTION_AMOUNT * rSquared; + vec2 distortedOffset = offset * distortion; + + // apply chromatic aberration + vec2 redOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00); + vec2 blueOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00); + + vec4 redColor = texture2D(tex, uv + redOffset); + vec4 blueColor = texture2D(tex, uv + blueOffset); + + vec4 result = vec4(redColor.r, color.g, blueColor.b, color.a); + + return result; +} + +vec4 reduceGlare(vec4 color) { + // Calculate the intensity of the color by taking the average of the RGB components + float intensity = (color.r + color.g + color.b) / 3.0; + // Set the maximum intensity that can be considered for glare + float maxIntensity = 0.98; + // Use smoothstep to create a smooth transition from no glare to full glare + // based on the intensity of the color and the maximum intensity + float glareIntensity = smoothstep(maxIntensity - 0.02, maxIntensity, intensity); + // Set the amount of glare to apply to the color + float glareAmount = 0.02; + // Mix the original color with the reduced color that has glare applied to it + vec3 reducedColor = mix(color.rgb, vec3(glareIntensity), glareAmount); + // Return the reduced color with the original alpha value + return vec4(reducedColor, color.a); +} + +// Apply a fake HDR effect to the input color. +// Parameters: +// - inputColor: the color to apply the effect to. +// - brightness: the brightness of the image. Should be a value between 0 and 1. +// - contrast: the contrast of the image. Should be a value between 0 and 1. +// - saturation: the saturation of the image. Should be a value between 0 and 2. +// - bloom: the intensity of the bloom effect. Should be a value between 0 and 1. +vec4 applyFakeHDREffect(vec4 inputColor, float brightness, float contrast, float saturation, float bloom) { + const float minBrightness = 0.0; + const float maxBrightness = 1.0; + const float minContrast = 0.0; + const float maxContrast = 1.0; + const float minSaturation = 0.0; + const float maxSaturation = 2.0; + const float minBloom = 0.0; + const float maxBloom = 1.0; + + // Check input parameters for validity + if (brightness < minBrightness || brightness > maxBrightness) { + return vec4(0.0, 0.0, 0.0, 1.0); // Return black with alpha of 1.0 to indicate error + } + if (contrast < minContrast || contrast > maxContrast) { + return vec4(0.0, 0.0, 0.0, 1.0); + } + if (saturation < minSaturation || saturation > maxSaturation) { + return vec4(0.0, 0.0, 0.0, 1.0); + } + if (bloom < minBloom || bloom > maxBloom) { + return vec4(0.0, 0.0, 0.0, 1.0); + } + + // Apply brightness and contrast + vec3 color = inputColor.rgb; + color = (color - vec3(0.5)) * exp2(brightness * 10.0) + vec3(0.5); + color = mix(vec3(0.5), color, pow(contrast * 4.0 + 1.0, 2.0)); + + // // NTSC + // vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721); + + // // BT.709 + // vec3 lumCoeff = vec3(0.299, 0.587, 0.114); + + // // BT.2020 + // vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593); + + // Warm NTSC + vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865); + + // Apply saturation + float luminance = dot(color, lumCoeff); + vec3 grey = vec3(luminance); + color = mix(grey, color, saturation); + + // Apply bloom effect + float threshold = 1.0 - bloom; + vec3 bloomColor = max(color - threshold, vec3(0.0)); + bloomColor = pow(bloomColor, vec3(2.0)); + bloomColor = mix(vec3(0.0), bloomColor, pow(min(luminance, threshold), 4.0)); + color += bloomColor; + + return vec4(color, inputColor.a); +} + +vec4 bilateralFilter(sampler2D tex, vec2 uv, vec4 color, float sampleRadius, float noiseThreshold, float intensity) { + vec4 filteredColor = vec4(0.0); + float totalWeight = 0.0; + + // Top-left pixel + vec4 sample = texture2D(tex, uv + vec2(-1.0, -1.0)); + float dist = length(vec2(-1.0, -1.0)); + float colorDist = length(sample - color); + float weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + + // Top pixel + sample = texture2D(tex, uv + vec2(0.0, -1.0)); + dist = length(vec2(0.0, -1.0)); + colorDist = length(sample - color); + weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + + // Top-right pixel + sample = texture2D(tex, uv + vec2(1.0, -1.0)); + dist = length(vec2(1.0, -1.0)); + colorDist = length(sample - color); + weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + + // Left pixel + sample = texture2D(tex, uv + vec2(-1.0, 0.0)); + dist = length(vec2(-1.0, 0.0)); + colorDist = length(sample - color); + weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + + // Center pixel + sample = texture2D(tex, uv); + dist = 0.0; + colorDist = length(sample - color); + weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + + // Right pixel + sample = texture2D(tex, uv + vec2(1.0, 0.0)); + dist = length(vec2(1.0, 0.0)); + colorDist = length(sample - color); + weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + + // Bottom-left pixel + sample = texture2D(tex, uv + vec2(-1.0, 1.0)); + dist = length(vec2(-1.0, 1.0)); + colorDist = length(sample - color); + weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + +// Bottom pixel + sample = texture2D(tex, uv + vec2(0.0, sampleRadius)); + dist = length(vec2(0.0, sampleRadius)); + colorDist = length(sample - color); + weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); + filteredColor += sample * weight; + totalWeight += weight; + + filteredColor /= totalWeight; + return mix(color, filteredColor, step(noiseThreshold, length(filteredColor - color))); +} + +vec4 supersample(sampler2D tex, vec2 uv, float sampleRadius, float noiseThreshold, float intensity) { + float radiusSq = sampleRadius * sampleRadius; + vec2 poissonDisk; + vec4 color = vec4(0.0); + + float r1_0 = sqrt(0.0 / 16.0); + float r2_0 = fract(1.0 / 3.0); + float theta_0 = TWOPI * r2_0; + poissonDisk = vec2(r1_0 * cos(theta_0), r1_0 * sin(theta_0)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_1 = sqrt(1.0 / 16.0); + float r2_1 = fract(2.0 / 3.0); + float theta_1 = TWOPI * r2_1; + poissonDisk = vec2(r1_1 * cos(theta_1), r1_1 * sin(theta_1)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_2 = sqrt(2.0 / 16.0); + float r2_2 = fract(3.0 / 3.0); + float theta_2 = TWOPI * r2_2; + poissonDisk = vec2(r1_2 * cos(theta_2), r1_2 * sin(theta_2)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_3 = sqrt(3.0 / 16.0); + float r2_3 = fract(4.0 / 3.0); + float theta_3 = TWOPI * r2_3; + poissonDisk = vec2(r1_3 * cos(theta_3), r1_3 * sin(theta_3)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_4 = sqrt(4.0 / 16.0); + float r2_4 = fract(5.0 / 3.0); + float theta_4 = TWOPI * r2_4; + poissonDisk = vec2(r1_4 * cos(theta_4), r1_4 * sin(theta_4)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_5 = sqrt(5.0 / 16.0); + float r2_5 = fract(6.0 / 3.0); + float theta_5 = TWOPI * r2_5; + poissonDisk = vec2(r1_5 * cos(theta_5), r1_5 * sin(theta_5)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_6 = sqrt(6.0 / 16.0); + float r2_6 = fract(7.0 / 3.0); + float theta_6 = TWOPI * r2_6; + poissonDisk = vec2(r1_6 * cos(theta_6), r1_6 * sin(theta_6)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_7 = sqrt(7.0 / 16.0); + float r2_7 = fract(8.0 / 3.0); + float theta_7 = TWOPI * r2_7; + poissonDisk = vec2(r1_7 * cos(theta_7), r1_7 * sin(theta_7)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_8 = sqrt(8.0 / 16.0); + float r2_8 = fract(9.0 / 3.0); + float theta_8 = TWOPI * r2_8; + poissonDisk = vec2(r1_8 * cos(theta_8), r1_8 * sin(theta_8)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_9 = sqrt(9.0 / 16.0); + float r2_9 = fract(10.0 / 3.0); + float theta_9 = TWOPI * r2_9; + poissonDisk = vec2(r1_9 * cos(theta_9), r1_9 * sin(theta_9)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_10 = sqrt(10.0 / 16.0); + float r2_10 = fract(11.0 / 3.0); + float theta_10 = TWOPI * r2_10; + poissonDisk = vec2(r1_10 * cos(theta_10), r1_10 * sin(theta_10)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_11 = sqrt(11.0 / 16.0); + float r2_11 = fract(12.0 / 3.0); + float theta_11 = TWOPI * r2_11; + poissonDisk = vec2(r1_11 * cos(theta_11), r1_11 * sin(theta_11)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_12 = sqrt(12.0 / 16.0); + float r2_12 = fract(13.0 / 3.0); + float theta_12 = TWOPI * r2_12; + poissonDisk = vec2(r1_12 * cos(theta_12), r1_12 * sin(theta_12)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_13 = sqrt(13.0 / 16.0); + float r2_13 = fract(14.0 / 3.0); + float theta_13 = TWOPI * r2_13; + poissonDisk = vec2(r1_13 * cos(theta_13), r1_13 * sin(theta_13)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_14 = sqrt(14.0 / 16.0); + float r2_14 = fract(15.0 / 3.0); + float theta_14 = TWOPI * r2_14; + poissonDisk = vec2(r1_14 * cos(theta_14), r1_14 * sin(theta_14)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + float r1_15 = sqrt(15.0 / 16.0); + float r2_15 = fract(16.0 / 3.0); + float theta_15 = TWOPI * r2_15; + poissonDisk = vec2(r1_15 * cos(theta_15), r1_15 * sin(theta_15)); + color += texture2D(tex, uv + poissonDisk * sampleRadius); + + return bilateralFilter(tex, uv, color, sampleRadius, noiseThreshold, intensity); +} +void main() { + vec2 tc_no_dist = v_texcoord; + + vec2 tc = applyBarrelDistortion(tc_no_dist, DISTORTION_AMOUNT); + + // [-1, 1] + vec2 tc_no_dist_symmetric = tc_no_dist * 2.0 - 1.0; + + // [0,1] + vec2 tc_no_dist_normalized = (tc_no_dist_symmetric + 1.0) / 2.0; + + // vec4 color = texture2D(tex, tc); + vec4 color = supersample(tex, tc, SAMPLING_RADIUS, NOISE_THRESHOLD, DENOISE_INTENSITY); + + color = applyAdaptiveSharpen(tc, color, tex); + + color = applyPhosphorGlow(tc, color, tex); + + color = reduceGlare(color); + + color = mix(applyFakeHDREffect(color, HDR_BRIGHTNESS, HDR_CONTRAST, HDR_SATURATION, HDR_BLOOM), color, 0.5); + + color = applyColorCorrection(color); + + color /= SUPERSAMPLING_SAMPLES; + + color = mix(applyChromaticAberration(tc, color), color, 0.25); + + color = mix(color, applyVignette(color), 0.37); + + color = applyBorder(tc_no_dist_normalized, color, 1.0 - BORDER_SIZE * BORDER_RADIUS, BORDER_COLOR); + + color = mix(applyBorder(tc, color, BORDER_SIZE, BORDER_COLOR), color, 0.05); + + color = applyScanlines(tc, color); + + gl_FragColor = color; + gl_FragColor.a = 1.0; +} + diff --git a/desktops/hyprland/hypr/shaders/drugs.frag b/desktops/hyprland/hypr/shaders/drugs.frag new file mode 100755 index 0000000..1190ed0 --- /dev/null +++ b/desktops/hyprland/hypr/shaders/drugs.frag @@ -0,0 +1,42 @@ + +precision highp float; +varying vec2 v_texcoord; +uniform sampler2D tex; +uniform float time; + +void warpco(inout vec2 tc) { + tc -= 0.5; + tc *= length(tc) * 2.0; + tc += 0.5; +} + +float rand1d(float seed) { + return sin(seed*1454.0); +} + +float rand2d(vec2 co) +{ + return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); +} + +vec3 rgb(in vec2 tc, float freq, float amp, inout vec4 centre) { + vec2 off = vec2(1.0/800.0, 0.0) * sin(tc.t * freq + time) * amp; + vec2 off2 = vec2(1.0/800.0, 0.0) * sin(tc.t * freq - time * 1.5) * amp; + centre = texture2D(tex, tc); + return vec3(texture2D(tex, tc-off).r, centre.g, texture2D(tex, tc+off2).b); +} + +void main() { + // vec2 px = 1.0 / textureSize(tex, 0).st; + vec2 tc = v_texcoord; + warpco(tc); + tc = mix(v_texcoord, tc, sin(time * 2.0)*0.07); + tc.x += rand2d(floor(tc * 20.0 + floor(time * 2.5))) * 0.01; + tc.x += rand1d(floor(tc.x * 40.0)) * 0.005 * rand1d(time * 0.001); + tc.y += sin(tc.x + time) * 0.02; + vec4 centre; + vec3 bent = rgb(tc, 100.0, 5.0, centre); + vec3 col = mix(centre.rgb, bent, sin(time)); + gl_FragColor = vec4(col, centre.a); + // gl_FragColor = vec4(texture2D(tex, v_texcoord)); +} \ No newline at end of file diff --git a/desktops/hyprland/hypr/shaders/extradark.frag b/desktops/hyprland/hypr/shaders/extradark.frag new file mode 100755 index 0000000..089ee81 --- /dev/null +++ b/desktops/hyprland/hypr/shaders/extradark.frag @@ -0,0 +1,21 @@ +// vim: set ft=glsl: +// blue light filter shader +// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux + +precision mediump float; +varying vec2 v_texcoord; +uniform sampler2D tex; + +void main() { + + vec4 pixColor = texture2D(tex, v_texcoord); + + // red + pixColor[0] *= 0.7; + // green + pixColor[1] *= 0.6; + // blue + pixColor[2] *= 0.5; + + gl_FragColor = pixColor; +} diff --git a/desktops/hyprland/hypr/shaders/gaussian.frag b/desktops/hyprland/hypr/shaders/gaussian.frag new file mode 100755 index 0000000..c4b9e0e --- /dev/null +++ b/desktops/hyprland/hypr/shaders/gaussian.frag @@ -0,0 +1,31 @@ +//--------------------------------------------------------------------------- +// Fragment +//--------------------------------------------------------------------------- +#version 420 core +//--------------------------------------------------------------------------- +precision mediump float; +in vec2 pos; // screen position <-1,+1> +out vec4 gl_FragColor; // fragment output color +uniform sampler2D txr; // texture to blur +uniform float xs,ys; // texture resolution +uniform float r = 6.0; // blur radius + +varying vec2 v_texcoord; +uniform sampler2D tex; +//--------------------------------------------------------------------------- +void main() + { + float x,y,xx,yy,rr=r*r,dx,dy,w,w0; + w0=0.3780/pow(r,1.975); + vec2 p; + vec4 pixColor = texture2D(tex, v_texcoord); + for (dx=1.0/xs,x=-r,p.x=0.5+(pos.x*0.5)+(x*dx);x<=r;x++,p.x+=dx){ xx=x*x; + for (dy=1.0/ys,y=-r,p.y=0.5+(pos.y*0.5)+(y*dy);y<=r;y++,p.y+=dy){ yy=y*y; + if (xx+yy<=rr) + { + w=w0*exp((-xx-yy)/(2.0*rr)); + pixColor+=texture2D(txr,p)*w; + }}} + gl_FragColor = pixColor; + } +//--------------------------------------------------------------------------- diff --git a/desktops/hyprland/hypr/shaders/motion-blur.frag b/desktops/hyprland/hypr/shaders/motion-blur.frag new file mode 100755 index 0000000..f0770de --- /dev/null +++ b/desktops/hyprland/hypr/shaders/motion-blur.frag @@ -0,0 +1,28 @@ +// vim: set ft=glsl: + +precision mediump float; +varying vec2 v_texcoord; +uniform sampler2D tex; +uniform float blurFactor; +uniform vec2 resolution; + +const int numSamples = 120000; + +uniform sampler2D accumulator; + +void main() { + float blurFactor = 120000.0; + + vec4 currentColor = texture2D(tex, v_texcoord); + vec4 prevColor = texture2D(accumulator, v_texcoord); + + vec2 velocity = (v_texcoord - gl_FragCoord.xy / resolution) * 2.0; + + vec4 colorDiff = currentColor - prevColor; + + float motionBlur = length(velocity) * blurFactor; + + vec4 finalColor = prevColor + colorDiff * 2.0; + + gl_FragColor = finalColor; +} \ No newline at end of file diff --git a/desktops/hyprland/hypr/shaders/nothing.frag b/desktops/hyprland/hypr/shaders/nothing.frag new file mode 100755 index 0000000..163e600 --- /dev/null +++ b/desktops/hyprland/hypr/shaders/nothing.frag @@ -0,0 +1,14 @@ +// vim: set ft=glsl: +// blue light filter shader +// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux + +precision mediump float; +varying vec2 v_texcoord; +uniform sampler2D tex; + +void main() { + + vec4 pixColor = texture2D(tex, v_texcoord); + + gl_FragColor = pixColor; +} diff --git a/desktops/hyprland/hypr/shaders/solarized.frag b/desktops/hyprland/hypr/shaders/solarized.frag new file mode 100755 index 0000000..1c37f2c --- /dev/null +++ b/desktops/hyprland/hypr/shaders/solarized.frag @@ -0,0 +1,41 @@ +// -*- mode:c -*- +precision lowp float; +varying vec2 v_texcoord; +uniform sampler2D tex; + +float distanceSquared(vec3 pixColor, vec3 solarizedColor) { + vec3 distanceVector = pixColor - solarizedColor; + return dot(distanceVector, distanceVector); +} + +void main() { + vec3 solarized[16]; + solarized[0] = vec3(0.,0.169,0.212); + solarized[1] = vec3(0.027,0.212,0.259); + solarized[2] = vec3(0.345,0.431,0.459); + solarized[3] = vec3(0.396,0.482,0.514); + solarized[4] = vec3(0.514,0.58,0.588); + solarized[5] = vec3(0.576,0.631,0.631); + solarized[6] = vec3(0.933,0.91,0.835); + solarized[7] = vec3(0.992,0.965,0.89); + solarized[8] = vec3(0.71,0.537,0.); + solarized[9] = vec3(0.796,0.294,0.086); + solarized[10] = vec3(0.863,0.196,0.184); + solarized[11] = vec3(0.827,0.212,0.51); + solarized[12] = vec3(0.424,0.443,0.769); + solarized[13] = vec3(0.149,0.545,0.824); + solarized[14] = vec3(0.165,0.631,0.596); + solarized[15] = vec3(0.522,0.6,0.); + + vec3 pixColor = vec3(texture2D(tex, v_texcoord)); + int closest = 0; + float closestDistanceSquared = distanceSquared(pixColor, solarized[0]); + for (int i = 1; i < 15; i++) { + float newDistanceSquared = distanceSquared(pixColor, solarized[i]); + if (newDistanceSquared < closestDistanceSquared) { + closest = i; + closestDistanceSquared = newDistanceSquared; + } + } + gl_FragColor = vec4(solarized[closest], 1.); +} diff --git a/desktops/hyprland/hypr/shaders/vibrance.frag b/desktops/hyprland/hypr/shaders/vibrance.frag new file mode 100755 index 0000000..5d3d488 --- /dev/null +++ b/desktops/hyprland/hypr/shaders/vibrance.frag @@ -0,0 +1,39 @@ +// vibrance for hyprland + +precision mediump float; +varying vec2 v_texcoord; +uniform sampler2D tex; + +// see https://github.com/CeeJayDK/SweetFX/blob/a792aee788c6203385a858ebdea82a77f81c67f0/Shaders/Vibrance.fx#L20-L30 +const vec3 VIB_RGB_BALANCE = vec3(1.0, 1.0, 1.0); +const float VIB_VIBRANCE = 0.15; + + +const vec3 VIB_coeffVibrance = VIB_RGB_BALANCE * -VIB_VIBRANCE; + +void main() { + + vec4 pixColor = texture2D(tex, v_texcoord); + + // RGB + vec3 color = vec3(pixColor[0], pixColor[1], pixColor[2]); + + + // vec3 VIB_coefLuma = vec3(0.333333, 0.333334, 0.333333); // was for `if VIB_LUMA == 1` + vec3 VIB_coefLuma = vec3(0.212656, 0.715158, 0.072186); // try both and see which one looks nicer. + + float luma = dot(VIB_coefLuma, color); + + float max_color = max(color[0], max(color[1], color[2])); + float min_color = min(color[0], min(color[1], color[2])); + + float color_saturation = max_color - min_color; + + vec3 p_col = vec3(vec3(vec3(vec3(sign(VIB_coeffVibrance) * color_saturation) - 1.0) * VIB_coeffVibrance) + 1.0); + + pixColor[0] = mix(luma, color[0], p_col[0]); + pixColor[1] = mix(luma, color[1], p_col[1]); + pixColor[2] = mix(luma, color[2], p_col[2]); + + gl_FragColor = pixColor; +} \ No newline at end of file diff --git a/desktops/hyprland/hyprland.nix b/desktops/hyprland/hyprland.nix index 0e047f3..8e2fe4c 100644 --- a/desktops/hyprland/hyprland.nix +++ b/desktops/hyprland/hyprland.nix @@ -18,6 +18,17 @@ "swww init" "swww img ~/Pictures/Wallpapers/wallpaper.jpg --transition-type center" ]; + general = { + gaps_in = 4; + gaps_out = 5; + border_size = 1; + + "col.active_border" = "rgba(0DB7D4FF)"; + "col.inactive_border" = "rgba(31313600)"; + + layout = "dwindle"; + }; + dwindle.preserve_split = true; input = { accel_profile = "flat"; }; @@ -28,14 +39,22 @@ ]; monitor = import ./monitors.nix; windowrule = [ - "pseudo,^(alacritty)$" + "pseudo,^(Alacritty)$" ]; + layerrule = import ./layerrules.nix; + decoration = import ./decoration.nix; }; }; services.dunst = import ./dunst.nix; + programs.ags = { + enable = true; + configDir = ./ags; + }; + programs.fuzzel = import ./fuzzel.nix; programs.alacritty.enable = true; + programs.foot = import ./foot.nix; programs.waybar = import ./waybar.nix; programs.wofi = import ./wofi.nix; programs.swaylock = import ./swaylock.nix; @@ -43,8 +62,14 @@ home.packages = with pkgs; [ xwaylandvideobridge hyprpicker + wl-clipboard wttrbar swww + # ags + glib + brightnessctl + ydotool + sassc ]; gtk = { @@ -73,5 +98,10 @@ || echo "not on tty1, letting in" ''; }; + + home.file.".config/hypr/shaders" = { + source = ./hypr/shaders; + recursive = true; + }; } diff --git a/desktops/hyprland/layerrules.nix b/desktops/hyprland/layerrules.nix new file mode 100644 index 0000000..6c0bb49 --- /dev/null +++ b/desktops/hyprland/layerrules.nix @@ -0,0 +1,34 @@ +[ + "noanim, .*" + "xray 1, .*" + + "noanim, noanim" + "blur, noanim" + "blur, gtk-layer-shell" + "ignorezero, gtk-layer-shell" + "blur, launcher" + "ignorealpha 0.5, launcher" + "blur, notifications" + "ignorealpha 0.69, notifications" + + # ags + "blur, bar" + "ignorealpha 0.64, bar" + "blur, corner.*" + "ignorealpha 0.69, corner.*" + "blur, indicator.*" + "ignorealpha 0.69, indicator.*" + "blur, overview" + "ignorealpha 0.69, overview" + "blur, cheatsheet" + "ignorealpha 0.69, cheatsheet" + "blur, sideright" + "ignorealpha 0.69, sideright" + "blur, sideleft" + "ignorealpha 0.69, sideleft" + "blur, indicatorundefined" + "ignorealpha 0.69, indicatorundefined" + "blur, osk" + "ignorealpha 0.69, osk" + "blur, session" +] diff --git a/desktops/hyprland/waybar.nix b/desktops/hyprland/waybar.nix index aed09a9..80c0bdc 100644 --- a/desktops/hyprland/waybar.nix +++ b/desktops/hyprland/waybar.nix @@ -6,11 +6,15 @@ position = "top"; height = 48; output = "HDMI-A-1"; + modules-left = [ "wlr/workspaces" ]; modules-center = [ "wlr/taskbar" ]; modules-right = [ "custom/weather" "clock" ]; "wlr/taskbar" = { all-outputs = true; + format = "{icon}"; + icon-size = 32; + on-click = "activate"; }; "custom/weather" = { @@ -20,6 +24,15 @@ exec = "wttrbar --location Berlin"; return-type = "json"; }; + + "wlr/workspaces" = { + all-outputs = true; + format = "{icon}\n{windows}"; + format-window-separator = "\n"; + window-rewrite = { + "(.*) — Mozilla Firefox" = ""; + }; + }; }; }; style = ''