feat: integrate system config

This commit is contained in:
2023-12-23 00:34:40 +01:00
parent f082d6eb65
commit 044e96eda4
140 changed files with 638 additions and 229 deletions

8
home/desktops/hyprland/ags/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

16
home/desktops/hyprland/ags/.idea/ags.iml generated Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/scripts/templates" />
</list>
</option>
</component>
</module>

View File

@@ -0,0 +1,8 @@
<project version="4">
<component name="ComposerSettings">
<execution />
</component>
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/ags.iml" filepath="$PROJECT_DIR$/.idea/ags.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,53 @@
// 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(),
...Array.from({length: 3}, (_, i) => [
CornerTopleft(i),
CornerTopright(i),
CornerBottomleft(i),
CornerBottomright(i),
]),
Overview(),
Indicator(),
Cheatsheet(),
SideRight(),
SideLeft(),
Osk(), // On-screen keyboard
Session(),
],
};

View File

@@ -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
}
]];

View File

@@ -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 }
]
]
}
}

View File

@@ -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',
}
];

View File

@@ -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;

View File

@@ -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', 'checklist', '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);
}
})

View File

@@ -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",
}))
})
]
})
]
}))
})),
});

View File

@@ -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),
})]
}),
]
})
]
})
});

View File

@@ -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;
}
}

View File

@@ -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();
}))
},
})

View File

@@ -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);
}
})

View File

@@ -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);
});
}

View File

@@ -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,
})

View File

@@ -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();
}
}))
},
})

View File

@@ -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;
}

View File

@@ -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();
}));
},
});

View File

@@ -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;
}],
]
});
}

View File

@@ -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',
})

View File

@@ -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(),
]
})
]
})
]
});
}

View File

@@ -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';
}]],
})
})
]
})
]
})
});

View File

@@ -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({
})

View File

@@ -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,
]
});
}

View File

@@ -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,
]
})
});

View File

@@ -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),
],
});

View File

@@ -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('');
}
}],
],
});
};

View File

@@ -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);
}
})

View File

@@ -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' })
]
})
});

View File

@@ -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
}],
],
});
}

View File

@@ -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(),
// ]
// })
// ],
// }),
]
});

View File

@@ -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(),
]
}),
],
}),
]
});

View File

@@ -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 || 'wired';
stack.shown = primary;
}]],
});
export const StatusIcons = (props = {}) => Widget.Box({
...props,
children: [Widget.EventBox({
child: Widget.Box({
className: 'spacing-h-15',
children: [
BluetoothIndicator(),
NetworkIndicator(),
]
})
})]
});

View File

@@ -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}`;
}]
]
})
})]
});
};

View File

@@ -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;
}]],
}),
],
}),]*/
],
}),
]
})
});

View File

@@ -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,
]
});
}

View File

@@ -0,0 +1,76 @@
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 = 9;
let lastWorkspace = 0;
const activeWorkspaceIndicator = 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 * (NUM_OF_WORKSPACES - ws + 1) + WORKSPACE_SIDE_PAD / 2 + 0.4}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 &']),
onPrimaryClickRelease: () => App.toggleWindow('overview'),
onMiddleClickRelease: () => App.toggleWindow('osk'),
child: Widget.Box({
homogeneous: true,
className: 'bar-group-center',
children: [
Widget.Box({
style: `padding: 0rem ${WORKSPACE_SIDE_PAD}rem;`,
children: [
Widget.Box({
halign: 'center',
children: Array.from({length: NUM_OF_WORKSPACES}, (_, i) => i + 1).map(i => Widget.Button({
onSecondaryClick: () => 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
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,
]
})
]
})
});

View File

@@ -0,0 +1,2 @@
# scripts folder
- For ARM devices, you have to compile C++ files yourself.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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) + ';')

View File

@@ -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; }

View File

@@ -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');" &

Binary file not shown.

View File

@@ -0,0 +1,79 @@
#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include "nlohmann/json.hpp"
using namespace std;
using json = nlohmann::json;
int workspace_a, workspace_b;
string clients;
json clientjson;
vector<string> windows_a, windows_b;
bool output = false;
string exec(const char* cmd) {
array<char, 128> buffer;
string result;
unique_ptr<FILE, decltype(&pclose)> 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();
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}
}

Binary file not shown.

View File

@@ -0,0 +1,85 @@
#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include "nlohmann/json.hpp"
using namespace std;
using json = nlohmann::json;
int workspace_a, workspace_b;
string clients;
json clientjson;
vector<string> windows_a, windows_b;
bool output = false;
string exec(const char* cmd) {
array<char, 128> buffer;
string result;
unique_ptr<FILE, decltype(&pclose)> 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();
}

View File

@@ -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=<bold variant of regular font>
# font-italic=<italic variant of regular font>
# font-bold-italic=<bold+italic variant of regular font>
# line-height=<font metrics>
letter-spacing=0
# horizontal-letter-offset=0
# vertical-letter-offset=0
# underline-offset=<font metrics>
# box-drawings-uses-font-glyphs=no
dpi-aware=no
# initial-window-size-pixels=700x500 # Or,
# initial-window-size-chars=<COLSxROWS>
# 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=<number of logical CPUs>
[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=<font 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=<primary font>
# color=<foreground color>
# button-width=26
# button-color=<background color>
# button-minimize-color=<regular4>
# button-maximize-color=<regular2>
# button-close-color=<regular1>
[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

View File

@@ -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

View File

@@ -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": {}
}

View File

@@ -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": {}
}

View File

@@ -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%);
}

View File

@@ -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);
}

View File

@@ -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)
}

View File

@@ -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;

View File

@@ -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"

View File

@@ -0,0 +1,468 @@
// 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;
// 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-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 {
min-height: 1.636rem;
min-width: 1.772rem;
transition: 300ms cubic-bezier(0.05, 0.7, 0.1, 1);
}
.bar-ws-active {
min-height: 1.636rem;
min-width: 1.772rem;
font-size: 1.091rem;
border: 0.4rem solid $black;
@include mainfont;
background-clip: content-box;
background-color: $notchSecondaryContainer;
color: $notchOnSecondaryContainer;
border-radius: 999px;
}
.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: white;
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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,79 @@
// Transparent version
$transparentize_amount: 0.6;
$transparentize_surface_amount_less: 0.3;
$transparentize_surface_amount_less_less: 0.15;
$transparentize_surface_amount: 0.7;
$transparentize_surface_amount_more: 0.1;
$transparentize_surface_amount_subtract_surface: $transparentize_surface_amount - $transparentize_amount;
@if $darkmode ==true {
// Less transparency
$transparentize_amount: 0.6;
$transparentize_surface_amount_less: 0.2;
$transparentize_surface_amount_less_less: 0.1;
$transparentize_surface_amount: 0.7;
$transparentize_surface_amount_more: 0.1;
$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%);

View File

@@ -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;
}

View File

@@ -0,0 +1,696 @@
// 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:
'Noto Sans',
'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;
}

View File

@@ -0,0 +1,29 @@
$darkmode: true;
$primary: #50d8ec;
$onPrimary: #00363d;
$primaryContainer: #004f58;
$onPrimaryContainer: #99f0ff;
$secondary: #b1cbd0;
$onSecondary: #1c3438;
$secondaryContainer: #334b4f;
$onSecondaryContainer: #cde7ec;
$tertiary: #bbc6ea;
$onTertiary: #24304d;
$tertiaryContainer: #3b4665;
$onTertiaryContainer: #dae2ff;
$error: #ba1b1b;
$onError: #ffffff;
$errorContainer: #ffdad4;
$onErrorContainer: #410001;
$colorbarbg: #191c1d;
$background: #191c1d;
$onBackground: #e1e3e3;
$surface: #191c1d;
$onSurface: #e1e3e3;
$surfaceVariant: #3f484a;
$onSurfaceVariant: #bfc8ca;
$outline: #899294;
$shadow: #000000;
$inverseOnSurface: #2d3132;
$inverseSurface: #eff1f1;
$inversePrimary: #4fd8ea;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
// }

View File

@@ -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);
}

View File

@@ -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();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
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'],
monitor: 1,
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);
}
}),
});

View File

@@ -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(),
]
}),
],
})
});

View File

@@ -0,0 +1,40 @@
import { Widget } from '../imports.js';
import { RoundedCorner } from "../modules/lib/roundedcorner.js";
export const CornerTopleft = monitor => Widget.Window({
name: 'cornertl',
layer: 'top',
monitor,
anchor: ['top', 'left'],
exclusive: false,
visible: true,
child: RoundedCorner('topleft', { className: monitor === 1 ? 'corner' : 'corner-black', }),
});
export const CornerTopright = monitor => Widget.Window({
name: 'cornertr',
layer: 'top',
monitor,
anchor: ['top', 'right'],
exclusive: false,
visible: true,
child: RoundedCorner('topright', { className: monitor === 1 ? 'corner' : 'corner-black', }),
});
export const CornerBottomleft = monitor => Widget.Window({
name: 'cornerbl',
layer: 'top',
monitor,
anchor: ['bottom', 'left'],
exclusive: false,
visible: true,
child: RoundedCorner('bottomleft', { className: 'corner-black', }),
});
export const CornerBottomright = monitor => Widget.Window({
name: 'cornerbr',
layer: 'top',
monitor,
anchor: ['bottom', 'right'],
exclusive: false,
visible: true,
child: RoundedCorner('bottomright', { className: 'corner-black', }),
});

View File

@@ -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,
}),
});

View File

@@ -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(),
});

View File

@@ -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(),
});

View File

@@ -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(),
]
}),
})

View File

@@ -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(),
})

View File

@@ -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(),
});

View File

@@ -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(),
});

View File

@@ -0,0 +1,98 @@
{
pkgs,
homeDirectory,
...
}: {
services.darkman = {
enable = true;
package = pkgs.buildGoModule rec {
pname = "darkman";
version = "1.5.4";
src = pkgs.fetchFromGitLab {
owner = "WhyNotHugo";
repo = "darkman";
rev = "5332193777fb0c5dbde6cbfd015a16697d6a0c8e";
hash = "sha256-3TGDy7hiI+z0IrA+d/Q+rMFlew6gipdpXyJ5eVLCmds=";
};
vendorHash = "sha256-xEPmNnaDwFU4l2G4cMvtNeQ9KneF5g9ViQSFrDkrafY=";
nativeBuildInputs = [pkgs.scdoc];
postPatch = ''
substituteInPlace darkman.service \
--replace "/usr/bin/darkman" "$out/bin/darkman"
substituteInPlace contrib/dbus/nl.whynothugo.darkman.service \
--replace "/usr/bin/darkman" "$out/bin/darkman"
substituteInPlace contrib/dbus/org.freedesktop.impl.portal.desktop.darkman.service \
--replace "/usr/bin/darkman" "$out/bin/darkman"
'';
buildPhase = ''
runHook preBuild
make build
runHook postBuild
'';
installPhase = ''
runHook preInstall
make PREFIX=$out install
runHook postInstall
'';
meta = with pkgs.lib; {
description = "Framework for dark-mode and light-mode transitions on Linux desktop";
homepage = "https://gitlab.com/WhyNotHugo/darkman";
license = licenses.isc;
maintainers = [maintainers.ajgrf];
platforms = platforms.linux;
mainProgram = "darkman";
};
};
settings = {
lat = 52.52;
lng = 13.405;
};
darkModeScripts = {
gtk-theme =
/*
bash
*/
''
${pkgs.dconf}/bin/dconf write \
/org/gnome/desktop/interface/color-scheme "'prefer-dark'"
'';
kitty-theme =
/*
bash
*/
''
${pkgs.kitty}/bin/kitty +kitten themes --reload-in=all --config-file-name ${homeDirectory}/.config/kitty/current-colors.conf Catppuccin-Frappe
'';
wallpaper = ''
${pkgs.swww}/bin/swww img ${./Lakeside-2/Lakeside-2-1.jpg}
'';
};
lightModeScripts = {
gtk-theme =
/*
bash
*/
''
${pkgs.dconf}/bin/dconf write \
/org/gnome/desktop/interface/color-scheme "'prefer-light'"
'';
kitty-theme =
/*
bash
*/
''
${pkgs.kitty}/bin/kitty +kitten themes --reload-in=all --config-file-name ${homeDirectory}/.config/kitty/current-colors.conf Catppuccin-Latte
'';
wallpaper = ''
${pkgs.swww}/bin/swww img ${./Lakeside-2/Lakeside-2-10.jpg}
'';
};
};
}

View File

@@ -0,0 +1,315 @@
{
config,
pkgs,
...
}: {
imports = [
./darkman.nix
];
wayland.windowManager.hyprland = {
enable = true;
enableNvidiaPatches = true;
settings = {
env = [
"LIBVA_DRIVER_NAME,nvidia"
"XDG_SESSION_TYPE,wayland"
"GBM_BACKEND,nvidia-drm"
"__GLX_VENDOR_LIBRARY_NAME,nvidia"
"WLR_NO_HARDWARE_CURSORS,1"
"NIXOS_OZONE_WL,1"
# Fixes black screen on Jellyfin
# https://github.com/jellyfin/jellyfin-media-player/issues/165#issuecomment-1569842393
"QT_QPA_PLATFORM,xcb"
# Potentially (?) fixes dialogs randomly closing again in IntelliJ
# https://github.com/hyprwm/Hyprland/issues/1947
"_JAVA_AWT_WM_NOREPARENTING,1"
# Gnome file manager fix
"GIO_EXTRA_MODULES,${pkgs.gnome.gvfs}/lib/gio/modules"
];
exec-once = [
"swww init"
"ags"
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY XAUTHORITY"
"dbus-update-activation-environment DISPLAY WAYLAND_DISPLAY XAUTHORITY"
"gnome-keyring-daemon --start --components=secrets"
"${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"
];
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;
dwindle.pseudotile = true;
input = {
accel_profile = "flat";
kb_layout = "cc1-thea";
# kb_options = "grp:alt_shift_toggle";
numlock_by_default = true;
};
bind = import ./keybinds.nix;
bindm = import ./mousebinds.nix;
bindr = [
"SUPER,SUPER_L,exec,pkill anyrun || anyrun"
];
monitor = import ./monitors.nix;
workspace = [
"1,monitor:DP-1"
"2,monitor:DP-1"
"3,monitor:DP-1"
"4,monitor:HDMI-A-1"
"5,monitor:HDMI-A-1"
"6,monitor:HDMI-A-1"
"7,monitor:DP-3"
"8,monitor:DP-3"
"9,monitor:DP-3"
"special:calc,border:false,gapsout:200,on-created-empty:[noanim;silent] kitty -e qalc"
];
windowrule = [
"pseudo,^(discord)$"
"pseudo,^(Slack)$"
"pseudo,^(steam)$"
"monitor DP-3,^(discord)$"
];
windowrulev2 = [
# Games
## AC2
"monitor DP-3,class:^(steam_app_805550)$"
"fullscreen,class:^(steam_app_805550)$"
"immediate,class:^(steam_app_805550)$"
# IntelliJ focus fixes
"windowdance,class:^(jetbrains-.*)$"
"dimaround,class:^(jetbrains-.*)$,floating:1,title:^(?!win)"
"center,class:^(jetbrains-.*)$,floating:1,title:^(?!win)"
"noanim,class:^(jetbrains-.*)$,title:^(win.*)$"
"noinitialfocus,class:^(jetbrains-.*)$,title:^(win.*)$"
"rounding 0,class:^(jetbrains-.*)$,title:^(win.*)$"
# pinentry
"dimaround,class:^(gcr-prompter)$"
"noborder,class:^(gcr-prompter)$"
"rounding 10,class:^(gcr-prompter)$"
"animation slide,class:^(gcr-prompter)$"
];
layerrule = [
"noanim, noanim"
"blur, noanim"
"blur, gtk-layer-shell"
"ignorezero, gtk-layer-shell"
"blur, launcher"
"ignorealpha 0.3, launcher"
"blur, notifications"
"ignorealpha 0.3, notifications"
"blur, anyrun"
"ignorealpha 0.3, anyrun"
# ags
"blur, bar"
"ignorealpha 0.3, bar"
"blur, corner.*"
"ignorealpha 0.3, corner.*"
"blur, indicator.*"
"ignorealpha 0.3, indicator.*"
"blur, overview"
"ignorealpha 0.3, overview"
"xray 0, overview"
"blur, cheatsheet"
"ignorealpha 0.3, cheatsheet"
"blur, sideright"
"ignorealpha 0.3, sideright"
"blur, sideleft"
"ignorealpha 0.3, sideleft"
"blur, indicatorundefined"
"ignorealpha 0.3, indicatorundefined"
"blur, osk"
"ignorealpha 0.3, osk"
"blur, session"
];
animation = [
"specialWorkspace,1,4,default,fade"
"fade,1,1,default"
];
decoration = {
rounding = 20;
blur = {
enabled = true;
xray = false;
size = 6;
passes = 4;
};
drop_shadow = true;
shadow_range = 15;
shadow_render_power = 6;
"col.shadow" = "rgba(00000044)";
};
};
};
programs.ags = {
enable = true;
configDir = ./ags;
};
programs.kitty = import ./kitty.nix {inherit pkgs;};
programs.anyrun = {
enable = true;
config = {
plugins = with pkgs.anyrunPlugins; [
applications
symbols
rink
dictionary
shell
];
y.fraction = 0.2;
closeOnClick = true;
};
extraCss =
/*
css
*/
''
* {
font-family: "NotoSans NF";
}
window#window {
background: transparent;
}
box#main {
background: rgba(48, 52, 70, 0.4);
box-shadow: 0 0 15px rgba(0, 0, 0, 0.29);
border-radius: 24px;
}
entry#entry {
border: none;
box-shadow: none;
padding: 8px 24px;
}
entry#entry,
list#main {
border-radius: 24px;
background: transparent;
}
row#match {
border-radius: 8px;
padding: 0 4px;
}
row#plugin {
border-radius: 16px;
padding: 16px;
}
list#plugin {
background: transparent;
}
'';
};
services.udiskie.enable = true;
services.udiskie.tray = "never";
fonts.fontconfig.enable = true;
home.packages = with pkgs; [
# fonts
noto-fonts
# essentials
xwaylandvideobridge
hyprpicker
grim
slurp
wl-clipboard
polkit_gnome
xdg-desktop-portal-gtk
/*
TODO: (flameshot.overrideAttrs(prev: {
nativeBuildInputs = prev.nativeBuildInputs ++ [ git grim ];
cmakeFlags = [
"-DUSE_WAYLAND_CLIPBOARD=1"
"-DUSE_WAYLAND_GRIM=true"
];
}))
*/
swww
# ags
glib
brightnessctl
ydotool
sassc
# gnome packages
evince
gnome.gvfs
gnome.gnome-keyring
gnome.nautilus
gnome.gnome-calendar
gnome.gnome-characters
gnome.gnome-contacts
gnome.gnome-clocks
gnome.gnome-calculator
gnome.simple-scan
gnome.gedit
gnome.eog
gnome.geary
gnome.ghex
gnome.gnome-weather
gnome.gnome-keyring
gnome.gnome-disk-utility
# fixes
xorg.xrandr
];
dconf.settings."org/gnome/desktop/interface".color-scheme = "prefer-dark";
gtk = {
enable = true;
theme = {
name = "adw-gtk3-dark";
package = pkgs.adw-gtk3;
};
iconTheme = {
name = "Tela";
package = pkgs.tela-icon-theme;
};
};
qt = {
enable = true;
platformTheme = "gtk";
};
home = {
pointerCursor = {
gtk.enable = true;
package = pkgs.capitaine-cursors;
name = "capitaine-cursors";
};
file.profile = {
enable = true;
target = ".zprofile"; # change to .profile if you're not using zsh
text =
/*
sh
*/
''
Hyprland && echo "goodbye" && exit 0 \
|| echo "$? couldn't launch Hyprland" && tty | grep tty1 \
&& echo "refusing to autologin without Hyprland on tty1" && exit 0 \
|| echo "not on tty1, letting in"
'';
};
file.".config/hypr/shaders" = {
source = ./hypr/shaders;
recursive = true;
};
};
}

View File

@@ -0,0 +1,8 @@
{
enable = true;
settings = {
global = {
origin = "top-left";
};
};
}

Binary file not shown.

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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));
}

View File

@@ -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;
}

Some files were not shown because too many files have changed in this diff Show More