Initial commit

This commit is contained in:
end-4
2023-12-25 18:16:14 +07:00
commit 16b0d77075
174 changed files with 18893 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg"><path d="m297.06 130.97c7.26-21.79 4.76-45.66-6.85-65.48-17.46-30.4-52.56-46.04-86.84-38.68-15.25-17.18-37.16-26.95-60.13-26.81-35.04-.08-66.13 22.48-76.91 55.82-22.51 4.61-41.94 18.7-53.31 38.67-17.59 30.32-13.58 68.54 9.92 94.54-7.26 21.79-4.76 45.66 6.85 65.48 17.46 30.4 52.56 46.04 86.84 38.68 15.24 17.18 37.16 26.95 60.13 26.8 35.06.09 66.16-22.49 76.94-55.86 22.51-4.61 41.94-18.7 53.31-38.67 17.57-30.32 13.55-68.51-9.94-94.51zm-120.28 168.11c-14.03.02-27.62-4.89-38.39-13.88.49-.26 1.34-.73 1.89-1.07l63.72-36.8c3.26-1.85 5.26-5.32 5.24-9.07v-89.83l26.93 15.55c.29.14.48.42.52.74v74.39c-.04 33.08-26.83 59.9-59.91 59.97zm-128.84-55.03c-7.03-12.14-9.56-26.37-7.15-40.18.47.28 1.3.79 1.89 1.13l63.72 36.8c3.23 1.89 7.23 1.89 10.47 0l77.79-44.92v31.1c.02.32-.13.63-.38.83l-64.41 37.19c-28.69 16.52-65.33 6.7-81.92-21.95zm-16.77-139.09c7-12.16 18.05-21.46 31.21-26.29 0 .55-.03 1.52-.03 2.2v73.61c-.02 3.74 1.98 7.21 5.23 9.06l77.79 44.91-26.93 15.55c-.27.18-.61.21-.91.08l-64.42-37.22c-28.63-16.58-38.45-53.21-21.95-81.89zm221.26 51.49-77.79-44.92 26.93-15.54c.27-.18.61-.21.91-.08l64.42 37.19c28.68 16.57 38.51 53.26 21.94 81.94-7.01 12.14-18.05 21.44-31.2 26.28v-75.81c.03-3.74-1.96-7.2-5.2-9.06zm26.8-40.34c-.47-.29-1.3-.79-1.89-1.13l-63.72-36.8c-3.23-1.89-7.23-1.89-10.47 0l-77.79 44.92v-31.1c-.02-.32.13-.63.38-.83l64.41-37.16c28.69-16.55 65.37-6.7 81.91 22 6.99 12.12 9.52 26.31 7.15 40.1zm-168.51 55.43-26.94-15.55c-.29-.14-.48-.42-.52-.74v-74.39c.02-33.12 26.89-59.96 60.01-59.94 14.01 0 27.57 4.92 38.34 13.88-.49.26-1.33.73-1.89 1.07l-63.72 36.8c-3.26 1.85-5.26 5.31-5.24 9.06l-.04 89.79zm14.63-31.54 34.65-20.01 34.65 20v40.01l-34.65 20-34.65-20z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+64
View File
@@ -0,0 +1,64 @@
"strict mode";
// Import
import { App, Utils } from './imports.js';
import { firstRunWelcome } from './services/messages.js';
// Widgets
import Bar from './widgets/bar/main.js';
import Cheatsheet from './widgets/cheatsheet/main.js';
import DesktopBackground from './widgets/desktopbackground/main.js';
import Dock from './widgets/dock/main.js';
import { CornerTopleft, CornerTopright, CornerBottomleft, CornerBottomright } from './widgets/screencorners/main.js';
import Indicator from './widgets/indicators/main.js';
import Osk from './widgets/onscreenkeyboard/main.js';
import Overview from './widgets/overview/main.js';
import Session from './widgets/session/main.js';
import SideLeft from './widgets/sideleft/main.js';
import SideRight from './widgets/sideright/main.js';
// Longer than actual anim time (see styles) to make sure widgets animate fully
const CLOSE_ANIM_TIME = 210;
// Init cache and check first run
Utils.exec(`bash -c 'mkdir -p ~/.cache/ags/user/colorschemes'`);
// SCSS compilation
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicmaterial.scss'`); // reset music styles
Utils.exec(`sassc ${App.configDir}/scss/main.scss ${App.configDir}/style.css`);
App.resetCss();
App.applyCss(`${App.configDir}/style.css`);
// Config object
export default {
css: `${App.configDir}/style.css`,
stackTraceOnError: true,
closeWindowDelay: { // For animations
'sideright': CLOSE_ANIM_TIME,
'sideleft': CLOSE_ANIM_TIME,
'osk': CLOSE_ANIM_TIME,
},
windows: [
// Bar() is below
CornerTopleft(),
CornerTopright(),
CornerBottomleft(),
CornerBottomright(),
DesktopBackground(),
Dock(), // Buggy
Overview(),
Indicator(),
Cheatsheet(),
SideLeft(),
SideRight(),
Osk(), // On-screen keyboard
Session(), // Power menu, if that's what you like to call it
],
};
// We don't want context menus of the bar's tray go under the rounded corner below,
// So bar is returned after 1ms, making it get spawned after the corner
// And having an Utils.timeout in that window array just gives an error
// Not having it in default export is fine since we don't need to toggle it
Bar();
// uwu
+135
View File
@@ -0,0 +1,135 @@
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 system sidebar" },
{ "keys": ["", "+", "B", "OR", "", "+", "O"], "action": "Toggle utilities 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": ["Ctrl", "", "+", "T"], "action": "Change wallpaper+colorscheme" },
// { "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": ["", "+", "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" },
],
"appeartick": 3
},
{
"icon": "terminal",
"name": "Launcher actions",
"binds": [
{ "keys": [">raw"], "action": "Toggle mouse acceleration" },
{ "keys": [">img"], "action": "Select wallpaper and generate colorscheme" },
{ "keys": [">light"], "action": "Switch to light theme" },
{ "keys": [">dark"], "action": "Switch to dark theme" },
{ "keys": [">badapple"], "action": "Apply black n' white colorscheme" },
{ "keys": [">color"], "action": "Pick acccent color" },
{ "keys": [">todo"], "action": "Type something after that to add a To-do item" },
],
"appeartick": 3
}
]];
+114
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 }
]
]
}
}
+62
View File
@@ -0,0 +1,62 @@
// For keyboard layout in lib/statusicons.js
// This list is not exhaustive. It just includes known/possible languages of users of my dotfiles
// Add your language here if you use multi-lang xkb input. Else, ignore
// Note that something like "French (Canada)" should go before "French"
// and "English (US)" should go before "English"
export const languages = [
{
layout: 'us',
name: 'English (US)',
flag: '🇺🇸'
},
{
layout: 'ru',
name: 'Russian',
flag: '🇷🇺',
},
{
layout: 'pl',
name: 'Polish',
flag: '🇷🇵🇵🇱',
},
{
layout: 'ro',
name: 'Romanian',
flag: '🇷🇴',
},
{
layout: 'ca',
name: 'French (Canada)',
flag: '🇫🇷',
},
{
layout: 'fr',
name: 'French',
flag: '🇫🇷',
},
{
layout: 'tr',
name: 'Turkish',
flag: '🇹🇷',
},
{
layout: 'jp',
name: 'Japanese',
flag: '🇯🇵',
},
{
layout: 'cn',
name: 'Chinese',
flag: '🇨🇳',
},
{
layout: 'vn',
name: 'Vietnamese',
flag: '🇻🇳',
},
{
layout: 'undef',
name: 'Undefined',
flag: '🧐',
},
]
+14
View File
@@ -0,0 +1,14 @@
export const quickLaunchItems = [
{
"name": "GitHub + Files×2",
"command": "github-desktop & nautilus --new-window & nautilus --new-window &"
},
{
"name": "Terminal×2",
"command": "foot & foot &"
},
{
"name": "Discord + Youtube + Github",
"command": "xdg-open 'https://discord.com/app' && xdg-open 'https://youtube.com/' && xdg-open 'https://github.com/' &"
},
]
+14
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',
}
];
+106
View File
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<style-scheme id="custom" _name="Custom" version="1.0">
<author>end_4</author>
<_description>Monokai port but very random</_description>
<!-- The scheme tries to look like the default GVim scheme,
since it is also what the hardwired GtkSourceView 1 color
scheme did -->
<!-- Palette -->
<color name="white" value="#F8F8F2"/>
<color name="blue" value="#179FFF"/>
<color name="magenta" value="#DA70D6"/>
<color name="violet" value="#AA7FF9"/>
<color name="cyan" value="#66D9EF"/>
<color name="grey" value="#817D69"/>
<color name="green" value="#A0D82E"/>
<color name="bordeaux" value="#B22757"/>
<color name="red" value="#F92672"/>
<color name="yellow" value="#E4D973"/>
<color name="purple" value="#A47AEE"/>
<color name="onSuccess" value="#D1E8D5"/>
<color name="success" value="#213528"/>
<color name="onError" value="#ffb4a9"/>
<color name="error" value="#680003"/>
<!-- Global Settings -->
<style name="current-line" background="#3E3D32"/>
<style name="current-line-number" background="#eeeeec"/>
<style name="draw-spaces" foreground="#babdb6"/>
<style name="background-pattern" background="#rgba(0,0,0,0)"/>
<!-- Bracket Matching -->
<!-- <style name="bracket-match" foreground="white" background="grey"/> -->
<!-- <style name="bracket-mismatch" foreground="white" background="red"/> -->
<!-- Right Margin -->
<style name="right-margin" foreground="#000000" background="#000000"/>
<!-- Search Matching -->
<style name="search-match" background="yellow"/>
<!-- Comments -->
<style name="def:comment" foreground="grey" italic="true"/>
<style name="def:shebang" foreground="grey" bold="true"/>
<style name="def:doc-comment-element" italic="true"/>
<!-- Constants -->
<style name="def:constant" foreground="white"/>
<style name="def:special-char" foreground="yellow"/>
<!-- Identifiers -->
<style name="def:identifier" foreground="green"/>
<!-- Statements -->
<style name="def:statement" foreground="cyan" bold="true"/>
<!-- Types -->
<style name="def:type" foreground="cyan" bold="true"/>
<!-- Others -->
<style name="def:preprocessor" foreground="purple"/>
<style name="def:error" background="red" bold="true"/>
<style name="def:warning" background="yellow"/>
<style name="def:note" foreground="bordeaux" bold="true"/>
<style name="def:underlined" italic="true" underline="single"/>
<!-- Heading styles, uncomment to enable -->
<!--
<style name="def:heading0" scale="5.0"/>
<style name="def:heading1" scale="2.5"/>
<style name="def:heading2" scale="2.0"/>
<style name="def:heading3" scale="1.7"/>
<style name="def:heading4" scale="1.5"/>
<style name="def:heading5" scale="1.3"/>
<style name="def:heading6" scale="1.2"/>
-->
<!-- Language specific styles -->
<style name="diff:added-line" background="success" foreground="onSuccess"/>
<style name="diff:removed-line" background="error" foreground="onError"/>
<style name="diff:changed-line" use-style="def:preprocessor"/>
<style name="diff:special-case" use-style="def:constant"/>
<style name="diff:location" use-style="def:statement"/>
<style name="diff:diff-file" use-style="def:type"/>
<style name="xml:tags" foreground="red"/>
<style name="xml:attribute-name" foreground="green"/>
<style name="xml:namespace" foreground="cyan" bold="true"/>
<style name="js:object" foreground="cyan" bold="true"/>
<style name="js:constructors" foreground="cyan"/>
<style name="latex:display-math" foreground="purple"/>
<style name="latex:command" foreground="green" bold="true"/>
<style name="latex:include" use-style="def:preprocessor"/>
<style name="sh:variable" foreground="purple"/>
<!-- legacy styles for old lang files -->
<style name="Others" foreground="cyan" bold="true"/>
<style name="Others 2" foreground="cyan"/>
<style name="Others 3" foreground="purple"/>
</style-scheme>
+39
View File
@@ -0,0 +1,39 @@
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;
export const SCREEN_WIDTH = Number(exec(`bash -c "xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f1 | head -1" | awk '{print $1}'`));
export const SCREEN_HEIGHT = Number(exec(`bash -c "xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2 | head -1" | awk '{print $1}'`));
+245
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;
}
}
+104
View File
@@ -0,0 +1,104 @@
const { Gdk, Gtk } = imports.gi;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
import { Utils, Widget } from '../imports.js';
// -- Styling --
// 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
// -- Usage --
// font size for progress value (0-100px) (hacky i know, but i want animations)
export const AnimatedCircProg = ({
initFrom = 0,
initTo = 0,
initAnimTime = 2900,
initAnimPoints = 1,
...rest
}) => Widget.DrawingArea({
...rest,
css: `${initFrom != initTo ? 'font-size: ' + initFrom + 'px; transition: ' + initAnimTime + 'ms linear;' : ''}`,
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();
if (progressValue == 0) return;
// 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();
}));
// Init animation
if (initFrom != initTo) {
// area.css = `font-size: ${initFrom}px; transition: ${initAnimTime}ms linear;`;
Utils.timeout(20, () => {
area.css = `font-size: ${initTo}px;`;
})
// const transitionDistance = initTo - initFrom;
// const oneStep = initAnimTime / initAnimPoints;
// area.css = `
// font-size: ${initFrom}px;
// transition: ${oneStep}ms linear;
// `;
// for (let i = 0; i < initAnimPoints; i++) {
// Utils.timeout(Math.max(10, i * oneStep), () => {
// if(!area) return;
// area.css = `${initFrom != initTo ? 'font-size: ' + (initFrom + (transitionDistance / initAnimPoints * (i + 1))) + 'px;' : ''}`;
// });
// }
}
else area.css = 'font-size: 0px;';
},
})
+87
View File
@@ -0,0 +1,87 @@
const { GLib, Gio } = imports.gi;
function checkLeapYear(year) {
return (
year % 400 == 0 ||
(year % 4 == 0 && year % 100 != 0));
}
function getMonthDays(month, year) {
const leapYear = checkLeapYear(year);
if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 31;
if (month == 2 && leapYear) return 29;
if (month == 2 && !leapYear) return 28;
return 30;
}
function getNextMonthDays(month, year) {
const leapYear = checkLeapYear(year);
if (month == 1 && leapYear) return 29;
if (month == 1 && !leapYear) return 28;
if (month == 12) return 31;
if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 30;
return 31;
}
function getPrevMonthDays(month, year) {
const leapYear = checkLeapYear(year);
if (month == 3 && leapYear) return 29;
if (month == 3 && !leapYear) return 28;
if (month == 1) return 31;
if ((month <= 7 && month % 2 == 1) || (month >= 8 && month % 2 == 0)) return 30;
return 31;
}
export function getCalendarLayout(dateObject, highlight) {
if (!dateObject) dateObject = new Date();
const weekday = (dateObject.getDay() + 6) % 7; // MONDAY IS THE FIRST DAY OF THE WEEK
const day = dateObject.getDate();
const month = dateObject.getMonth() + 1;
const year = dateObject.getFullYear();
const weekdayOfMonthFirst = (weekday + 35 - (day - 1)) % 7;
const daysInMonth = getMonthDays(month, year);
const daysInNextMonth = getNextMonthDays(month, year);
const daysInPrevMonth = getPrevMonthDays(month, year);
// Fill
var monthDiff = (weekdayOfMonthFirst == 0 ? 0 : -1);
var toFill, dim;
if(weekdayOfMonthFirst == 0) {
toFill = 1;
dim = daysInMonth;
}
else {
toFill = (daysInPrevMonth - (weekdayOfMonthFirst - 1));
dim = daysInPrevMonth;
}
var calendar = [...Array(6)].map(() => Array(7));
var i = 0, j = 0;
while (i < 6 && j < 7) {
calendar[i][j] = {
"day": toFill,
"today": ((toFill == day && monthDiff == 0 && highlight) ? 1 : (
monthDiff == 0 ? 0 :
-1
))
};
// Increment
toFill++;
if (toFill > dim) { // Next month?
monthDiff++;
if (monthDiff == 0)
dim = daysInMonth;
else if (monthDiff == 1)
dim = daysInNextMonth;
toFill = 1;
}
// Next tile
j++;
if (j == 7) {
j = 0;
i++;
}
}
return calendar;
}
+61
View File
@@ -0,0 +1,61 @@
const { Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Variable, Widget } from '../imports.js';
import { MaterialIcon } from './materialicon.js';
import { setupCursorHover } from './cursorhover.js';
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
export const ConfigToggle = ({ icon, name, desc = '', initValue, onChange, ...props }) => {
let value = initValue;
const toggleIcon = Label({
className: `icon-material txt-bold ${value ? '' : 'txt-poof'}`,
label: `${value ? 'check' : ''}`,
})
const toggleButtonIndicator = Box({
className: `switch-fg ${value ? 'switch-fg-true' : ''}`,
vpack: 'center',
hpack: 'start',
homogeneous: true,
children: [toggleIcon,],
});
const toggleButton = Box({
hpack: 'end',
className: `switch-bg ${value ? 'switch-bg-true' : ''}`,
homogeneous: true,
children: [toggleButtonIndicator,],
});
const widgetContent = Box({
tooltipText: desc,
className: 'txt spacing-h-5 configtoggle-box',
children: [
MaterialIcon(icon, 'norm'),
Label({
className: 'txt txt-small',
label: name,
}),
Box({ hexpand: true }),
toggleButton,
]
});
const interactionWrapper = Button({
...props,
child: widgetContent,
setup: setupCursorHover,
onClicked: () => { // mouse up/kb press
value = !value;
toggleIcon.toggleClassName('switch-fg-toggling-false', false);
toggleIcon.label = `${value ? 'check' : ''}`;
toggleIcon.toggleClassName('txt-poof', !value);
toggleButtonIndicator.toggleClassName('switch-fg-true', value);
toggleButton.toggleClassName('switch-bg-true', value);
onChange(interactionWrapper, value);
},
setup: (button) => {
button.connect('pressed', () => { // mouse down
toggleIcon.toggleClassName('txt-poof', true);
toggleIcon.toggleClassName('switch-fg-true', false);
if(!value) toggleIcon.toggleClassName('switch-fg-toggling-false', true);
});
}
});
return interactionWrapper;
}
+99
View File
@@ -0,0 +1,99 @@
const { Gdk, Gtk } = imports.gi;
const CLICK_BRIGHTEN_AMOUNT = 0.13;
export function setupCursorHover(button) {
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);
});
}
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);
});
}
export function setupCursorHoverGrab(button) {
button.connect('enter-notify-event', () => {
const display = Gdk.Display.get_default();
const cursor = Gdk.Cursor.new_from_name(display, 'grab');
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);
});
}
export function setupCursorHoverInfo(button) {
const display = Gdk.Display.get_default();
button.connect('enter-notify-event', () => {
const cursor = Gdk.Cursor.new_from_name(display, 'help');
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);
});
}
// failed radial ripple experiment
//
// 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();
// 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.css = `
// 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.css = `
// 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.css = `
// 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.css = `
// 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;
// });
+24
View File
@@ -0,0 +1,24 @@
const { Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Variable, Widget } from '../imports.js';
// TODO: Allow reveal update. Currently this just helps at declaration
export const DoubleRevealer = ({
transition1 = 'slide_right',
transition2 = 'slide_left',
duration1 = 150,
duration2 = 150,
child,
revealChild,
}) => {
return Widget.Revealer({
transition: transition1,
transitionDuration: duration1,
revealChild: revealChild,
child: Widget.Revealer({
transition: transition2,
transitionDuration: duration2,
revealChild: revealChild,
child: child,
})
})
}
+7
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,
})
+255
View File
@@ -0,0 +1,255 @@
// SPDX-FileCopyrightText: 2021 Uwe Jugel
// SPDX-License-Identifier: MIT
// This file is part of md2pango (https://github.com/ubunatic/md2pango).
const monospaceFonts = 'JetBrains Mono NF, JetBrains Mono Nerd Font, JetBrains Mono NL, SpaceMono NF, SpaceMono Nerd Font, monospace'
const H1 = "H1", H2 = "H2", H3 = "H3", H4 = "H4", H5 = "H5", BULLET = "BULLET", NUMBERING = "NUMBERING", CODE = "CODE"
const BOLD = "BOLD", EMPH = "EMPH", INLCODE = "INLCODE", LINK = "LINK", HEXCOLOR = "HEXCOLOR", UND = "UND"
let sub_h1, sub_h2, sub_h3, sub_h4, sub_h5
// m2p_sections defines how to detect special markdown sections.
// These expressions scan the full line to detect headings, lists, and code.
const m2p_sections = [
// h1 is actually 210% on github, but it's unecessary large imo
sub_h1 = { name: H1, re: /^(#\s+)(.*)(\s*)$/, sub: "<span font_weight='bold' size='150%'>$2</span>" },
sub_h2 = { name: H2, re: /^(##\s+)(.*)(\s*)$/, sub: "<span font_weight='bold' size='125%'>$2</span>" },
sub_h3 = { name: H3, re: /^(###\s+)(.*)(\s*)$/, sub: "<span font_weight='bold' size='100%'>$2</span>" },
sub_h4 = { name: H4, re: /^(####\s+)(.*)(\s*)$/, sub: "<span font_weight='bold' size='90%'>$2</span>" },
sub_h5 = { name: H5, re: /^(#####\s+)(.*)(\s*)$/, sub: "<span font_weight='bold' size='80%'>$2</span>" },
{ name: BULLET, re: /^(\s*)([\*\-]\s)(.*)(\s*)$/, sub: "$1• $3" },
{ name: NUMBERING, re: /^(\s*[0-9]+\.\s)(.*)(\s*)$/, sub: " $1$2" },
]
// m2p_styles defines how to replace inline styled text
const m2p_styles = [
{ name: BOLD, re: /(\*\*)(\S[\s\S]*?\S)(\*\*)/g, sub: "<b>$2</b>" },
{ name: UND, re: /(__)(\S[\s\S]*?\S)(__)/g, sub: "<u>$2</u>" },
{ name: EMPH, re: /\*(\S.*?\S)\*/g, sub: "<i>$1</i>" },
// { name: EMPH, re: /_(\S.*?\S)_/g, sub: "<i>$1</i>" },
{ name: HEXCOLOR, re: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/g, sub: `<span bgcolor='#$1' fgcolor='#000000' font_family='${monospaceFonts}'> #$1 </span>` },
{ name: INLCODE, re: /(`)([^`]*)(`)/g, sub: `<span font_weight='bold' font_family='${monospaceFonts}' bgcolor='#000000' fgcolor='#ffffff'> $2 </span>` },
// { name: UND, re: /(__|\*\*)(\S[\s\S]*?\S)(__|\*\*)/g, sub: "<u>$2</u>" },
]
const re_comment = /^\s*<!--.*-->\s*$/
const re_color = /^(\s*<!--\s*(fg|bg)=(#?[0-9a-z_A-Z-]*)\s*((fg|bg)=(#?[0-9a-z_A-Z-]*))?\s*-->\s*)$/
const re_reset = /(<!--\/-->)/
const re_uri = /http[s]?:\/\/[^\s']*/
const re_href = "/href='(http[s]?:\\/\\/[^\\s]*)'"
const re_atag = "<a\s.*>.*(http[s]?:\\/\\/[^\\s]*).*</a>/"
const re_h1line = /^===+\s*$/
const re_h2line = /^---+\s*$/
const m2p_escapes = [
[/<!--.*-->/, ''],
[/&/g, '&amp;'],
[/</g, '&lt;'],
[/>/g, '&gt;'],
]
const code_color_span = "<span foreground='#bbb' background='#222'>"
const escape_line = (line) => m2p_escapes.reduce((l, esc) => l.replace(...esc), line)
const pad = (lines, start = 1, end = 1) => {
let len = lines.reduce((n, l) => l.length > n ? l.length : n, 0)
return lines.map((l) => l.padEnd(len + end, ' ').padStart(len + end + start, ' '))
}
export function convert(text) {
let lines = text.split('\n')
// Indicates if the current line is within a code block
let is_code = false
let code_lines = []
let output = []
let color_span_open = false
let tt_must_close = false
const try_close_span = () => {
if (color_span_open) {
output.push('</span>')
color_span_open = false
}
}
const try_open_span = () => {
if (!color_span_open) {
output.push('</span>')
color_span_open = false
}
}
for (const line of lines) {
// first parse color macros in non-code texts
if (!is_code) {
let colors = line.match(re_color)
if (colors || line.match(re_reset)) {
try_close_span()
}
if (colors) {
try_close_span()
if (color_span_open) {
close_span()
}
let fg = colors[2] == 'fg' ? colors[3] : colors[5] == 'fg' ? colors[6] : ''
let bg = colors[2] == 'bg' ? colors[3] : colors[5] == 'bg' ? colors[6] : ''
let attrs = ''
if (fg != '') {
attrs += ` foreground='${fg}'`
}
if (bg != '') {
attrs += ` background='${bg}'`
}
if (attrs != '') {
output.push(`<span${attrs}>`)
color_span_open = true
}
}
}
// all macros processed, let's remove remaining comments
if (line.match(re_comment)) {
continue
}
// is this line an opening statement of a code block
let code_start = false
// escape all non-verbatim text
let result = is_code ? line : escape_line(line)
for (const { name, re, sub } of m2p_sections) {
if (line.match(re)) {
if (name === CODE) {
if (!is_code) {
// haven't been inside a code block, so ``` indicates
// that it is starting now
code_start = true
is_code = true
if (color_span_open) {
// cannot color
result = '<tt>'
tt_must_close = false
} else {
result = code_color_span + '<tt>'
tt_must_close = true
}
} else {
// the code block ends now
is_code = false
output.push(...pad(code_lines).map(escape_line))
code_lines = []
result = '</tt>'
if (tt_must_close) {
result += '</span>'
tt_must_close = false
}
}
} else {
if (is_code) {
result = line
} else {
result = line.replace(re, sub)
}
}
}
}
if (is_code && !code_start) {
code_lines.push(result)
continue
}
if (line.match(re_h1line)) {
output.push(`# ${output.pop()}`.replace(sub_h1.re, sub_h1.sub))
continue
}
if (line.match(re_h2line)) {
output.push(`## ${output.pop()}`.replace(sub_h2.re, sub_h2.sub))
continue
}
// all other text can be styled
for (const style of m2p_styles) {
result = result.replace(style.re, style.sub)
}
// all raw urls can be linked if possible
let uri = result.match(re_uri) // look for any URI
let href = result.match(re_href) // and for URIs in href=''
let atag = result.match(re_atag) // and for URIs in <a></a>
href = href && href[1] == uri
atag = href && atag[1] == uri
if (uri && (href || atag)) {
result = result.replace(uri, `<a href='${uri}'>${uri}</a>`)
}
output.push(result)
}
try_close_span()
// remove trailing whitespaces
output = output.map(line => line.replace(/ +$/, ''))
return output.join('\n')
}
const readFile = (f) => {
// node.js only and when running from the command line
const fs = require('fs')
return fs.readFileSync(f, 'utf8')
}
let __is_nodejs_main = false
try {
// node.js specific checks and exports
__is_nodejs_main = (require.main === module)
exports.convert = convert
} catch (e) { }
if (__is_nodejs_main) {
// running in node.js called from the CLI
let args = process.argv.slice(2)
if (args.length == 0 || args.find((a) => a == '-h')) {
console.log(`Usage: ${process.argv[1]} FILE [FILE...]`)
process.exit(0)
}
args.forEach((f) => process.stdout.write(convert(readFile(f))))
}
export const markdownTest = `# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
1. yes
2. no
127. well
- Bulletpoint starting with minus
* Bulletpoint starting with asterisk
---
- __Underline__ __ No underline __
- **Bold** ** No bold **
- _Italics1_ *Italics2* _ No Italics _
- A color: #D6BAFF
- nvidia green: #7ABB08
- sub-item
\`\`\`javascript
// A code block!
myArray = [23, 123, 43, 54, '6969'];
console.log('uwu');
\`\`\`
To update arch lincox, run \`sudo pacman -Syu\`
`;
+72
View File
@@ -0,0 +1,72 @@
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.DrawingArea({
...props,
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();
}
}))
},
})
+325
View File
@@ -0,0 +1,325 @@
// 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, Overlay, Label, Button, Revealer } = Widget;
import { MaterialIcon } from "./materialicon.js";
import { setupCursorHover } from "./cursorhover.js";
import { AnimatedCircProg } from "./animatedcircularprogress.js";
function guessMessageType(summary) {
if (summary.includes('recording')) return 'screen_record';
if (summary.includes('battery') || summary.includes('power')) return 'power';
if (summary.includes('screenshot')) return 'screenshot_monitor';
if (summary.includes('welcome')) return 'waving_hand';
if (summary.includes('time')) return 'scheduleb';
if (summary.includes('installed')) return 'download';
if (summary.includes('update')) return 'update';
if (summary.startsWith('file')) return 'folder_copy';
return 'chat';
}
const NotificationIcon = (notifObject) => {
// { appEntry, appIcon, image }, urgency = 'normal'
if (notifObject.image) {
return Box({
valign: Gtk.Align.CENTER,
hexpand: false,
className: 'notif-icon',
css: `
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: Gtk.Align.CENTER,
hexpand: false,
className: `notif-icon notif-icon-material-${notifObject.urgency}`,
homogeneous: true,
children: [
(icon != 'NO_ICON' ?
Icon({
icon: icon,
halign: Gtk.Align.CENTER, hexpand: true,
valign: Gtk.Align.CENTER,
setup: (self) => Utils.timeout(1, () => {
const styleContext = self.get_parent().get_style_context();
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
self.size = Math.max(width * 0.9, height * 0.9, 1); // im too lazy to add another box lol
}),
})
:
MaterialIcon(`${notifObject.urgency == 'critical' ? 'release_alert' : guessMessageType(notifObject.summary.toLowerCase())}`, 'hugerass', {
hexpand: true,
})
)
],
});
};
export default ({
notifObject,
isPopup = false,
popupTimeout = 3000,
props = {},
} = {}) => {
const command = (isPopup ?
() => notifObject.dismiss() :
() => notifObject.close()
)
const destroyWithAnims = () => {
widget.sensitive = false;
notificationBox.setCss(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: Gtk.Align.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,
truncate: 'end',
ellipsize: 3,
wrap: true,
useMarkup: notifObject.summary.startsWith('<'),
label: notifObject.summary,
}),
Label({
valign: Gtk.Align.CENTER,
className: 'txt-smaller txt-semibold',
justify: Gtk.Justification.RIGHT,
setup: (label) => {
// Let's ignore how it won't work for Jan1 cuz I'm lazy
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('Yesterday');
}
else {
label.label = messageTime.format('%d/%m');
}
}
}),
]
}),
Label({
xalign: 0,
className: `txt-smallie notif-body-${notifObject.urgency}`,
useMarkup: true,
xalign: 0,
justify: Gtk.Justification.LEFT,
wrap: true,
label: notifObject.body,
}),
]
}),
Overlay({
child: AnimatedCircProg({
className: `notif-circprog-${notifObject.urgency}`,
valign: Gtk.Align.CENTER,
initFrom: (isPopup ? 100 : 0),
initTo: 0,
initAnimTime: popupTimeout,
}),
overlays: [
Button({
className: 'notif-close-btn',
onClicked: () => {
destroyWithAnims()
},
child: MaterialIcon('close', 'large', {
valign: Gtk.Align.CENTER,
}),
setup: setupCursorHover,
}),
]
}),
// 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.setCss(`margin-left: 0px; margin-right: 0px;`);
else
self.setCss(`
margin-left: ${Number(offset + startMargin)}px;
margin-right: -${Number(offset + startMargin)}px;
`);
}
else if (offset < 0) {
if (initialDir > 0)
self.setCss(`margin-left: 0px; margin-right: 0px;`);
else {
offset = Math.abs(offset);
self.setCss(`
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.setCss(rightAnim1);
widget.sensitive = false;
}
else {
self.setCss(leftAnim1);
widget.sensitive = false;
}
Utils.timeout(200, () => {
wholeThing.revealChild = false
});
Utils.timeout(400, () => {
command();
wholeThing.destroy();
});
}
else {
self.setCss(`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;
}
+27
View File
@@ -0,0 +1,27 @@
import { App, Widget } from '../imports.js';
const { 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,
}),
});
+50
View File
@@ -0,0 +1,50 @@
import { Widget } from '../imports.js';
const { Gtk } = imports.gi;
const Lang = imports.lang;
export const RoundedCorner = (place, props) => Widget.DrawingArea({
...props,
hpack: place.includes('left') ? 'start' : 'end',
vpack: place.includes('top') ? 'start' : 'end',
setup: (widget) => Utils.timeout(1, () => {
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();
}));
}),
});
+5
View File
@@ -0,0 +1,5 @@
import { App, Service, Utils, Widget } from '../imports.js';
export const separatorLine = Widget.Box({
className: 'separator-line',
})
+223
View File
@@ -0,0 +1,223 @@
import { App, Service, Utils, Widget } from '../imports.js';
import { MaterialIcon } from './materialicon.js';
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import { languages } from '../data/languages.js';
// A guessing func to try to support langs not listed in data/languages.js
function isLanguageMatch(abbreviation, word) {
const lowerAbbreviation = abbreviation.toLowerCase();
const lowerWord = word.toLowerCase();
let j = 0;
for (let i = 0; i < lowerWord.length; i++) {
if (lowerWord[i] === lowerAbbreviation[j]) {
j++;
}
if (j === lowerAbbreviation.length) {
return true;
}
}
return false;
}
export const NotificationIndicator = (notifCenterName = 'sideright') => {
const widget = Widget.Revealer({
transition: 150,
transition: 'slide_left',
revealChild: false,
connections: [
[Notifications, (self, id) => {
if (!id || Notifications.dnd) return;
if (!Notifications.getNotification(id)) return;
self.revealChild = true;
}, 'notified'],
[App, (self, currentName, visible) => {
if (visible && currentName === notifCenterName) {
self.revealChild = false;
}
}],
],
child: Widget.Box({
children: [
MaterialIcon('notifications', 'norm'),
Widget.Label({
className: 'txt-small titlefont',
properties: [
['increment', (self) => self._unreadCount++],
['markread', (self) => self._unreadCount = 0],
['update', (self) => self.label = `${self._unreadCount}`],
['unreadCount', 0],
],
connections: [
[Notifications, (self, id) => {
if (!id || Notifications.dnd) return;
if (!Notifications.getNotification(id)) return;
self._increment(self);
self._update(self);
}, 'notified'],
[App, (self, currentName, visible) => {
if (visible && currentName === notifCenterName) {
self._markread(self);
self._update(self);
}
}],
]
})
]
})
});
return widget;
}
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: [
['fallback', SimpleNetworkIndicator()],
['unknown', Widget.Label({ className: 'txt-norm icon-material', label: 'wifi_off' })],
['disconnected', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_off' })],
['connected', Widget.Label({ className: 'txt-norm icon-material', label: 'lan' })],
['connecting', Widget.Label({ className: 'txt-norm icon-material', label: 'settings_ethernet' })],
],
connections: [[Network, stack => {
if (!Network.wired)
return;
const { internet } = Network.wired;
if (internet === 'connected' || internet === 'connecting')
stack.shown = internet;
else if (Network.connectivity !== 'full')
stack.shown = 'disconnected';
else
stack.shown = 'fallback';
}]],
});
const SimpleNetworkIndicator = () => Widget.Icon({
connections: [[Network, self => {
const icon = Network[Network.primary || 'wifi']?.iconName;
self.icon = icon || '';
self.visible = icon;
}]],
});
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: 'settings_ethernet' })],
['0', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_0_bar' })],
['1', Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_1_bar' })],
['2', Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_2_bar' })],
['3', Widget.Label({ className: 'txt-norm icon-material', label: 'network_wifi_3_bar' })],
['4', Widget.Label({ className: 'txt-norm icon-material', label: 'signal_wifi_4_bar' })],
],
connections: [[Network, (stack) => {
if (!Network.wifi) {
return;
}
if (Network.wifi.internet == 'connected') {
stack.shown = String(Math.ceil(Network.wifi.strength / 25));
}
else if (Network.wifi.internet == 'disconnected' || Network.wifi.internet == 'connecting') {
stack.shown = Network.wifi.internet;
}
}]],
});
export const NetworkIndicator = () => Widget.Stack({
transition: 'slide_up_down',
items: [
['fallback', SimpleNetworkIndicator()],
['wifi', NetworkWifiIndicator()],
['wired', NetworkWiredIndicator()],
],
connections: [[Network, stack => {
let primary = Network.primary || 'fallback';
if (primary == 'wifi' || primary == 'wired')
stack.shown = primary;
else
stack.shown = 'fallback';
}]],
});
const KeyboardLayout = ({ useFlag } = {}) => {
var initLangs = [];
var languageStackArray = [];
var currentKeyboard;
const updateCurrentKeyboards = () => {
currentKeyboard = JSON.parse(Utils.exec('hyprctl -j devices')).keyboards
.find(device => device.name === 'at-translated-set-2-keyboard');
if (currentKeyboard) {
initLangs = currentKeyboard.layout.split(',').map(lang => lang.trim());
}
languageStackArray = Array.from({ length: initLangs.length }, (_, i) => {
const lang = languages.find(lang => lang.layout == initLangs[i]);
if (!lang) return [
initLangs[i],
Widget.Label({ label: initLangs[i] })
];
return [
lang.layout,
Widget.Label({ label: (useFlag ? lang.flag : lang.layout) })
];
});
};
updateCurrentKeyboards();
const widgetRevealer = Widget.Revealer({
transition: 150,
transition: 'slide_left',
revealChild: languageStackArray.length > 1,
});
const widgetContent = Widget.Stack({
transition: 'slide_up_down',
items: [
...languageStackArray,
['undef', Widget.Label({ label: '?' })]
],
connections: [
[Hyprland, (stack, kbName, layoutName) => {
if (!kbName) {
return;
}
var lang = languages.find(lang => layoutName.includes(lang.name));
if (lang) {
widgetContent.shown = lang.layout;
}
else { // Attempt to support langs not listed
lang = languageStackArray.find(lang => isLanguageMatch(lang[0], layoutName));
if (!lang) stack.shown = 'undef';
else stack.shown = lang[0];
}
}, 'keyboard-layout']
],
});
widgetRevealer.child = widgetContent;
return widgetRevealer;
}
export const StatusIcons = (props = {}) => Widget.Box({
...props,
child: Widget.Box({
className: 'spacing-h-15',
children: [
KeyboardLayout({ useFlag: false }),
NotificationIndicator(),
BluetoothIndicator(),
NetworkIndicator(),
]
})
});
+3
View File
@@ -0,0 +1,3 @@
# scripts folder
- For ARM devices, you have to compile C++ files yourself. If there are none, yippee
- It is advised to use services instead of listening/polling scripts, so everything here are just scripts for actions
+156
View File
@@ -0,0 +1,156 @@
#!/usr/bin/env bash
sleep 0 # idk i want some delay or colors dont get applied properly
cd "$HOME/.config/ags" || exit
colornames=''
colorstrings=''
colorlist=()
colorvalues=()
if [[ "$1" = "--bad-apple" ]]; then
cp scripts/color_generation/specials/_material_badapple.scss scss/_material.scss
colornames=$(cat scripts/color_generation/specials/_material_badapple.scss | cut -d: -f1)
colorstrings=$(cat scripts/color_generation/specials/_material_badapple.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
else
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
fi
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_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]} }}/${colorvalues[$i]#\#}/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_new.ini"
# Apply colors
for i in "${!colorlist[@]}"; do
sed -i "s/${colorlist[$i]} #/${colorvalues[$i]#\#}/g" "$HOME/.config/foot/foot_new.ini" # note: ff because theyre opaque
done
cp "$HOME/.config/foot/foot_new.ini" "$HOME/.config/foot/foot.ini"
}
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_new.conf"
# Apply colors
for i in "${!colorlist[@]}"; do
sed -i "s/{{ ${colorlist[$i]} }}/${colorvalues[$i]#\#}/g" "$HOME/.config/hypr/colors_new.conf"
done
mv "$HOME/.config/hypr/colors_new.conf" "$HOME/.config/hypr/colors.conf"
}
apply_gtk() { # Using gradience-cli
lightdark=$(get_light_dark)
# 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
mkdir -p "$HOME/.config/presets" # create gradience presets folder
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 gtk-theme 'adw-gtk3'
gsettings set org.gnome.desktop.interface color-scheme 'default'
else
gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3-dark
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
fi
}
apply_ags() {
sassc "$HOME"/.config/ags/scss/main.scss "$HOME"/.config/ags/style.css
ags run-js 'openColorScheme.value = true; Utils.timeout(2000, () => openColorScheme.value = false);'
ags run-js "App.resetCss(); App.applyCss('${HOME}/.config/ags/style.css');"
}
# apply_svgs
apply_ags &
apply_hyprland &
apply_gtk &
apply_gtklock &
apply_fuzzel &
apply_foot
+61
View File
@@ -0,0 +1,61 @@
#!/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 [[ "$1" = "#"* ]]; then # this is a color
color_generation/generate_colors_material.py --color "$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" = "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
@@ -0,0 +1,118 @@
#!/bin/python3
from material_color_utilities_python import *
from pathlib import Path
import sys
import subprocess
def darken(hex_color, factor=0.7):
if not (hex_color.startswith('#') and len(hex_color) in (4, 7)):
raise ValueError("Invalid hex color format")
hex_color = hex_color.lstrip('#')
rgb = tuple(int(hex_color[i:i + 2], 16) for i in (0, 2, 4))
darkened_rgb = tuple(int(max(0, val * factor)) for val in rgb)
darkened_hex = "#{:02X}{:02X}{:02X}".format(*darkened_rgb)
return darkened_hex
img = 0
newtheme=0
if len(sys.argv) > 1 and sys.argv[1] == '--path':
# try:
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)
# except FileNotFoundError:
# print('[generate_colors_material.py] File not found', file=sys.stderr);
# exit()
# except:
# print('[generate_colors_material.py] Something went wrong', file=sys.stderr);
# exit()
elif len(sys.argv) > 1 and sys.argv[1] == '--color':
colorstr = sys.argv[2]
newtheme = themeFromSourceColor(argbFromHex(colorstr))
else:
# try:
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)
# except FileNotFoundError:
# print('[generate_colors_material.py] File not found', file=sys.stderr)
# exit()
# except:
# print('[generate_colors_material.py] Something went wrong', file=sys.stderr);
# exit()
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 = hexFromArgb(colorscheme.get_primary())
onPrimary = hexFromArgb(colorscheme.get_onPrimary())
primaryContainer = hexFromArgb(colorscheme.get_primaryContainer())
onPrimaryContainer = hexFromArgb(colorscheme.get_onPrimaryContainer())
secondary = hexFromArgb(colorscheme.get_secondary())
onSecondary = hexFromArgb(colorscheme.get_onSecondary())
secondaryContainer = hexFromArgb(colorscheme.get_secondaryContainer())
onSecondaryContainer = hexFromArgb(colorscheme.get_onSecondaryContainer())
tertiary = hexFromArgb(colorscheme.get_tertiary())
onTertiary = hexFromArgb(colorscheme.get_onTertiary())
tertiaryContainer = hexFromArgb(colorscheme.get_tertiaryContainer())
onTertiaryContainer = hexFromArgb(colorscheme.get_onTertiaryContainer())
error = hexFromArgb(colorscheme.get_error())
onError = hexFromArgb(colorscheme.get_onError())
errorContainer = hexFromArgb(colorscheme.get_errorContainer())
onErrorContainer = hexFromArgb(colorscheme.get_onErrorContainer())
background = hexFromArgb(colorscheme.get_background())
onBackground = hexFromArgb(colorscheme.get_onBackground())
surface = hexFromArgb(colorscheme.get_surface())
onSurface = hexFromArgb(colorscheme.get_onSurface())
surfaceVariant = hexFromArgb(colorscheme.get_surfaceVariant())
onSurfaceVariant = hexFromArgb(colorscheme.get_onSurfaceVariant())
outline = hexFromArgb(colorscheme.get_outline())
shadow = hexFromArgb(colorscheme.get_shadow())
inverseSurface = hexFromArgb(colorscheme.get_inverseSurface())
inverseOnSurface = hexFromArgb(colorscheme.get_inverseOnSurface())
inversePrimary = hexFromArgb(colorscheme.get_inversePrimary())
# post proccessing
background = darken(background, 0.6)
print('$primary: ' + primary + ';')
print('$onPrimary: ' + onPrimary + ';')
print('$primaryContainer: ' + primaryContainer + ';')
print('$onPrimaryContainer: ' + onPrimaryContainer + ';')
print('$secondary: ' + secondary + ';')
print('$onSecondary: ' + onSecondary + ';')
print('$secondaryContainer: ' + secondaryContainer + ';')
print('$onSecondaryContainer: ' + onSecondaryContainer + ';')
print('$tertiary: ' + tertiary + ';')
print('$onTertiary: ' + onTertiary + ';')
print('$tertiaryContainer: ' + tertiaryContainer + ';')
print('$onTertiaryContainer: ' + onTertiaryContainer + ';')
print('$error: ' + error + ';')
print('$onError: ' + onError + ';')
print('$errorContainer: ' + errorContainer + ';')
print('$onErrorContainer: ' + onErrorContainer + ';')
print('$colorbarbg: ' + background + ';')
print('$background: ' + background + ';')
print('$onBackground: ' + onBackground + ';')
print('$surface: ' + surface + ';')
print('$onSurface: ' + onSurface + ';')
print('$surfaceVariant: ' + surfaceVariant + ';')
print('$onSurfaceVariant: ' + onSurfaceVariant + ';')
print('$outline: ' + outline + ';')
print('$shadow: ' + shadow + ';')
print('$inverseSurface: ' + inverseSurface + ';')
print('$inverseOnSurface: ' + inverseOnSurface + ';')
print('$inversePrimary: ' + inversePrimary + ';')
@@ -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; }
@@ -0,0 +1,30 @@
$darkmode: true;
$primary: #e2e2e2;
$onPrimary: #000000;
$primaryContainer: #6b6b6b;
$onPrimaryContainer: #e2e2e2;
$secondary: #e2e2e2;
$onSecondary: #000000;
$secondaryContainer: #313131;
$onSecondaryContainer: #e2e2e2;
$tertiary: #e2e2e2;
$onTertiary: #000000;
$tertiaryContainer: #000000;
$onTertiaryContainer: #e2e2e2;
$error: #e2e2e2;
$onError: #000000;
$errorContainer: #000000;
$onErrorContainer: #e2e2e2;
$colorbarbg: #000000;
$background: #000000;
$onBackground: #e2e2e2;
$surface: #000000;
$onSurface: #e2e2e2;
$surfaceVariant: #202020;
$onSurfaceVariant: #e2e2e2;
$outline: #a1a1a1;
$shadow: #000000;
$inverseSurface: #e2e2e2;
$inverseOnSurface: #000000;
$inversePrimary: #e2e2e2;
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/bash
# Switches sww wallpaper
# Requires: coreutils, xrandr, hyprland
color=$(hyprpicker --no-fancy)
# Generate colors for ags n stuff
"$HOME"/.config/ags/scripts/color_generation/colorgen.sh "${color}" --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');"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/bash
# Switches sww wallpaper
# Requires: coreutils, xrandr, hyprland
if [ "$1" == "--noswitch" ]; then
imgpath=$(swww query | awk -F 'image: ' '{print $2}')
else
# 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"
fi
# Generate colors for ags n stuff
"$HOME"/.config/ags/scripts/color_generation/colorgen.sh "${imgpath}" --apply
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
getdate() {
date '+%Y%m%d_%H-%M-%S'
}
cd ~/Videos || exit
if [[ "$(pidof wf-recorder)" == "" ]]; then
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'record-script.sh'
if [[ "$1" == "--sound" ]]; then
wf-recorder -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" --audio=alsa_output.pci-0000_08_00.6.analog-stereo.monitor
elif [[ "$1" == "--fullscreen-sound" ]]; then
wf-recorder -f './recording_'"$(getdate)"'.mp4' -t --audio=alsa_output.pci-0000_08_00.6.analog-stereo.monitor
elif [[ "$1" == "--fullscreen" ]]; then
wf-recorder -f './recording_'"$(getdate)"'.mp4' -t
else
wf-recorder -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)"
fi
else
/usr/bin/kill --signal SIGINT wf-recorder
notify-send "Recording Stopped" "Stopped" -a 'record-script.sh'
fi
+156
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=SpaceMono Nerd Font:size=11
# 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
+21
View File
@@ -0,0 +1,21 @@
font=Gabarito
terminal=foot -e
prompt=">> "
layer=overlay
[colors]
background={{ $background }}ff
text={{ $onBackground }}ff
selection={{ $surfaceVariant }}ff
selection-text={{ $onSurfaceVariant }}ff
border={{ $surfaceVariant }}ff
match={{ $primary }}ff
selection-match={{ $primary }}ff
[border]
radius=17
width=2
[dmenu]
exit-immediately-if-empty=yes
@@ -0,0 +1,139 @@
{
"name": "Material3_Generated",
"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": "#c4c0ff",
"accent_fg_color": "#251a8c",
"accent_color": "#c4c0ff",
"destructive_bg_color": "#ffb4a9",
"destructive_fg_color": "#680003",
"destructive_color": "#ffb4a9",
"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": "#ffb4a9",
"error_fg_color": "#680003",
"window_bg_color": "#101012",
"window_fg_color": "#e5e1e6",
"view_bg_color": "#1c1b1f",
"view_fg_color": "#e5e1e6",
"headerbar_bg_color": "mix(@dialog_bg_color, @window_bg_color, 0.5)",
"headerbar_fg_color": "#e3dff9",
"headerbar_border_color": "#464559",
"headerbar_backdrop_color": "@headerbar_bg_color",
"headerbar_shade_color": "rgba(0, 0, 0, 0.09)",
"card_bg_color": "#101012",
"card_fg_color": "#e3dff9",
"card_shade_color": "rgba(0, 0, 0, 0.09)",
"dialog_bg_color": "#464559",
"dialog_fg_color": "#e3dff9",
"popover_bg_color": "#464559",
"popover_fg_color": "#e3dff9",
"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": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; ",
"gtk3": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; "
},
"plugins": {}
}
@@ -0,0 +1,139 @@
{
"name": "Material3_Generated",
"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": "{{ $background }}",
"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": "{{ $secondaryContainer }}",
"headerbar_backdrop_color": "@headerbar_bg_color",
"headerbar_shade_color": "rgba(0, 0, 0, 0.09)",
"card_bg_color": "{{ $background }}",
"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": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; ",
"gtk3": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; "
},
"plugins": {}
}
+89
View File
@@ -0,0 +1,89 @@
// 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.6);
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,
#poweroff-button:focus,
#reboot-button:focus,
#suspend-button:focus {
background-color: mix($secondaryContainer, white, 80%);
}
#poweroff-button:active,
#reboot-button:active,
#suspend-button:active {
background-color: mix($secondaryContainer, white, 70%);
}
+102
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);
}
+43
View File
@@ -0,0 +1,43 @@
# Auto generated color theme for image at: [Local wallpaper]
general {
col.active_border = rgba({{ $primaryContainer }}FF)
col.inactive_border = rgba({{ $secondaryContainer }}CC)
}
plugin {
droidbars {
# example config
bar_height = 30
background_color = rgba({{ $background }}FF)
# background_color_active = rgba({{ $surfaceVariant }}FF) # Not added yet
text_color = rgba({{ $onSecondaryContainer }}FF)
font_family = Lexend
button_font_fmily = JetBrainsMono NF
# example buttons (R -> L)
# droidbars-button = [0]isLeft(0/1), [1]color, [2]color2, [3]width, [4]height, [5]icon, [6]buttonType, [7]on-click
droidbars-button = 0, rgba({{ $onSecondaryContainer }}FF), rgba({{ $primary }}FF), 42, 16, 󰖭, normal, hyprctl dispatch killactive
droidbars-button = 0, rgba({{ $onSecondaryContainer }}FF), rgba({{ $primary }}FF), 42, 16, , normal, hyprctl dispatch fullscreen 1
droidbars-button = 1, rgba({{ $onSecondaryContainer }}FF), rgba({{ $primary }}FF), 16, 16,‎󰐃, pin, hyprctl dispatch pin
droidbars-button = 1, rgba({{ $onSecondaryContainer }}FF), rgba({{ $primary }}FF), 16, 16,󰗘, float, hyprctl dispatch togglefloating
}
hyprbars {
# Honestly idk if it works like css, but well, why not
bar_text_font = Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
bar_height = 30
bar_padding = 10
bar_button_padding = 5
bar_precedence_over_border = true
bar_part_of_window = true
bar_color = rgb({{ $background }})
col.text = rgb({{ $onBackground }})
# example buttons (R -> L)
# hyprbars-button = color, size, on-click
hyprbars-button = rgb({{ $onBackground }}), 13, 󰖭, hyprctl dispatch killactive
hyprbars-button = rgb({{ $onBackground }}), 13, 󰖯, hyprctl dispatch fullscreen 1
hyprbars-button = rgb({{ $onBackground }}), 13, 󰖰, hyprctl dispatch movetoworkspacesilent special
}
}
+489
View File
@@ -0,0 +1,489 @@
// Made to be pixel-perfect with 11pt font size
// 1rem = 11pt = 14.6666666667px
// Init
$black: black;
$white: white;
$notchSecondaryContainer: $secondaryContainer;
$notchOnSecondaryContainer: $onSecondaryContainer;
$notchPrimary: $primary;
$notchOnPrimary: $onPrimary;
// Check dark mode. Set colors accordingly for the fake snotch that's always black
@if $darkmode ==true {
$notchSecondaryContainer: $secondaryContainer;
$notchOnSecondaryContainer: $onSecondaryContainer;
$notchPrimary: $primary;
$notchOnPrimary: $onPrimary;
}
@else {
$notchSecondaryContainer: $onSecondaryContainer;
$notchOnSecondaryContainer: $secondaryContainer;
$notchPrimary: $primaryContainer;
$notchOnPrimary: $onPrimaryContainer;
}
.bar-bg {
background-color: $t_background;
min-height: 2.727rem;
}
.bar-sidespace {
min-width: 1.5rem;
}
.bar-group-margin {
padding: 0.2rem;
}
.bar-group {
// @include elevation-border;
background-color: $t_surface;
}
.bar-group-center {
border-bottom-left-radius: 1.364rem;
border-bottom-right-radius: 1.364rem;
padding: 0.2rem;
// background-color: $t_surface;
background-color: $black; // Hard code: fake notch
}
.corner-bar-group {
border-radius: 1.364rem; // Half of bar height
border-width: 0.068rem;
// background-color: $t_surface;
background-color: $black; // Hard code: fake notch
}
.bar-group-pad {
padding: 0rem 1.023rem;
}
.bar-group-pad-less {
padding: 0rem 0.681rem;
}
.bar-group-pad-system {
padding-left: 1.023rem;
padding-right: 0.547rem;
}
.bar-group-pad-music {
padding-right: 1.023rem;
// padding-left: 0.273rem;
}
.bar-group-pad-left {
padding-left: 1.364rem;
padding-right: 0.681rem;
}
.bar-group-pad-right {
padding-left: 0.681rem;
padding-right: 1.364rem;
}
.bar-group-pad-leftonly {
padding-left: 0.681rem;
}
.bar-group-pad-rightonly {
padding-right: 0.681rem;
}
.bar-group-standalone {
border-radius: 1.364rem;
-gtk-outline-radius: 1.364rem;
}
.bar-group-round {
border-radius: 10rem;
-gtk-outline-radius: 10rem;
}
.bar-group-middle {
border-radius: 0.477rem;
-gtk-outline-radius: 0.477rem;
}
.bar-group-left {
border-radius: 0.477rem;
-gtk-outline-radius: 0.477rem;
border-top-left-radius: 1.364rem;
border-bottom-left-radius: 1.364rem;
}
.bar-group-right {
border-radius: 0.477rem;
-gtk-outline-radius: 0.477rem;
border-top-right-radius: 1.364rem;
border-bottom-right-radius: 1.364rem;
}
.bar-ws-width {
min-width: 18.614rem;
}
.bar-separator {
@include full-rounding;
min-width: 0.341rem;
min-height: 0.341rem;
background-color: mix($t_surface, $t_onSurface, 90%);
margin: 0rem 0.341rem;
}
.bar-clock {
@include titlefont;
font-size: 1.2727rem;
color: $onBackground;
}
.bar-date {
@include titlefont;
font-size: 1rem;
color: $onBackground;
}
.bar-ws {
min-height: 1.636rem;
min-width: 1.772rem;
font-size: 1.091rem;
@include mainfont;
border-top: 0.068rem solid;
border-bottom: 0.068rem solid;
border-color: transparent;
color: $white;
}
.bar-ws-active-box {
transition: 100ms cubic-bezier(0.05, 0.7, 0.1, 1);
}
.bar-ws-active {
min-height: 1.5rem;
min-width: 1.5rem;
font-size: 1.091rem;
@include mainfont;
background-color: $notchPrimary;
color: $notchOnPrimary;
border-radius: 999px;
margin: 0.068rem;
// background-color: red;
}
.bar-ws-active-middledecor {
min-width: 0.682rem;
min-height: 0.682rem;
border-radius: 9999px;
background-color: $black;
margin: 0rem 0.409rem;
}
.bar-ws-occupied {
background-color: $notchSecondaryContainer;
color: $notchOnSecondaryContainer;
min-width: 1.772rem;
border-top: 0.068rem solid $notchOnSecondaryContainer;
border-bottom: 0.068rem solid $notchOnSecondaryContainer;
}
.bar-ws-occupied-left {
background-color: $notchSecondaryContainer;
color: $notchOnSecondaryContainer;
min-width: 1.704rem;
border-top-left-radius: 999px;
border-bottom-left-radius: 999px;
border-left: 0.068rem solid $notchOnSecondaryContainer;
border-top: 0.068rem solid $notchOnSecondaryContainer;
border-bottom: 0.068rem solid $notchOnSecondaryContainer;
border-right: 0px solid transparent;
}
.bar-ws-occupied-right {
background-color: $notchSecondaryContainer;
color: $notchOnSecondaryContainer;
min-width: 1.704rem;
border-top-right-radius: 999px;
border-bottom-right-radius: 999px;
border-right: 0.068rem solid $notchOnSecondaryContainer;
border-top: 0.068rem solid $notchOnSecondaryContainer;
border-bottom: 0.068rem solid $notchOnSecondaryContainer;
border-left: 0px solid transparent;
}
.bar-ws-occupied-left-right {
@include full-rounding;
background-color: $notchSecondaryContainer;
color: $notchOnSecondaryContainer;
min-width: 1.636rem;
border: 0.068rem solid $notchOnSecondaryContainer;
}
.bar-ws-empty {
color: $onBackground;
border-color: transparent;
}
.bar-batt {
@include full-rounding;
padding: 0rem 0.341rem;
background-color: $t_secondaryContainer;
color: $t_onSecondaryContainer;
// border: 1px solid $onSecondaryContainer;
}
.bar-sidemodule {
min-width: 26rem;
}
.bar-batt-low {
background-color: $error;
color: $errorContainer;
}
.bar-batt-full {
background-color: $successContainer;
color: $onSuccessContainer;
}
.bar-batt-prog-low {
background-color: $error;
color: $errorContainer;
}
.bar-batt-prog-full {
background-color: $successContainer;
color: $onSuccessContainer;
}
.bar-music-playstate {
min-height: 1.770rem;
min-width: 1.770rem;
border-radius: 10rem;
background-color: $secondaryContainer;
color: $onSecondaryContainer;
}
.bar-music-circprog {
@include fluent_decel_long;
min-width: 0.068rem; // line width
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;
background-color: $secondaryContainer;
color: $onSecondaryContainer;
}
.bar-music-playstate-txt {
transition: 100ms cubic-bezier(0.05, 0.7, 0.1, 1);
@include icon-material;
}
.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 menu_decel;
transition-duration: 100ms;
opacity: 1;
}
.bar-music-hide-true {
@include menu_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,
.bar-music-btn:focus {
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;
color: $secondaryContainer;
}
.bar-batt-chargestate-charging-smaller {
border-radius: 10rem;
min-width: 0.409rem;
min-height: 0.409rem;
background-color: $t_onSecondaryContainer;
color: $secondaryContainer;
}
.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,
.bar-space-button:focus>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;
}
.bar-statusicons {
@include full-rounding;
@include menu_decel;
margin: 0.2rem;
padding: 0rem 0.614rem;
}
.bar-statusicons-hover {
background-color: mix($t_background, $t_onBackground, 90%);
}
.bar-statusicons-active {
background-color: mix($t_background, $t_onBackground, 80%);
}
+52
View File
@@ -0,0 +1,52 @@
.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 menu_decel;
@include full-rounding;
min-width: 2.386rem;
min-height: 2.386rem;
}
.cheatsheet-closebtn:hover,
.cheatsheet-closebtn:focus {
background-color: $surfaceVariant;
}
.cheatsheet-closebtn:active {
background-color: mix($surfaceVariant, $onSurfaceVariant, 70%);
}
.cheatsheet-category-title {
@include titlefont;
font-size: 1.705rem;
}
+128
View File
@@ -0,0 +1,128 @@
///////////// COLOR MODIFICATIONS /////////////
// Material colors provide excellent readability, but can be uninteresting.
// This is an attempt to improve that.
$transparency_enabled: false;
@if $transparency_enabled ==false {
@if $darkmode ==true {
$background: mix(mix($background, $primary, 94%), #000000, 50%);
$surface: mix($surface, $primaryContainer, 98%);
$surfaceVariant: mix($surfaceVariant, #000000, 55%);
}
@if $darkmode ==false {
$background: mix($background, $primary, 87%);
$surface: mix($surface, $primary, 93%);
$surfaceVariant: mix($surfaceVariant, #FFFFFF, 55%);
}
}
@if $transparency_enabled ==true {
@if $darkmode ==true {
$background: mix(mix($background, $primary, 94%), #000000, 50%);
$surface: mix($surface, $primaryContainer, 98%);
$surfaceVariant: mix($surfaceVariant, #000000, 55%);
}
@if $darkmode ==false {
$background: mix($background, $primary, 94%);
$surface: mix($surface, $primary, 93%);
$surfaceVariant: mix($surfaceVariant, #FFFFFF, 55%);
}
}
// Amounts
$transparentize_amount: 0.3;
$transparentize_surface_amount_less: 0.6;
$transparentize_surface_amount_less_less: 0.55;
$transparentize_surface_amount: 0.7;
$transparentize_surface_amount_more: 0.8;
$transparentize_surface_amount_subtract_surface: $transparentize_surface_amount - $transparentize_amount;
@if $darkmode ==true {
// Less transparency
$transparentize_amount: 0.15;
$transparentize_surface_amount_less: 0.5;
$transparentize_surface_amount_less_less: 0.55;
$transparentize_surface_amount: 0.69;
$transparentize_surface_amount_more: 0.9;
$transparentize_surface_amount_subtract_surface: $transparentize_surface_amount - $transparentize_amount;
}
@if $transparency_enabled ==false {
$transparentize_amount: 0;
}
// Extended material
$success: #4f6354;
$onSuccess: #ffffff;
$successContainer: #d1e8d5;
$onSuccessContainer: #0c1f13;
@if $darkmode ==true {
// Dark variant
$success: #b5ccba;
$onSuccess: #213528;
$successContainer: #374b3e;
$onSuccessContainer: #d1e9d6;
}
// Transparent versions
$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);
// Others
$hovercolor: mix($t_surface, $t_onSurface, 50%);
$activecolor: mix($t_surface, $t_onSurface, 30%);
$subtext: mix($onBackground, $background, 70%);
$actiontext: mix($onBackground, $background, 85%);
// Terminal colors
$termbg: mix($t_surfaceVariant, $t_onSurfaceVariant, 80%);
$termfg: $onSurfaceVariant;
$term0: $t_background;
$term1: $error;
$term2: $inversePrimary;
$term3: $onPrimaryContainer;
$term4: $onPrimaryContainer;
$term5: $onSecondaryContainer;
$term6: $primary;
$term7: $onSurfaceVariant;
+129
View File
@@ -0,0 +1,129 @@
// * {
// border: 1px solid $onSurfaceVariant; // Debugging
// }
// *:focus {
// outline: 1px solid mix($onSurface, $surface, 40%);
// -gtk-outline-radius: $rounding_small;
// }
* {
selection {
background-color: $secondary;
color: $onSecondary;
}
caret-color: $primary;
}
@keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
tooltip {
animation-name: appear;
animation-duration: 100ms;
animation-timing-function: ease-out;
animation-iteration-count: 1;
}
menu {
@include elevation-border-softer;
padding: 0.681rem;
background: $surfaceVariant;
color: $onSurfaceVariant;
border-radius: 1.159rem;
-gtk-outline-radius: 1.159rem;
animation-name: appear;
animation-duration: 100ms;
animation-timing-function: ease-out;
animation-iteration-count: 1;
}
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,
menu>menuitem:focus {
background-color: mix($surfaceVariant, $onSurfaceVariant, 90%);
}
.separator-line {
background-color: $surfaceVariant;
min-width: 0.068rem;
min-height: 0.068rem;
}
tooltip {
@include normal-rounding;
background-color: $surfaceVariant;
color: $onSurfaceVariant;
border: 1px solid $onSurfaceVariant;
}
.configtoggle-box {
padding: 0.205rem;
border: 0.136rem solid transparent;
}
.configtoggle-box:focus {
padding: 0.205rem;
border: 0.136rem solid mix($onSurface, $surface, 40%);
}
.switch-bg {
@include menu_decel;
@include full-rounding;
background-color: mix($surface, $background, 50%);
border: 0.136rem solid $onSurface;
min-width: 2.864rem;
min-height: 1.637rem;
}
.switch-bg-true {
background-color: $primary;
border: 0.136rem solid $primary;
}
.switch-fg {
@include full-rounding;
@include menu_decel;
background-color: $onSurface;
color: $surface;
min-width: 0.819rem;
min-height: 0.819rem;
margin-left: 0.477rem;
}
.switch-fg-true {
background-color: $onPrimary;
color: $primary;
min-width: 1.431rem;
min-height: 1.431rem;
margin-left: 1.431rem;
}
.switch-fg-toggling-false {
@include menu_decel;
min-width: 1.636rem;
min-height: 0.819rem;
}
+89
View File
@@ -0,0 +1,89 @@
@mixin bg-textshadow {
// text-shadow: mix($shadow, $secondaryContainer, 50%) 1px 0px 3px;
}
.bg-time-box {
@include large-rounding;
margin: 2.045rem;
padding: 0.682rem;
}
.bg-time-clock {
@include bg-textshadow;
font-family: 'Gabarito';
font-size: 5.795rem;
color: $onBackground;
}
.bg-time-date {
@include bg-textshadow;
font-family: 'Gabarito';
font-size: 2.591rem;
color: $onBackground;
}
.bg-distro-box {
@include large-rounding;
margin: 2.045rem;
padding: 0.682rem;
}
.bg-distro-txt {
@include bg-textshadow;
font-family: 'Gabarito';
font-size: 1.432rem;
color: $onBackground;
}
.bg-distro-name {
@include bg-textshadow;
font-family: 'Gabarito';
font-size: 1.432rem;
color: $onSecondaryContainer;
}
.bg-graph {
color: rgba(255, 255, 255, 0.5);
border-radius: 0.614rem;
border: 0.682rem solid;
}
.bg-quicklaunch-title {
@include mainfont;
color: $onSurfaceVariant;
}
.bg-quicklaunch-btn {
@include mainfont;
@include full-rounding;
background-color: $surfaceVariant;
color: $onSurfaceVariant;
border: 0.068rem solid $subtext;
min-width: 4.432rem;
min-height: 2.045rem;
padding: 0.273rem 0.682rem;
}
.bg-quicklaunch-btn:hover,
.bg-quicklaunch-btn:focus {
background-color: mix($surfaceVariant, $onSurfaceVariant, 95%);
}
.bg-quicklaunch-btn:active {
background-color: mix($surfaceVariant, $onSurfaceVariant, 90%);
}
.bg-system-bg {
@include normal-rounding;
// background-color: $background;
}
.bg-system-circprog {
@include fluent_decel_long;
// margin-left: 0.273rem;
min-width: 0.205rem; // Trough stroke width
min-height: 4.091rem; // Diameter
font-size: 0px;
padding: 0rem;
background-color: $surfaceVariant;
}
+30
View File
@@ -0,0 +1,30 @@
.dock-bg {
@include large-rounding;
@include elevation2;
background-color: $t_background;
padding: 0.682rem;
}
.dock-app-btn {
@include normal-rounding;
padding: 0.273rem;
}
.dock-app-btn:hover,
.dock-app-btn:focus {
background-color: mix($t_surface, $t_onSurface, 95%);
}
.dock-app-btn:active {
background-color: mix($t_surface, $t_onSurface, 85%);
}
.dock-app-icon {
min-width: 3.409rem;
min-height: 3.409rem;
}
.dock-separator {
min-width: 0.068rem;
background-color: $surfaceVariant;
}
+442
View File
@@ -0,0 +1,442 @@
@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;
}
}
.test {
background-image: linear-gradient(45deg,
#F4D609 0%, #F4D609 10%, #212121 10%, #212121 20%,
#F4D609 20%, #F4D609 30%, #212121 30%, #212121 40%,
#F4D609 40%, #F4D609 50%, #212121 50%, #212121 60%,
#F4D609 60%, #F4D609 70%, #212121 70%, #212121 80%,
#F4D609 80%, #F4D609 90%, #212121 90%, #212121 100%);
background-repeat: repeat;
}
.test-size {
min-height: 3rem;
min-width: 3rem;
}
.txt-title {
@include titlefont;
font-size: 2.045rem;
}
.txt-title-small {
@include titlefont;
font-size: 1.364rem;
}
.techfont {
@include techfont;
}
.txt-reading {
@include readingfont;
}
.no-anim {
@include noanim;
}
.txt {
color: $onBackground;
}
.txt-primary {
color: $primary;
}
.txt-onSecondaryContainer {
color: $onSecondaryContainer;
}
.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-hugerass {
@include mainfont;
font-size: 2.045rem;
}
.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-tiny {
@include mainfont;
font-size: 0.7273rem;
}
.txt-poof {
font-size: 0px;
}
.txt-subtext {
@include subtext;
}
.txt-action {
@include actiontext;
}
.txt-semibold {
font-weight: 500;
}
.txt-bold {
font-weight: bold;
}
.txt-italic {
font-style: italic;
}
.titlefont {
@include titlefont;
}
.mainfont {
@include mainfont;
}
.icon-material {
@include icon-material;
}
.icon-nerd {
@include icon-nerd;
}
.separator-circle {
@include full-rounding;
background-color: $onSurface;
margin: 0rem 0.682rem;
min-width: 0.545rem;
min-height: 0.545rem;
}
.spacing-h-3>* {
margin-right: 0.205rem;
}
.spacing-h-3>*:last-child {
margin-right: 0rem;
}
.spacing-v-15>* {
margin-bottom: 1.023rem;
}
.spacing-v-15>*:last-child {
margin-bottom: 0rem;
}
.spacing-h-15>* {
margin-right: 1.023rem;
}
.spacing-h-15>*:last-child {
margin-right: 0rem;
}
.spacing-h-15>revealer>* {
margin-right: 1.023rem;
}
.spacing-h-15>revealer: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>* {
margin-bottom: 0.341rem;
}
.spacing-v-5>*:last-child {
margin-bottom: 0rem;
}
.spacing-v-5-revealer>revealer>* {
margin-bottom: 0.341rem;
}
.spacing-v-5-revealer>revealer:last-child>* {
margin-bottom: 0rem;
}
.spacing-h-5>* {
margin-right: 0.341rem;
}
.spacing-h-5>*:last-child {
margin-right: 0rem;
}
.spacing-h-5>widget>* {
margin-right: 0.341rem;
}
.spacing-h-5>widget:last-child>* {
margin-right: 0rem;
}
.spacing-h-5>revealer>* {
margin-right: 0.341rem;
}
.spacing-h-5>revealer:last-child>* {
margin-right: 0rem;
}
.spacing-v-minus5>* {
margin-bottom: -0.341rem;
}
.spacing-v-minus5>*:last-child {
margin-bottom: 0rem;
}
.spacing-h-10>* {
margin-right: 0.682rem;
}
.spacing-h-10>*: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>flowboxchild>* {
margin-right: 0.682rem;
}
.spacing-h-10>flowboxchild:last-child>* {
margin-right: 0rem;
}
.spacing-v-10>* {
margin-bottom: 0.682rem;
}
.spacing-v-10>*:last-child {
margin-bottom: 0rem;
}
.spacing-h-20>* {
margin-right: 1.364rem;
}
.spacing-h-20>*:last-child {
margin-right: 0rem;
}
.spacing-v-20>* {
margin-bottom: 1.364rem;
}
.spacing-v-20>*:last-child {
margin-bottom: 0rem;
}
.anim-enter {
@include anim-enter;
}
.anim-exit {
@include anim-exit;
}
.button-minsize {
@include button-minsize;
}
.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;
}
.margin-right-15 {
margin-right: 1.023rem;
}
.margin-left-15 {
margin-left: 1.023rem;
}
.margin-top-15 {
margin-top: 1.023rem;
}
.margin-bottom-15 {
margin-bottom: 1.023rem;
}
.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-v--5>label {
margin-bottom: -0.341rem;
}
.spacing-v--5>label:last-child {
margin-bottom: 0rem;
}
.spacing-v--10>box {
margin-bottom: -0.682rem;
}
.spacing-v--10>box:last-child {
margin-bottom: 0rem;
}
.spacing-v--10>label {
margin-bottom: -0.682rem;
}
.spacing-v--10>label:last-child {
margin-bottom: 0rem;
}
.spacing-h--20>box {
margin-left: -1.364rem;
}
.spacing-h--20>box:first-child {
margin-left: 0rem;
}
.instant {
transition: 0ms;
}
.menu-decel {
@include menu_decel;
}
+205
View File
@@ -0,0 +1,205 @@
// 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;
// Common rules
@mixin small-rounding {
border-radius: $rounding_small;
-gtk-outline-radius: $rounding_small;
}
@mixin normal-rounding {
border-radius: $rounding_medium;
-gtk-outline-radius: $rounding_medium;
}
@mixin large-rounding {
border-radius: $rounding_large;
-gtk-outline-radius: $rounding_large;
}
@mixin full-rounding {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
}
@mixin titlefont {
// Geometric sans-serif
font-family:
'Gabarito',
'Poppins',
'Lexend',
sans-serif;
}
@mixin mainfont {
// Other clean sans-serif
font-family:
'Geist',
'AR One Sans',
'Reddit Sans',
'Inter',
'Roboto',
'Ubuntu',
'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 NF', 'SpaceMono Nerd Font',
'JetBrains Mono NF', 'JetBrains Mono 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 NF', 'JetBrains Mono Nerd Font', 'JetBrains Mono NL', 'SpaceMono NF', 'SpaceMono Nerd Font', monospace;
}
@mixin readingfont {
// The most readable fonts, for a comfortable reading experience
// in stuff like ChatGPT widget
font-family:
'Lexend',
'Noto Sans',
sans-serif;
// font-weight: 500;
}
@mixin subtext {
color: $subtext;
}
@mixin actiontext {
color: $actiontext;
}
$elevation_margin: 0.476rem;
@mixin elevation-safe {
background: $surface;
color: $onSurface;
box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.69);
margin: $elevation_margin;
}
@mixin elevation2 {
box-shadow: 0px 2px 3px transparentize($shadow, 0.55);
margin: $elevation_margin;
}
@mixin elevation2-margin {
margin: $elevation_margin;
}
@mixin elevation2-padding {
padding: $elevation_margin;
}
@mixin elevation3 {
box-shadow: 0px 2px 5px $shadow;
margin: $elevation_margin;
}
@mixin menu_decel {
transition: 300ms cubic-bezier(0.1, 1, 0, 1);
}
@mixin menu_decel_fast {
transition: 170ms cubic-bezier(0.1, 1, 0, 1);
}
@mixin menu_accel {
transition: 160ms cubic-bezier(0.38, 0.04, 1, 0.07);
}
@mixin menu_accel_fast {
transition: 100ms cubic-bezier(0.38, 0.04, 1, 0.07);
}
@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);
}
@function tint($color, $percentage) {
@return mix(rgb(245, 250, 255), $color, $percentage);
}
@function shade($color, $percentage) {
@return mix(rgb(0, 0, 0), $color, $percentage);
}
$overlay1: mix($onSurface, rgba(0, 0, 0, 0), 25%);
$overlay2: mix($onSurface, rgba(0, 0, 0, 0), 40%);
@mixin elevation-border-softer {
border-top: 1px solid mix($t_t_surface, $t_onSurface, 90%);
border-left: 1px solid mix($t_t_surface, $t_onSurface, 90%);
border-right: 1px solid mix($t_t_surface, $t_onSurface, 95%);
border-bottom: 1px solid mix($t_t_surface, $t_onSurface, 95%);
}
@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;
}
$white: white;
$black: black;
+29
View File
@@ -0,0 +1,29 @@
$darkmode: true;
$primary: #c4c0ff;
$onPrimary: #251a8c;
$primaryContainer: #3d36a2;
$onPrimaryContainer: #e3dfff;
$secondary: #c7c4dd;
$onSecondary: #2f2e42;
$secondaryContainer: #464559;
$onSecondaryContainer: #e3dff9;
$tertiary: #eab9d1;
$onTertiary: #472639;
$tertiaryContainer: #603c50;
$onTertiaryContainer: #ffd8eb;
$error: #ffb4a9;
$onError: #680003;
$errorContainer: #930006;
$onErrorContainer: #ffb4a9;
$colorbarbg: #101012;
$background: #101012;
$onBackground: #e5e1e6;
$surface: #1c1b1f;
$onSurface: #e5e1e6;
$surfaceVariant: #47464f;
$onSurfaceVariant: #c8c5d0;
$outline: #928f9a;
$shadow: #000000;
$inverseSurface: #e5e1e6;
$inverseOnSurface: #313033;
$inversePrimary: #5550bb;
+152
View File
@@ -0,0 +1,152 @@
@import './material';
@import './musicmaterial';
@import './wal';
@import './musicwal';
@import './colors';
@import './lib_mixins';
$music_gradient1: mix($color1, $background, 30%);
@if $darkmode ==true {
$music_gradient1: mix($color1, $background, 30%);
}
$music_gradient2: $color3;
$music_gradient3: $color5;
$music_colorstart_transparentize: 0.3;
$music_extra_transparentize: 0.15;
$secondaryContainer: transparentize(mix(mix($background, $color2, 50%), $color6, 80%), 0.5);
$onSecondaryContainer: mix($color7, $color2, 90%);
.osd-music {
@include menu_decel;
@include elevation2;
margin-top: 0.313rem;
@include normal-rounding;
// min-height: 7.159rem;
min-width: 29.659rem;
background-color: $t_background;
padding: 0rem 1.023rem;
background: // Inspired by Amberol
linear-gradient(127deg, transparentize($music_gradient1, $music_colorstart_transparentize), transparentize($music_gradient1, $music_colorstart_transparentize - $transparentize_amount + $music_extra_transparentize) 70.71%),
linear-gradient(217deg, transparentize($music_gradient2, $music_colorstart_transparentize), transparentize($music_gradient2, $music_colorstart_transparentize - $transparentize_amount + $music_extra_transparentize) 70.71%),
radial-gradient(circle at 0% 100%, $color4 13%, rgba(0, 0, 0, 0) 100%),
linear-gradient(336deg, transparentize($music_gradient3, $music_colorstart_transparentize), transparentize($music_gradient3, $music_colorstart_transparentize - $transparentize_amount + $music_extra_transparentize) 70.71%),
linear-gradient($background, $background), // We don't want wallpaper tint, so here's a fully opaque surface
;
}
.osd-music-cover-fallback {
@include menu_decel;
@include small-rounding;
// margin: 1.023rem;
min-width: 7.5rem;
min-height: 7.5rem;
background-color: $t_surface;
color: $onSecondaryContainer;
}
.osd-music-cover {
@include small-rounding;
@include menu_decel;
// Must be -top and -bottom or it'll mess up horizontal spacing
margin-top: 1.023rem;
margin-bottom: 1.023rem;
min-width: 7.5rem;
min-height: 7.5rem;
}
.osd-music-cover-art {
@include small-rounding;
min-width: 7.5rem;
min-height: 7.5rem;
background-size: cover;
background-position: center;
}
.osd-music-info {
margin: 1.023rem 0rem;
}
.osd-music-title {
@include menu_decel;
@include titlefont;
font-size: 1.364rem;
color: $onSecondaryContainer;
}
.osd-music-artists {
@include menu_decel;
@include mainfont;
font-size: 0.955rem;
color: mix($onSecondaryContainer, $secondaryContainer, 80%);
}
.osd-music-pill {
@include menu_decel;
@include full-rounding;
@include titlefont;
min-width: 1.833rem;
padding: 0.273rem 0.682rem;
background-color: $secondaryContainer;
color: $onSecondaryContainer;
}
.osd-music-controls {
@include menu_decel;
@include full-rounding;
@include titlefont;
min-width: 1.833rem;
padding: 0.205rem;
background-color: $secondaryContainer;
color: $onSecondaryContainer;
}
.osd-music-controlbtn {
@include menu_decel;
min-width: 2.045rem;
min-height: 2.045rem;
@include full-rounding;
}
.osd-music-controlbtn:hover,
.osd-music-controlbtn:focus {
background-color: mix($secondaryContainer, $onSecondaryContainer, 90%);
}
.osd-music-controlbtn:active {
background-color: mix($secondaryContainer, $onSecondaryContainer, 85%);
}
.osd-music-controlbtn-txt {
@include menu_decel;
transition: 100ms cubic-bezier(0.05, 0.7, 0.1, 1);
@include icon-material;
font-size: 1.364rem;
margin: -0.1rem 0rem;
}
.osd-music-circprog {
@include fluent_decel_long;
min-width: 0.409rem; // width of progress
min-height: 3.068rem;
padding: 0.273rem;
color: $onSecondaryContainer;
}
.osd-music-playstate {
@include menu_decel;
min-height: 3.068rem;
min-width: 3.068rem;
border-radius: 10rem;
background-color: $secondaryContainer;
color: $onSecondaryContainer;
}
.osd-music-playstate-btn>label {
transition: 50ms cubic-bezier(0.05, 0.7, 0.1, 1);
@include icon-material;
font-size: 1.364rem;
margin: -0.1rem 0rem;
}
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+152
View File
@@ -0,0 +1,152 @@
$notif_surface: $t_background;
@mixin notif-rounding {
@include normal-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: $secondaryContainer;
color: $onSecondaryContainer;
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: $secondaryContainer;
color: $onSecondaryContainer;
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($onSecondaryContainer, $secondaryContainer, 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_onSecondaryContainer;
color: $secondaryContainer;
}
.notif-close-btn {
@include notif-rounding;
padding: 0rem 0.136rem;
}
.notif-close-btn:hover,
.notif-close-btn:focus {
background: $hovercolor;
}
.notif-close-btn:active {
background: $activecolor;
}
.notif-closeall-btn {
@include notif-rounding;
padding: 0.341rem 0.341rem;
}
.notif-closeall-btn:hover,
.notif-closeall-btn:focus {
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;
}
.notif-circprog-low {
transition: 0ms linear;
min-width: 0.136rem; // line width
min-height: 1.770rem;
padding: 0rem;
color: $onSecondaryContainer;
}
.notif-circprog-normal {
transition: 0ms linear;
min-width: 0.136rem; // line width
min-height: 1.770rem;
padding: 0rem;
color: $onSecondaryContainer;
}
.notif-circprog-critical {
transition: 0ms linear;
min-width: 0.136rem; // line width
min-height: 1.770rem;
padding: 0rem;
color: $onSecondaryContainer;
}
+116
View File
@@ -0,0 +1,116 @@
.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: $secondaryContainer;
// border: 0.068rem solid $onSecondaryContainer;
}
progress {
@include fluent_decel;
min-height: 0.680rem;
min-width: 0.680rem;
margin: 0rem 0.137rem;
border-radius: 10rem;
background-color: $onSecondaryContainer;
}
}
.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;
}
.osd-colorscheme {
border-radius: 1.023rem;
background-color: $t_background;
padding: 1.023rem;
@include elevation2;
}
.osd-color {
@include full-rounding;
min-width: 2.727rem;
min-height: 1.705rem;
padding: 0rem 0.341rem;
font-weight: bold;
box {
@include full-rounding;
margin: 0.409rem;
}
}
.osd-color-primary {
background-color: $primary;
color: $onPrimary;
box { background-color: $onPrimary; }
}
.osd-color-primaryContainer {
background-color: $primaryContainer;
color: $onPrimaryContainer;
box { background-color: $onPrimaryContainer; }
}
.osd-color-secondary {
background-color: $secondary;
color: $onSecondary;
box { background-color: $onSecondary; }
}
.osd-color-secondaryContainer {
background-color: $secondaryContainer;
color: $onSecondaryContainer;
box { background-color: $onSecondaryContainer; }
}
.osd-color-surfaceVariant {
background-color: $surfaceVariant;
color: $onSurfaceVariant;
box { background-color: $onSurfaceVariant; }
}
.osd-color-surface {
background-color: $surface;
color: $onSurface;
box { background-color: $onSurface; }
}
.osd-color-background {
background-color: $background;
color: $onBackground;
box { background-color: $onBackground; }
}
+112
View File
@@ -0,0 +1,112 @@
$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 menu_decel_fast;
@include large-rounding;
@include elevation-border;
@include elevation2;
// min-height: 29.591rem;
// min-width: 50rem;
background-color: $t_background;
}
.osk-body {
padding: 1.023rem;
padding-top: 0px;
}
.osk-show {
@include menu_decel_fast;
}
.osk-hide {
margin-top: 30.682rem;
margin-bottom: -30.682rem;
// opacity: 0;
@include menu_accel_fast;
}
.osk-dragline {
@include full-rounding;
background-color: $surfaceVariant;
min-height: 0.273rem;
min-width: 10.227rem;
margin-top: 0.545rem;
margin-bottom: 0.205rem;
}
.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,
.osk-key:focus {
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,
.osk-control-button:focus {
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;
}
+135
View File
@@ -0,0 +1,135 @@
.overview-search-box {
@include menu_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: $elevation_margin + 0.068rem;
}
.overview-search-icon-box {
margin-left: -18.545rem;
margin-right: $elevation_margin + 0.068rem;
}
.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;
min-width: 2.386rem;
min-height: 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:hover,
.overview-search-result-btn:focus {
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-workspace-number {
@include mainfont;
color: mix($t_onSurface, $t_surface, 93%);
}
.overview-tasks-window {
@include normal-rounding;
@include menu_decel;
background-color: $l_t_secondaryContainer;
color: $onSecondaryContainer;
border: 0.068rem solid $t_t_t_onSecondaryContainer;
}
.overview-tasks-window:hover,
.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;
}
+36
View File
@@ -0,0 +1,36 @@
.session-bg {
margin-top: -2.727rem;
background-color: mix($t_t_background, $background, 40%);
}
.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;
}
+690
View File
@@ -0,0 +1,690 @@
$sidebar_chat_textboxareaColor: mix($onSurfaceVariant, $surfaceVariant, 40%);
$system: $secondary;
$onSystem: $onSecondary;
$chatgpt: $primary;
$onChatgpt: $onPrimary;
@mixin group-padding {
padding: 0.341rem;
}
.sidebar-right {
@include menu_decel;
@include elevation-border;
@include elevation2;
border-radius: $rounding_large - $elevation_margin;
min-width: 27.818rem; // COMMENT THIS LATER IF TEXT WRAP IS USED
// min-height: 29.591rem;
background-color: $t_background;
padding: 1.023rem;
}
.sideright-show {
@include menu_decel;
}
.sideright-hide {
@include menu_accel;
margin-right: -30.682rem;
// opacity: 0;
}
.sidebar-left {
@include menu_decel;
@include elevation-border;
@include elevation2;
border-radius: $rounding_large - $elevation_margin;
min-width: 27.818rem; // COMMENT THIS LATER IF TEXT WRAP IS USED
// min-height: 29.591rem;
background-color: $t_background;
padding: 1.023rem;
}
.sideleft-show {
@include menu_decel;
}
.sideleft-hide {
margin-left: -30.682rem;
// opacity: 0;
@include menu_accel;
}
.sidebar-group {
@include normal-rounding;
@include group-padding;
background-color: $t_surface;
}
.sidebar-group-nopad {
@include normal-rounding;
background-color: $t_surface;
}
.sidebar-group-invisible {
@include group-padding;
}
.sidebar-group-invisible-morehorizpad {
padding: 0.341rem 0.682rem;
}
.sidebar-togglesbox {
@include full-rounding;
@include group-padding;
background-color: $t_surface;
}
.sidebar-iconbutton {
@include full-rounding;
@include menu_decel;
color: $onSurface;
min-width: 2.727rem;
min-height: 2.727rem;
}
.sidebar-iconbutton:hover,
.sidebar-iconbutton:focus {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
}
.sidebar-iconbutton:active {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%);
}
.sidebar-button {
@include menu_decel;
padding: 0rem $rounding_small;
background-color: $t_secondaryContainer;
color: $onSecondaryContainer;
}
.sidebar-button-nopad {
@include menu_decel;
background-color: $t_secondaryContainer;
color: $onSecondaryContainer;
}
.sidebar-button:hover,
.sidebar-button:focus {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
}
.sidebar-button:active {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%);
}
.sidebar-button-nopad:hover,
.sidebar-button-nopad:focus {
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,
.sidebar-button-active:focus {
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 menu_decel;
}
.sidebar-navrail-btn:hover>box>label:first-child,
.sidebar-navrail-btn:focus>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,
.sidebar-navrail-btn-active:focus>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-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,
slider:focus {
background-color: mix($t_onSurfaceVariant, $onSurfaceVariant, 80%);
}
slider:active {
background-color: mix($onSurfaceVariant, $surfaceVariant, 50%);
}
}
.sidebar-calendar-btn {
@include full-rounding;
@include menu_decel;
min-height: 2.523rem;
min-width: 2.523rem;
color: $onSurface;
}
.sidebar-calendar-btn:hover,
.sidebar-calendar-btn:focus {
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,
.sidebar-calendar-btn-today:focus {
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,
.sidebar-calendar-monthyear-btn:focus {
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-selector-tab {
@include small-rounding;
transition: 0ms;
min-height: 2.5rem;
color: $onSurface;
}
.sidebar-selector-tab:hover,
.sidebar-selector-tab:focus {
background-color: mix($t_surfaceVariant, $onSurfaceVariant, 90%);
}
.sidebar-selector-tab:active {
background-color: mix($surfaceVariant, $onSurfaceVariant, 75%);
}
.sidebar-selector-tab-active>box>label {
color: $primary;
}
.sidebar-selector-highlight-offset {
margin-top: -0.205rem;
margin-bottom: 0.205rem;
}
.sidebar-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 {
padding-right: 0.545rem;
}
.sidebar-todo-item-even {
background-color: $t_t_surfaceVariant;
}
.sidebar-todo-item-action {
border-radius: 9999px;
min-width: 1.705rem;
min-height: 1.705rem;
}
.sidebar-todo-item-action:hover,
.sidebar-todo-item-action:focus {
background-color: mix($t_surface, $t_onSurface, 80%);
}
.sidebar-todo-item-action:active {
background-color: mix($t_surface, $t_onSurface, 65%);
}
.sidebar-todo-crosser {
transition: margin 200ms cubic-bezier(0.1, 1, 0, 1), background-color 0ms;
min-width: 0rem;
}
.sidebar-todo-crosser-crossed {
background-color: $onBackground;
}
.sidebar-todo-crosser-removed {
background-color: $error;
}
.sidebar-clipboard-item {
border-radius: $rounding_small;
min-height: 2.045rem;
padding: 0.341rem;
background-color: $t_secondaryContainer;
color: $onSecondaryContainer;
}
.sidebar-clipboard-item:hover,
.sidebar-clipboard-item:focus {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 90%);
}
.sidebar-clipboard-item:active {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
}
.sidebar-todo-new {
@include full-rounding;
color: $onSecondaryContainer;
margin: 0.341rem;
padding: 0.205rem 0.545rem;
border: 0.068rem solid $onSurface;
}
.sidebar-todo-new:hover,
.sidebar-todo-new:focus {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 97%);
}
.sidebar-todo-new:active {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
}
.sidebar-todo-add {
@include full-rounding;
@include menu_decel;
min-width: 1.705rem;
min-height: 1.705rem;
color: $onSecondaryContainer;
border: 0.068rem solid $onSurface;
}
.sidebar-todo-add:hover,
.sidebar-todo-add:focus {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 97%);
}
.sidebar-todo-add:active {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
}
.sidebar-todo-add-available {
@include menu_decel;
@include full-rounding;
min-width: 1.705rem;
min-height: 1.705rem;
background-color: $primary;
color: $onPrimary;
border: 0.068rem solid $primary;
}
.sidebar-todo-add-available:hover,
.sidebar-todo-add-available:focus {
background-color: mix($primary, $onPrimary, 97%);
}
.sidebar-todo-add-available:active {
background-color: mix($primary, $onPrimary, 80%);
}
.sidebar-todo-entry {
@include menu_decel;
@include small-rounding;
background-color: $surfaceVariant;
color: $onSurfaceVariant;
caret-color: $onSurfaceVariant;
margin: 0rem 0.341rem;
min-height: 1.773rem;
min-width: 0rem;
padding: 0.205rem 0.682rem;
border: 0.068rem solid mix($onSurfaceVariant, $surfaceVariant, 50%);
}
.sidebar-todo-entry:focus {
border: 0.068rem solid mix($onSurfaceVariant, $surfaceVariant, 90%);
}
.sidebar-module {
@include normal-rounding;
@include group-padding;
background-color: $t_surface;
}
.sidebar-module-btn-arrow {
@include full-rounding;
@include icon-material;
background-color: $t_surfaceVariant;
min-width: 1.705rem;
min-height: 1.705rem;
}
.sidebar-chat-viewport {
@include menu_decel;
margin: 0.682rem 0rem;
padding: 0.682rem 0rem;
}
.sidebar-chat-textarea {
@include normal-rounding;
border: 0.068rem solid $sidebar_chat_textboxareaColor;
padding: 0.682rem;
}
.sidebar-chat-entry {
color: $onSurfaceVariant;
caret-color: $onSurfaceVariant;
min-height: 1.773rem;
min-width: 0rem;
}
.sidebar-chat-send {
@include menu_decel;
min-width: 1.705rem;
min-height: 1.705rem;
border-radius: $rounding_medium - 0.681rem;
background-color: $sidebar_chat_textboxareaColor;
}
.sidebar-chat-send:hover,
.sidebar-chat-send:focus {
background-color: mix($sidebar_chat_textboxareaColor, $t_onSecondaryContainer, 97%);
}
.sidebar-chat-send:active {
background-color: mix($sidebar_chat_textboxareaColor, $t_onSecondaryContainer, 80%);
}
.sidebar-chat-send-available {
background-color: $primary;
color: $onPrimary;
}
.sidebar-chat-send-available:hover,
.sidebar-chat-send-available:focus {
background-color: mix($primary, $onPrimary, 97%);
}
.sidebar-chat-send-available:active {
background-color: mix($primary, $onPrimary, 80%);
}
.sidebar-chat-message {
margin: 0.682rem;
}
.sidebar-chat-indicator {
@include full-rounding;
min-width: 0.136rem;
}
.sidebar-chat-indicator-user {
background-color: $onBackground;
}
.sidebar-chat-indicator-bot {
background-color: $chatgpt;
}
.sidebar-chat-indicator-System {
background-color: $system;
}
.sidebar-chat-name {
@include titlefont;
padding: 0.341rem;
margin-left: -0.136rem;
padding: 0.341rem;
padding-left: 0.818rem;
}
.sidebar-chat-txtblock {
margin-left: -0.136rem;
padding: 0.341rem;
padding-left: 0.818rem;
}
.sidebar-chat-txt {
@include readingfont;
}
.sidebar-chat-codeblock {
@include normal-rounding;
// @include elevation2;
background-color: $termbg;
color: $termfg;
margin: 0rem 0.682rem;
border: 0.068rem solid $t_t_t_onSecondaryContainer;
}
.sidebar-chat-codeblock-topbar {
@include mainfont;
margin: 0.273rem;
margin-bottom: 0rem;
background-color: $secondaryContainer;
color: $onSecondaryContainer;
border-radius: $rounding_medium - 0.273rem;
border: 0.068rem solid mix($secondaryContainer, $onSecondaryContainer, 90%);
border-top-left-radius: $rounding_small + 0.068rem;
border-top-right-radius: $rounding_small + 0.068rem;
padding: 0.341rem 0.477rem;
}
.sidebar-chat-codeblock-topbar-txt {
@include full-rounding;
padding: 0.273rem;
}
.sidebar-chat-codeblock-topbar-btn {
@include full-rounding;
padding: 0.273rem;
}
.sidebar-chat-codeblock-topbar-btn:hover,
.sidebar-chat-codeblock-topbar-btn:focus {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
}
.sidebar-chat-codeblock-topbar-btn:active {
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%);
}
.sidebar-chat-codeblock-code {
@include techfont;
padding: 0.682rem;
}
.sidebar-chat-divider {
min-height: 1px;
background-color: $sidebar_chat_textboxareaColor;
margin: 0rem 0.545rem;
}
.sidebar-chat-welcome-txt {
margin: 0rem 3.409rem;
}
.sidebar-chat-settings {
margin: 0rem 5.455rem;
}
.sidebar-chat-welcome-icon {
@include full-rounding;
font-size: 4rem;
}
.sidebar-chat-welcome-logo {
@include full-rounding;
@include menu_decel;
min-height: 4.773rem;
min-width: 4.773rem;
font-size: 4rem;
background-color: white;
color: black;
}
.sidebar-chat-chip {
@include menu_decel;
@include small-rounding;
padding: 0.341rem 0.477rem;
}
.sidebar-chat-chip-action {
border: 0.068rem solid $sidebar_chat_textboxareaColor;
}
.sidebar-chat-chip-action:hover,
.sidebar-chat-chip-action:focus {
background-color: $sidebar_chat_textboxareaColor;
}
.sidebar-chat-chip-action:active {
background-color: mix($sidebar_chat_textboxareaColor, $onSurfaceVariant, 70%);
color: mix($sidebar_chat_textboxareaColor, $surfaceVariant, 70%);
}
.sidebar-chat-chip-action-active {
color: $sidebar_chat_textboxareaColor;
border: 0.068rem solid $sidebar_chat_textboxareaColor;
}
+8
View File
@@ -0,0 +1,8 @@
// To prevent errors
$color1: $surfaceVariant;
$color2: $surfaceVariant;
$color3: $surfaceVariant;
$color4: $secondaryContainer;
$color5: $secondaryContainer;
$color6: $secondaryContainer;
$color7: $onBackground;
+34
View File
@@ -0,0 +1,34 @@
// Reset
* {
all: unset;
}
// Colors
@import './material'; // Material colors
@import './colors'; // Global color definitions. Uses material colors as base.
@import './lib_mixins';
@import './lib_classes';
@import './common'; // Context menu n stuff
// Components
@import './bar';
@import './cheatsheet';
@import './desktopbackground';
@import './dock';
@import './osd';
@import './overview';
@import './osk';
@import './sidebars';
@import './session';
@import './notifications';
// Music is put last as it might mess stuff up with pywal
@import './music'; // Everything related to music is here
// 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);
}
+58
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;
+261
View File
@@ -0,0 +1,261 @@
import { Utils, Widget } from '../imports.js';
import Service from 'resource:///com/github/Aylur/ags/service.js';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Soup from 'gi://Soup?version=3.0';
import { fileExists } from './messages.js';
// This is for custom prompt
// It's hard to make gpt-3.5 listen to all these, I know
// Disabled by default
const initMessages =
[
{
role: "user",
content: `
## Style
- You should a natural tone like a real conversation!
## Formatting
- Try to use **bold**, _italics_ and __underline__ extensively. Using bullet points is also encouraged.
- When providing code blocks or facts, precede with h2 heading (\`##\`) and use 2 spaces for indentation, not 4.
- Use dividers (\`---\`) to separate different information.
## Content
- When asked to perform system tasks, include a bash code block to handle it on a Linux desktop with Wayland.
- Unless requested otherwise or asked writing questions, be as short and concise as possible.
`,
thinking: false,
done: true
},
{
role: "assistant",
content: "Got it! I'll try to give commands to perform Linux tasks. I'll try to use markdown features extensively, use divider when appropriate, and use a heading for code blocks. All code blocks should use 2 spaces for indent. And most importantly, I'll speak naturally.",
thinking: false,
done: true,
}
]
function expandTilde(path) {
if (path.startsWith('~')) {
return GLib.get_home_dir() + path.slice(1);
} else {
return path;
}
}
// We're using many models to not be restricted to 3 messages per minute.
// The whole chat will be sent every request anyway.
const KEY_FILE_LOCATION = `~/.cache/ags/user/openai_api_key.txt`;
const CHAT_MODELS = ["gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0613"]
const ONE_CYCLE_COUNT = 3;
class ChatGPTMessage extends Service {
static {
Service.register(this,
{
'delta': ['string'],
},
{
'content': ['string'],
'thinking': ['boolean'],
'done': ['boolean'],
});
}
_role = '';
_content = '';
_thinking = false;
_done = false;
constructor(role, content, thinking = false, done = false) {
super();
this._role = role;
this._content = content;
this._thinking = thinking;
this._done = done;
}
get done() { return this._done }
set done(isDone) { this._done = isDone; this.notify('done') }
get role() { return this._role }
set role(role) { this._role = role; this.emit('changed') }
get content() { return this._content }
set content(content) {
this._content = content;
this.notify('content')
this.emit('changed')
}
get label() { return this._parserState.parsed + this._parserState.stack.join('') }
get thinking() { return this._thinking }
set thinking(thinking) {
this._thinking = thinking;
this.notify('thinking')
this.emit('changed')
}
addDelta(delta) {
if (this.thinking) {
this.thinking = false;
this.content = delta;
}
else {
this.content += delta;
}
this.emit('delta', delta);
}
}
class ChatGPTService extends Service {
static {
Service.register(this, {
'initialized': [],
'clear': [],
'newMsg': ['int'],
'hasKey': ['boolean'],
});
}
_assistantPrompt = false;
_messages = [];
_cycleModels = true;
_requestCount = 0;
_modelIndex = 0;
_key = '';
_decoder = new TextDecoder();
url = GLib.Uri.parse('https://api.openai.com/v1/chat/completions', GLib.UriFlags.NONE);
constructor() {
super();
if (fileExists(expandTilde(KEY_FILE_LOCATION))) this._key = Utils.readFile(expandTilde(KEY_FILE_LOCATION)).trim();
else this.emit('hasKey', false);
if (this._assistantPrompt) this._messages = [...initMessages];
else this._messages = [];
this.emit('initialized');
}
get modelName() { return CHAT_MODELS[this._modelIndex] }
get keyPath() { return KEY_FILE_LOCATION }
get key() { return this._key }
set key(keyValue) {
this._key = keyValue;
Utils.writeFile(this._key, expandTilde(KEY_FILE_LOCATION))
.then(this.emit('hasKey', true))
.catch(err => print(err));
}
get cycleModels() { return this._cycleModels }
set cycleModels(value) {
this._cycleModels = value;
if (!value) this._modelIndex = 0;
else {
this._modelIndex = (this._requestCount - (this._requestCount % ONE_CYCLE_COUNT)) % CHAT_MODELS.length;
}
}
get messages() { return this._messages }
get lastMessage() { return this._messages[this._messages.length - 1] }
clear() {
if (this._assistantPrompt)
this._messages = [...initMessages];
else
this._messages = [];
this.emit('clear');
}
get assistantPrompt() { return this._assistantPrompt; }
set assistantPrompt(value) {
this._assistantPrompt = value;
if (value) this._messages = [...initMessages];
else this._messages = [];
}
readResponse(stream, aiResponse) {
stream.read_line_async(
0, null,
(stream, res) => {
if (!stream) return;
const [bytes] = stream.read_line_finish(res);
const line = this._decoder.decode(bytes);
if (line && line != '') {
let data = line.substr(6);
if (data == '[DONE]') return;
try {
const result = JSON.parse(data);
if (result.choices[0].finish_reason === 'stop') {
aiResponse.done = true;
return;
}
aiResponse.addDelta(result.choices[0].delta.content);
}
catch {
aiResponse.addDelta(line + '\n');
}
}
this.readResponse(stream, aiResponse);
});
}
addMessage(role, message) {
this._messages.push(new ChatGPTMessage(role, message));
this.emit('newMsg', this._messages.length - 1);
}
send(msg) {
this._messages.push(new ChatGPTMessage('user', msg));
this.emit('newMsg', this._messages.length - 1);
const aiResponse = new ChatGPTMessage('assistant', 'thinking...', true, false)
this._messages.push(aiResponse);
this.emit('newMsg', this._messages.length - 1);
const body = {
model: CHAT_MODELS[this._modelIndex],
messages: this._messages.map(msg => { let m = { role: msg.role, content: msg.content }; return m; }),
stream: true,
};
const session = new Soup.Session();
const message = new Soup.Message({
method: 'POST',
uri: this.url,
});
message.request_headers.append('Authorization', `Bearer ${this._key}`);
message.set_request_body_from_bytes('application/json', new GLib.Bytes(JSON.stringify(body)));
session.send_async(message, GLib.DEFAULT_PRIORITY, null, (_, result) => {
const stream = session.send_finish(result);
this.readResponse(new Gio.DataInputStream({
close_base_stream: true,
base_stream: stream
}), aiResponse);
});
if (this._cycleModels) {
this._requestCount++;
if (this._cycleModels)
this._modelIndex = (this._requestCount - (this._requestCount % ONE_CYCLE_COUNT)) % CHAT_MODELS.length;
}
}
}
export default new ChatGPTService();
+40
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;
+58
View File
@@ -0,0 +1,58 @@
const { Notify, GLib, Gio } = imports.gi;
import { Utils } from '../imports.js';
import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
export function fileExists(filePath) {
let file = Gio.File.new_for_path(filePath);
return file.query_exists(null);
}
const FIRST_RUN_FILE = "firstrun.txt";
const FIRST_RUN_PATH = GLib.build_filenamev([GLib.get_user_cache_dir(), "ags", "user", FIRST_RUN_FILE]);
const FIRST_RUN_FILE_CONTENT = "Just a file to confirm that you have been greeted ;)";
const APP_NAME = "ags";
const FIRST_RUN_NOTIF_TITLE = "Welcome!";
const FIRST_RUN_NOTIF_BODY = `Looks like this is your first run.\nHit <span foreground="#c06af1" font_weight="bold">Super + /</span> for a list of keybinds.`;
export async function firstRunWelcome() {
if (!fileExists(FIRST_RUN_PATH)) {
console.log('uuwuwuwuwuwuwuwuu');
Utils.writeFile(FIRST_RUN_FILE_CONTENT, FIRST_RUN_PATH)
.then(() => {
// Note that we add a little delay to make sure the cool circular progress works
Utils.execAsync(['bash', '-c',
`sleep 0.5; notify-send "Millis since epoch" "$(date +%s%N | cut -b1-13)"; sleep 0.5; notify-send '${FIRST_RUN_NOTIF_TITLE}' '${FIRST_RUN_NOTIF_BODY}' -a '${APP_NAME}' &`
]).catch(print)
})
.catch(print);
}
}
const BATTERY_WARN_LEVELS = [20, 15, 5];
const BATTERY_WARN_TITLES = ["Low battery", "Very low battery", 'Critical Battery']
const BATTERY_WARN_BODIES = ["Plug in the charger", "You there?", 'PLUG THE CHARGER ALREADY']
var batteryWarned = false;
async function batteryMessage() {
const perc = Battery.percent;
const charging = Battery.charging;
if(charging) {
batteryWarned = false;
return;
}
for (let i = BATTERY_WARN_LEVELS.length - 1; i >= 0; i--) {
if (perc <= BATTERY_WARN_LEVELS[i] && !charging && !batteryWarned) {
batteryWarned = true;
Utils.execAsync(['bash', '-c',
`notify-send "${BATTERY_WARN_TITLES[i]}" "${BATTERY_WARN_BODIES[i]}" -u critical -a 'ags' &`
]).catch(print);
break;
}
}
}
// Run them
firstRunWelcome();
Utils.timeout(1, () => {
Battery.connect('changed', () => batteryMessage());
})
+86
View File
@@ -0,0 +1,86 @@
const { Gio, Gdk, GLib, 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 = `${GLib.get_user_cache_dir()}/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;
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
// AGS Variables
export const showMusicControls = Variable(false, {})
export const showColorScheme = Variable(false, {})
globalThis['openMusicControls'] = showMusicControls;
globalThis['openColorScheme'] = showColorScheme;
+100
View File
@@ -0,0 +1,100 @@
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 { setupCursorHover } from "../../lib/cursorhover.js";
import { RoundedCorner } from "../../lib/roundedcorner.js";
import Brightness from '../../services/brightness.js';
import Indicator from '../../services/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;
},
onPrimaryClick: () => {
App.toggleWindow('sideleft');
},
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.Box({
vertical: true,
className: 'bar-space-button',
children: [
Widget.Scrollable({
hexpand: true, vexpand: true,
hscroll: 'automatic', vscroll: 'never',
child: Widget.Box({
vertical: true,
children: [
Widget.Label({
xalign: 0,
className: 'txt-smaller bar-topdesc txt',
connections: [[Hyprland.active.client, 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.active.client, label => { // Hyprland.active.client
label.label = Hyprland.active.client._title.length === 0 ? `Workspace ${Hyprland.active.workspace.id}` : truncateTitle(Hyprland.active.client._title);
}]
],
})
]
})
})
]
})]
}),
]
})
]
})
});
+53
View File
@@ -0,0 +1,53 @@
const { Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
const { execAsync, exec } = Utils;
import { ModuleLeftSpace } from "./leftspace.js";
import { ModuleMusic } from "./music.js";
import { ModuleRightSpace } from "./rightspace.js";
import { ModuleSystem } from "./system.js";
import { ModuleWorkspaces } from "./workspaces.js";
import { RoundedCorner } from "../../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'],
exclusivity: 'exclusive',
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);
}
}),
});
+82
View File
@@ -0,0 +1,82 @@
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";
import { showMusicControls } from '../../variables.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.css = `font-size: ${Math.max(mpris.position / mpris.length * 100, 0)}px;`
}
return AnimatedCircProg({
className: 'bar-music-circprog',
vpack: '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'),
onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value),
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']),
onMiddleClickRelease: () => 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({
vpack: 'center',
className: 'bar-music-playstate',
homogeneous: true,
children: [Widget.Label({
vpack: 'center',
className: 'bar-music-playstate-txt',
justification: 'center',
connections: [[Mpris, label => {
const mpris = Mpris.getPlayer('');
label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
}]],
})],
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 media';
}]],
})
})
]
})
]
})
});
+59
View File
@@ -0,0 +1,59 @@
import { App, 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 { execAsync } = Utils;
import Indicator from '../../services/indicator.js';
import { StatusIcons } from "../../lib/statusicons.js";
import { RoundedCorner } from "../../lib/roundedcorner.js";
import { Tray } from "./tray.js";
export const ModuleRightSpace = () => {
const barTray = Tray();
const barStatusIcons = StatusIcons({
className: 'bar-statusicons',
connections: [[App, (self, currentName, visible) => {
if (currentName === 'sideright') {
self.toggleClassName('bar-statusicons-active', visible);
}
}]],
});
return Widget.EventBox({
onScrollUp: () => {
if (!Audio.speaker) return;
Audio.speaker.volume += 0.03;
Indicator.popup(1);
},
onScrollDown: () => {
if (!Audio.speaker) return;
Audio.speaker.volume -= 0.03;
Indicator.popup(1);
},
// onHover: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', true) },
// onHoverLost: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', false) },
onPrimaryClick: () => App.toggleWindow('sideright'),
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']),
onMiddleClickRelease: () => 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',
children: [
Widget.Box({ hexpand: true, }),
barTray,
barStatusIcons,
],
}),
]
}),
RoundedCorner('topright', { className: 'corner-black' })
]
})
});
}
+149
View File
@@ -0,0 +1,149 @@
// 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;
const { GLib } = imports.gi;
import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
import { MaterialIcon } from '../../lib/materialicon.js';
const BATTERY_LOW = 20;
const BarClock = () => Widget.Box({
vpack: 'center',
className: 'spacing-h-5',
children: [
Widget.Label({
className: 'bar-clock',
connections: [[5000, label => {
label.label = GLib.DateTime.new_now_local().format("%H:%M");
}]],
}),
Widget.Label({
className: 'txt-norm txt',
label: '•',
}),
Widget.Label({
className: 'txt-smallie txt',
connections: [[5000, label => {
label.label = GLib.DateTime.new_now_local().format("%A, %d/%m");
}]],
}),
],
});
const BarBattery = () => {
const BarResourceValue = (name, icon, command) => Widget.Box({
vpack: 'center',
className: 'bar-batt spacing-h-5',
children: [
MaterialIcon(icon, 'small'),
Widget.ProgressBar({ // Progress
vpack: 'center', hexpand: true,
className: 'bar-prog-batt',
connections: [[5000, (progress) => execAsync(['bash', '-c', command])
.then((output) => {
progress.value = Number(output) / 100;
progress.tooltipText = `${name}: ${Number(output)}%`
})
.catch(print)
]],
}),
]
});
const batteryWidget = Widget.Box({
vpack: 'center',
hexpand: true,
className: 'spacing-h-5 bar-batt',
connections: [[Battery, box => {
box.toggleClassName('bar-batt-low', Battery.percent <= BATTERY_LOW);
box.toggleClassName('bar-batt-full', Battery.charged);
}]],
children: [
MaterialIcon('settings_heart', 'small'),
Widget.Label({ // Percentage
className: 'bar-batt-percentage',
connections: [[Battery, label => {
label.label = `${Battery.percent}`;
}]],
}),
Widget.ProgressBar({ // Progress
vpack: '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 <= BATTERY_LOW);
progress.toggleClassName('bar-prog-batt-full', Battery.charged);
batteryWidget.tooltipText = `Battery: ${Battery.percent}%`
}]],
}),
Widget.Revealer({ // A dot for charging state
transitionDuration: 150,
revealChild: false,
transition: 'slide_left',
child: Widget.Box({
className: 'spacing-h-3',
children: [
Widget.Box({
vpack: 'center',
className: 'bar-batt-chargestate-charging-smaller',
connections: [[Battery, box => {
box.toggleClassName('bar-batt-chargestate-low', Battery.percent <= BATTERY_LOW);
box.toggleClassName('bar-batt-chargestate-full', Battery.charged);
}]],
}),
Widget.Box({
vpack: 'center',
className: 'bar-batt-chargestate-charging',
connections: [[Battery, box => {
box.toggleClassName('bar-batt-chargestate-low', Battery.percent <= BATTERY_LOW);
box.toggleClassName('bar-batt-chargestate-full', Battery.charged);
}]],
}),
]
}),
connections: [[Battery, revealer => {
revealer.revealChild = Battery.charging;
}]],
}),
],
});
const memUsage = Widget.Box({
className: 'spacing-h-5',
children: [
BarResourceValue('RAM usage', 'memory', `free | awk '/^Mem/ {printf("%.2f\\n", ($3/$2) * 100)}'`),
BarResourceValue('Swap usage', 'swap_horiz', `free | awk '/^Swap/ {printf("%.2f\\n", ($3/$2) * 100)}'`),
]
})
const widgetStack = Widget.Stack({
transition: 'slide_up_down',
vpack: 'center',
hexpand: true,
items: [
['fallback', memUsage],
['battery', batteryWidget],
],
setup: (stack) => Utils.timeout(1, () => {
if (Battery.available) stack.shown = 'battery';
else stack.shown = 'fallback';
})
})
return widgetStack;
}
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: [
BarClock(),
BarBattery(),
],
}),
]
})
});
+66
View File
@@ -0,0 +1,66 @@
const { GLib, Gdk, Gtk } = imports.gi;
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({
hpack: 'center',
binds: [['icon', item, 'icon']]
}),
binds: [['tooltipMarkup', item, 'tooltip-markup']],
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({
vpack: '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,
]
});
}
+100
View File
@@ -0,0 +1,100 @@
const { GLib, Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
const WORKSPACE_SIDE_PAD = 0.546; // rem
const NUM_OF_WORKSPACES = 10;
let lastWorkspace = 0;
const activeWorkspaceIndicator = Widget.Box({
css: `
padding: 0rem ${WORKSPACE_SIDE_PAD}rem;
`,
children: [
Widget.Box({
vpack: 'center',
hpack: 'start',
className: 'bar-ws-active-box',
connections: [
[Hyprland.active.workspace, (box) => {
const ws = Hyprland.active.workspace.id;
box.setCss(`
margin-left: ${1.774 * (ws - 1) + 0.068}rem;
`);
lastWorkspace = ws;
}],
],
children: [
Widget.Label({
vpack: 'center',
className: 'bar-ws-active',
label: ``,
})
]
})
]
});
export const ModuleWorkspaces = () => Widget.EventBox({
onScrollUp: () => Utils.execAsync(['bash', '-c', 'hyprctl dispatch workspace -1 &']),
onScrollDown: () => Utils.execAsync(['bash', '-c', 'hyprctl dispatch workspace +1 &']),
onMiddleClickRelease: () => App.toggleWindow('overview'),
onSecondaryClickRelease: () => App.toggleWindow('osk'),
child: Widget.Box({
homogeneous: true,
className: 'bar-ws-width',
children: [
Widget.Overlay({
passThrough: true,
child: Widget.Box({
homogeneous: true,
className: 'bar-group-center',
children: [Widget.Box({
className: 'bar-group-standalone bar-group-pad',
})]
}),
overlays: [
Widget.Overlay({
setup: (self) => self.set_overlay_pass_through(self.get_children()[1], true),
child: Widget.Box({
hpack: 'center',
css: `
padding: 0rem ${WORKSPACE_SIDE_PAD}rem;
`,
// homogeneous: true,
children: Array.from({ length: NUM_OF_WORKSPACES }, (_, i) => i + 1).map(i => Widget.Button({
onPrimaryClick: () => Utils.execAsync(['bash', '-c', `hyprctl dispatch workspace ${i} &`]).catch(print),
child: Widget.Label({
vpack: 'center',
label: `${i}`,
className: 'bar-ws txt',
}),
})),
connections: [
[Hyprland, (box) => {
// console.log('update');
const kids = box.children;
kids.forEach((child, i) => {
child.child.toggleClassName('bar-ws-occupied', false);
child.child.toggleClassName('bar-ws-occupied-left', false);
child.child.toggleClassName('bar-ws-occupied-right', false);
child.child.toggleClassName('bar-ws-occupied-left-right', false);
});
const occupied = Array.from({ length: NUM_OF_WORKSPACES }, (_, i) => Hyprland.getWorkspace(i + 1)?.windows > 0);
for (let i = 0; i < occupied.length; i++) {
if (!occupied[i]) continue;
const child = kids[i];
child.child.toggleClassName(`bar-ws-occupied${!occupied[i - 1] ? '-left' : ''}${!occupied[i + 1] ? '-right' : ''}`, true);
}
}, 'notify::workspaces'],
],
}),
overlays: [
activeWorkspaceIndicator,
]
})
],
})
]
})
});
@@ -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",
}))
})
]
})
]
}))
})),
});
+90
View File
@@ -0,0 +1,90 @@
const { Gdk, Gtk } = imports.gi;
import { Service, Widget } from '../../imports.js';
import { Keybinds } from "./keybinds.js";
import { setupCursorHover } from "../../lib/cursorhover.js";
const cheatsheetHeader = () => Widget.CenterBox({
vertical: false,
startWidget: Widget.Box({}),
centerWidget: Widget.Box({
vertical: true,
className: "spacing-h-15",
children: [
Widget.Box({
hpack: 'center',
className: 'spacing-h-5',
children: [
Widget.Label({
hpack: 'center',
css: 'margin-right: 0.682rem;',
className: 'txt-title txt',
label: 'Cheat sheet',
}),
Widget.Label({
vpack: 'center',
className: "cheatsheet-key txt-small",
label: "",
}),
Widget.Label({
vpack: 'center',
className: "cheatsheet-key-notkey txt-small",
label: "+",
}),
Widget.Label({
vpack: 'center',
className: "cheatsheet-key txt-small",
label: "/",
})
]
}),
Widget.Label({
useMarkup: true,
selectable: true,
justify: Gtk.Justification.CENTER,
className: 'txt-small txt',
label: 'Sheet data stored in <tt>~/.config/ags/data/keybinds.js</tt>\nChange keybinds in <tt>~/.config/hypr/keybinds.conf</tt>'
}),
]
}),
endWidget: Widget.Button({
vpack: 'start',
hpack: '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: setupCursorHover,
}),
});
const clickOutsideToClose = Widget.EventBox({
onPrimaryClick: () => App.closeWindow('cheatsheet'),
onSecondaryClick: () => App.closeWindow('cheatsheet'),
onMiddleClick: () => App.closeWindow('cheatsheet'),
});
export default () => Widget.Window({
name: 'cheatsheet',
exclusivity: 'normal',
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(),
]
}),
],
})
});
@@ -0,0 +1,83 @@
const { Gdk, Gtk } = imports.gi;
const Lang = imports.lang;
import { App, Service, Utils, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
const { execAsync, exec } = Utils;
const { Box, Label } = Widget;
const NUM_OF_VERTICES = 30;
const NUM_OF_EDGES = 29;
// Vertices
var vertices = [];
for (var i = 0; i < NUM_OF_VERTICES; i++) {
vertices.push([
Math.floor(Math.random() * SCREEN_WIDTH),
Math.floor(Math.random() * SCREEN_HEIGHT)
]);
}
// Edges
function generateRandomEdges(numVertices, numEdges) { // TODO: make sure whole graph is connected
var edges = new Set();
var vertices = [];
// Generate vertices
for (var i = 0; i < numVertices; i++) {
vertices.push(i);
}
// Generate random distinct edges
while (edges.size < numEdges) {
var randomVertex1 = vertices[Math.floor(Math.random() * numVertices)];
var randomVertex2 = vertices[Math.floor(Math.random() * numVertices)];
// Ensure the two vertices are distinct and the edge doesn't already exist
if (randomVertex1 !== randomVertex2) {
var edge = [randomVertex1, randomVertex2].sort();
edges.add(edge.join(','));
}
}
return Array.from(edges).map(edge => edge.split(',').map(Number));
}
var edges = generateRandomEdges(NUM_OF_VERTICES, NUM_OF_EDGES);
export default () => Box({
hpack: 'fill',
vpack: 'fill',
homogeneous: true,
children: [
Widget.DrawingArea({
className: 'bg-graph',
setup: (area) => {
area.connect('draw', Lang.bind(area, (area, cr) => {
// area.set_size_request(SCREEN_WIDTH, SCREEN_HEIGHT);
// console.log('allocated width/height:', area.get_allocated_width(), '/', area.get_allocated_height())
const styleContext = area.get_style_context();
const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL);
const backgroundColor = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
const radius = area.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
const borderWidth = area.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway
cr.setSourceRGBA(backgroundColor.red, backgroundColor.green, backgroundColor.blue, backgroundColor.alpha);
cr.rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
cr.fill();
cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha);
// Draw edges
cr.setLineWidth(borderWidth);
console.log("line width:", borderWidth);
for (var i = 0; i < NUM_OF_EDGES; i++) {
console.log(vertices[edges[i][0]][0], vertices[edges[i][0]][1], '->', vertices[edges[i][1]][0], vertices[edges[i][1]][1])
cr.moveTo(vertices[edges[i][0]][0], vertices[edges[i][0]][1]);
cr.lineTo(vertices[edges[i][1]][0], vertices[edges[i][1]][1]);
cr.stroke();
}
// Draw vertices
for (var i = 0; i < NUM_OF_VERTICES; i++) {
cr.arc(vertices[i][0], vertices[i][1], radius, 0, 2 * Math.PI)
cr.fill()
}
}))
}
})
]
})
@@ -0,0 +1,27 @@
const { Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
const { execAsync, exec } = Utils;
import TimeAndLaunchesWidget from './timeandlaunches.js'
import SystemWidget from './system.js'
import GraphWidget from './graph.js'
export default () => Widget.Window({
name: 'desktopbackground',
anchor: ['top', 'bottom', 'left', 'right'],
layer: 'background',
exclusivity: 'normal',
visible: true,
child: Widget.Overlay({
child: Widget.Box({
hexpand: true,
vexpand: true,
}),
overlays: [
// GraphWidget(),
TimeAndLaunchesWidget(),
SystemWidget(),
],
setup: (self) => self.set_overlay_pass_through(self.get_children()[1], true),
}),
});
@@ -0,0 +1,151 @@
import { App, Service, Utils, Widget } from '../../imports.js';
const { execAsync, exec } = Utils;
const { Box, EventBox, Label, Revealer, Overlay } = Widget;
import { AnimatedCircProg } from '../../lib/animatedcircularprogress.js'
import { MaterialIcon } from '../../lib/materialicon.js';
const ResourceValue = (name, icon, interval, valueUpdateCmd, displayFunc, props = {}) => Box({
...props,
className: 'bg-system-bg txt',
children: [
Revealer({
transition: 'slide_left',
transitionDuration: 200,
child: Box({
vpack: 'center',
vertical: true,
className: 'margin-right-15',
children: [
Label({
xalign: 1,
className: 'txt-small txt',
label: `${name}`,
}),
Label({
xalign: 1,
className: 'titlefont txt-norm txt-onSecondaryContainer',
connections: [[interval, (label) => displayFunc(label)]]
})
]
})
}),
Overlay({
child: AnimatedCircProg({
className: 'bg-system-circprog',
connections: [[interval, (self) => {
execAsync(['bash', '-c', `${valueUpdateCmd}`]).then((newValue) => {
self.css = `font-size: ${Math.round(newValue)}px;`
}).catch(print);
}]]
}),
overlays: [
MaterialIcon(`${icon}`, 'hugeass'),
],
setup: self => self.set_overlay_pass_through(self.get_children()[1], true),
}),
]
})
const resources = Box({
vpack: 'fill',
vertical: true,
className: 'spacing-v-15',
children: [
ResourceValue('Memory', 'memory', 10000, `free | awk '/^Mem/ {printf("%.2f\\n", ($3/$2) * 100)}'`,
(label) => {
execAsync(['bash', '-c', `free -h | awk '/^Mem/ {print $3 " / " $2}' | sed 's/Gi/Gib/g'`])
.then((output) => {
label.label = `${output}`
}).catch(print);
}, { hpack: 'end' }),
ResourceValue('Swap', 'swap_horiz', 10000, `free | awk '/^Swap/ {printf("%.2f\\n", ($3/$2) * 100)}'`,
(label) => {
execAsync(['bash', '-c', `free -h | awk '/^Swap/ {print $3 " / " $2}' | sed 's/Gi/Gib/g'`])
.then((output) => {
label.label = `${output}`
}).catch(print);
}, { hpack: 'end' }),
ResourceValue('Disk space', 'hard_drive_2', 3600000, `echo $(df --output=pcent / | tr -dc '0-9')`,
(label) => {
execAsync(['bash', '-c', `df -h --output=avail / | awk 'NR==2{print $1}'`])
.then((output) => {
label.label = `${output} available`
}).catch(print);
}, { hpack: 'end' }),
]
});
const distroAndVersion = Box({
vertical: true,
children: [
Box({
hpack: 'end',
children: [
Label({
className: 'bg-distro-txt',
xalign: 0,
label: 'Hyping on ',
}),
Label({
className: 'bg-distro-name',
xalign: 0,
label: '<distro>',
setup: (label) => {
execAsync([`grep`, `-oP`, `PRETTY_NAME="\\K[^"]+`, `/etc/os-release`]).then(distro => {
label.label = distro;
}).catch(print);
},
}),
]
}),
Box({
hpack: 'end',
children: [
Label({
className: 'bg-distro-txt',
xalign: 0,
label: 'with ',
}),
Label({
className: 'bg-distro-name',
xalign: 0,
label: '<version>',
setup: (label) => {
execAsync([`bash`, `-c`, `hyprctl version | grep -oP "Tag: v\\K\\d+\\.\\d+\\.\\d+"`]).then(distro => {
label.label = `Hyprland ${distro}`;
}).catch(print);
},
}),
]
})
]
})
export default () => Box({
hpack: 'end',
vpack: 'end',
children: [
EventBox({
child: Box({
hpack: 'end',
vpack: 'end',
className: 'bg-distro-box spacing-v-20',
vertical: true,
children: [
resources,
distroAndVersion,
]
}),
onPrimaryClickRelease: () => {
const kids = resources.get_children();
kids.forEach((child, i) => {
child.get_children()[0].revealChild = !child.get_children()[0].revealChild;
});
},
})
],
})
@@ -0,0 +1,69 @@
const { GLib, Gio } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
const { execAsync, exec } = Utils;
const { Box, Label, Button, Revealer, EventBox } = Widget;
import { setupCursorHover } from '../../lib/cursorhover.js';
import { quickLaunchItems } from '../../data/quicklaunches.js'
const TimeAndDate = () => Box({
vertical: true,
className: 'spacing-v--5',
children: [
Label({
className: 'bg-time-clock',
xalign: 0,
connections: [[5000, label => {
label.label = GLib.DateTime.new_now_local().format("%H:%M");
}]],
}),
Label({
className: 'bg-time-date',
xalign: 0,
connections: [[5000, label => {
label.label = GLib.DateTime.new_now_local().format("%A, %d/%m/%Y");
}]],
}),
]
})
const QuickLaunches = () => Box({
vertical: true,
className: 'spacing-v-10',
children: [
Label({
xalign: 0,
className: 'bg-quicklaunch-title',
label: 'Quick Launches',
}),
Box({
hpack: 'start',
className: 'spacing-h-5',
children: quickLaunchItems.map((item, i) => Button({
onClicked: () => {
execAsync(['bash', '-c', `${item["command"]}`]).catch(print);
},
className: 'bg-quicklaunch-btn',
child: Label({
label: `${ item["name"]}`,
}),
setup: (self) => {
setupCursorHover(self);
}
})),
})
]
})
export default () => Box({
hpack: 'start',
vpack: 'end',
vertical: true,
className: 'bg-time-box spacing-v-20',
children: [
TimeAndDate(),
// QuickLaunches(),
],
})
+264
View File
@@ -0,0 +1,264 @@
const { Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
const { execAsync, exec } = Utils;
const { Box, EventBox, Label, Revealer, Overlay } = Widget;
import { AnimatedCircProg } from '../../lib/animatedcircularprogress.js'
import { MaterialIcon } from '../../lib/materialicon.js';
import { setupCursorHover, setupCursorHoverAim } from "../../lib/cursorhover.js";
const ANIMATION_TIME = 150;
const pinnedApps = [
'firefox',
'org.gnome.Nautilus',
];
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;
}
const focus = ({ address }) => Utils.execAsync(`hyprctl dispatch focuswindow address:${address}`);
const DockSeparator = (props = {}) => Box({
...props,
className: 'dock-separator',
})
const AppButton = ({ icon, ...rest }) => Widget.Revealer({
properties: [
['workspace', 0],
],
revealChild: false,
transition: 'slide_right',
transitionDuration: ANIMATION_TIME,
child: Widget.Button({
...rest,
className: 'dock-app-btn',
child: Widget.Box({
child: Widget.Overlay({
child: Widget.Box({
homogeneous: true,
className: 'dock-app-icon',
child: Widget.Icon({
icon: icon,
setup: (self) => Utils.timeout(1, () => {
const styleContext = self.get_parent().get_style_context();
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
self.size = Math.max(width, height, 1);
})
}),
}),
overlays: [Widget.Box({
class_name: 'indicator',
vpack: 'end',
hpack: 'center',
})],
}),
}),
setup: (button) => {
setupCursorHover(button);
}
})
});
const Taskbar = () => Widget.Box({
className: 'dock-apps',
properties: [
['map', new Map()],
['clientSortFunc', (a, b) => {
return a._workspace > b._workspace;
}],
['update', (box) => {
Hyprland.clients.forEach(client => {
if (client["pid"] == -1) return;
const appClass = substitute(client.class);
for (const appName of pinnedApps) {
if (appClass.includes(appName.toLowerCase()))
return null;
}
const newButton = AppButton({
icon: appClass,
tooltipText: `${client.title} (${appClass})`,
onClicked: () => focus(client),
});
newButton._workspace = client.workspace.id;
newButton.revealChild = true;
box._map.set(client.address, newButton);
})
box.children = Array.from(box._map.values());
}],
['add', (box, address) => {
if (!address) { // Since the first active emit is undefined
box._update(box);
return;
}
const newClient = Hyprland.clients.find(client => {
return client.address == address;
});
const appClass = substitute(newClient.class);
const newButton = AppButton({
icon: appClass,
tooltipText: `${newClient.title} (${appClass})`,
onClicked: () => focus(newClient),
})
newButton._workspace = newClient.workspace.id;
box._map.set(address, newButton);
box.children = Array.from(box._map.values());
newButton.revealChild = true;
}],
['remove', (box, address) => {
if (!address) return;
const removedButton = box._map.get(address);
removedButton.revealChild = false;
Utils.timeout(ANIMATION_TIME, () => {
removedButton.destroy();
box._map.delete(address);
box.children = Array.from(box._map.values());
})
}],
],
connections: [
// [Hyprland, (box) => box._update(box)],
[Hyprland, (box, address) => box._add(box, address), 'client-added'],
[Hyprland, (box, address) => box._remove(box, address), 'client-removed'],
],
setup: (self) => {
Utils.timeout(100, () => self._update(self));
}
});
const PinnedApps = () => Widget.Box({
class_name: 'dock-apps',
homogeneous: true,
children: pinnedApps
.map(term => ({ app: Applications.query(term)?.[0], term }))
.filter(({ app }) => app)
.map(({ app, term = true }) => {
const newButton = AppButton({
icon: app.icon_name,
onClicked: () => {
for (const client of Hyprland.clients) {
if (client.class.toLowerCase().includes(term))
return focus(client);
}
app.launch();
},
onMiddleClick: () => app.launch(),
setup: (self) => {
self.revealChild = true;
},
tooltipText: app.name,
connections: [[Hyprland, button => {
const running = Hyprland.clients
.find(client => client.class.toLowerCase().includes(term)) || false;
button.toggleClassName('nonrunning', !running);
button.toggleClassName('focused', Hyprland.active.client.address == running.address);
button.set_tooltip_text(running ? running.title : app.name);
}, 'notify::clients']],
})
newButton.revealChild = true;
return newButton;
}),
});
export default () => {
const dockContent = Box({
className: 'dock-bg spacing-h-5',
children: [
PinnedApps(),
DockSeparator(),
Taskbar(),
]
})
const dockRevealer = Revealer({
properties: [
['updateShow', self => { // I only use mouse to resize. I don't care about keyboard resize if that's a thing
const dockSize = [
dockContent.get_allocated_width(),
dockContent.get_allocated_height()
]
const dockAt = [
SCREEN_WIDTH / 2 - dockSize[0] / 2,
SCREEN_HEIGHT - dockSize[1],
];
const dockLeft = dockAt[0];
const dockRight = dockAt[0] + dockSize[0];
const dockTop = dockAt[1];
const dockBottom = dockAt[1] + dockSize[1];
const currentWorkspace = Hyprland.active.workspace.id;
var toReveal = true;
const hyprlandClients = JSON.parse(exec('hyprctl clients -j'));
for (const index in hyprlandClients) {
const client = hyprlandClients[index];
const clientLeft = client.at[0];
const clientRight = client.at[0] + client.size[0];
const clientTop = client.at[1];
const clientBottom = client.at[1] + client.size[1];
if (client.workspace.id == currentWorkspace) {
if (
clientLeft < dockRight &&
clientRight > dockLeft &&
clientTop < dockBottom &&
clientBottom > dockTop
) {
self.revealChild = false;
return;
}
}
}
self.revealChild = true;
}]
],
revealChild: false,
transition: 'slide_up',
transitionDuration: 200,
child: dockContent,
connections: [
// [Hyprland, (self) => self._updateShow(self)],
// [Hyprland.active.workspace, (self) => self._updateShow(self)],
// [Hyprland.active.client, (self) => self._updateShow(self)],
// [Hyprland, (self) => self._updateShow(self), 'client-added'],
// [Hyprland, (self) => self._updateShow(self), 'client-removed'],
],
})
return EventBox({
onHover: () => {
dockRevealer.revealChild = true;
},
onHoverLost: () => {
if (Hyprland.active.client._class.length === 0) return;
dockRevealer.revealChild = false;
},
child: Box({
homogeneous: true,
css: 'min-height: 2px;',
children: [
dockRevealer,
]
}),
})
}
+11
View File
@@ -0,0 +1,11 @@
import { App, Widget } from '../../imports.js';
import Dock from './dock.js';
export default () => Widget.Window({
name: 'dock',
layer: 'bottom',
anchor: ['bottom'],
exclusivity: 'normal',
visible: true,
child: Dock(),
});
@@ -0,0 +1,58 @@
const { Gio, GLib, Gtk } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
const { exec, execAsync } = Utils;
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
import { MaterialIcon } from '../../lib/materialicon.js';
import { showColorScheme } from '../../variables.js';
const ColorBox = ({
name = 'Color',
...rest
}) => Box({
...rest,
homogeneous: true,
children: [
Label({
label: `${name}`,
})
]
})
const colorschemeContent = Box({
className: 'osd-colorscheme spacing-v-5',
vertical: true,
hpack: 'center',
children: [
Label({
xalign: 0,
className: 'txt-norm titlefont txt',
label: 'Colorscheme',
}),
Box({
className: 'spacing-h-5',
children: [
ColorBox({ name: 'P', className: 'osd-color osd-color-primary' }),
ColorBox({ name: 'P-c', className: 'osd-color osd-color-primaryContainer' }),
ColorBox({ name: 'S', className: 'osd-color osd-color-secondary' }),
ColorBox({ name: 'S-c', className: 'osd-color osd-color-secondaryContainer' }),
ColorBox({ name: 'Sf-v', className: 'osd-color osd-color-surfaceVariant' }),
ColorBox({ name: 'Sf', className: 'osd-color osd-color-surface' }),
ColorBox({ name: 'Bg', className: 'osd-color osd-color-background' }),
]
})
]
});
export default () => Widget.Revealer({
transition: 'slide_down',
transitionDuration: 200,
child: colorschemeContent,
connections: [
[showColorScheme, (revealer) => {
revealer.revealChild = showColorScheme.value;
}],
],
})
@@ -0,0 +1,76 @@
// This file is for brightness/volume indicators
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 '../../services/brightness.js';
import Indicator from '../../services/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;
}]],
);
export default () => Widget.Revealer({
transition: 'slide_down',
connections: [
[Indicator, (revealer, value) => {
revealer.revealChild = (value > -1);
}, 'popup'],
],
child: Widget.Box({
hpack: 'center',
vertical: false,
children: [
brightnessIndicator,
volumeIndicator,
]
})
});
+30
View File
@@ -0,0 +1,30 @@
import { Widget } from '../../imports.js';
import Indicator from '../../services/indicator.js';
import IndicatorValues from './indicatorvalues.js';
import MusicControls from './musiccontrols.js';
import ColorScheme from './colorscheme.js';
import NotificationPopups from './notificationpopups.js';
export default (monitor) => Widget.Window({
name: `indicator${monitor}`,
monitor,
className: 'indicator',
layer: 'overlay',
visible: true,
anchor: ['top'],
child: Widget.EventBox({
onHover: () => { //make the widget hide when hovering
Indicator.popup(-1);
},
child: Widget.Box({
vertical: true,
css: 'min-height: 2px;',
children: [
IndicatorValues(),
MusicControls(),
NotificationPopups(),
ColorScheme(),
]
})
}),
});
@@ -0,0 +1,373 @@
const { Gio, GLib, Gtk } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
const { exec, execAsync } = Utils;
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
import { MaterialIcon } from '../../lib/materialicon.js';
import { showMusicControls } from '../../variables.js';
function expandTilde(path) {
if (path.startsWith('~')) {
return GLib.get_home_dir() + path.slice(1);
} else {
return path;
}
}
const LIGHTDARK_FILE_LOCATION = '~/.cache/ags/user/colormode.txt'
const lightDark = Utils.readFile(expandTilde(LIGHTDARK_FILE_LOCATION)).trim();
const COVER_COLORSCHEME_SUFFIX = '_colorscheme.css';
const PREFERRED_PLAYER = 'plasma-browser-integration';
var lastCoverPath = '';
function isRealPlayer(player) {
return (
!player.busName.startsWith('org.mpris.MediaPlayer2.firefox') &&
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld')
);
}
export const getPlayer = (name = PREFERRED_PLAYER) => {
return Mpris.getPlayer(name) || Mpris.players[0] || null;
}
function lengthStr(length) {
const min = Math.floor(length / 60);
const sec = Math.floor(length % 60);
const sec0 = sec < 10 ? '0' : '';
return `${min}:${sec0}${sec}`;
}
function fileExists(filePath) {
let file = Gio.File.new_for_path(filePath);
return file.query_exists(null);
}
function detectMediaSource(link) {
if (link.startsWith("file://")) {
if (link.includes('firefox-mpris'))
return '󰈹 Firefox'
return "󰈣 File";
}
// Remove protocol if present
let url = link.replace(/(^\w+:|^)\/\//, '');
// Extract the domain name
let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1];
if (domain == 'ytimg.com')
return '󰗃 Youtube';
if (domain == 'discordapp.net')
return '󰙯 Discord';
if (domain == 'sndcdn.com')
return '󰓀 SoundCloud';
return domain;
}
const DEFAULT_MUSIC_FONT = 'Gabarito, sans-serif';
function getTrackfont(player) {
const title = player.trackTitle;
const artists = player.trackArtists.join(' ');
if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo')) return 'Chakra Petch'; // Rigid square replacement
if (title.includes('東方')) return 'Crimson Text, serif'; // Serif for Touhou stuff
return DEFAULT_MUSIC_FONT;
}
const TrackProgress = ({ player, ...rest }) => {
const _updateProgress = (circprog) => {
const player = Mpris.getPlayer();
if (!player) return;
// Set circular progress (see definition of AnimatedCircProg for explanation)
circprog.css = `font-size: ${Math.max(player.position / player.length * 100, 0)}px;`
}
return AnimatedCircProg({
...rest,
className: 'osd-music-circprog',
vpack: 'center',
connections: [ // Update on change/once every 3 seconds
[Mpris, _updateProgress],
[3000, _updateProgress]
],
})
}
const TrackTitle = ({ player, ...rest }) => Label({
...rest,
label: 'No music playing',
xalign: 0,
truncate: 'end',
// wrap: true,
className: 'osd-music-title',
connections: [[player, (self) => {
// Player name
self.label = player.trackTitle.length > 0 ? player.trackTitle : 'No media';
// Font based on track/artist
const fontForThisTrack = getTrackfont(player);
self.css = `font-family: ${fontForThisTrack}, ${DEFAULT_MUSIC_FONT};`;
}, 'notify::track-title']]
});
const TrackArtists = ({ player, ...rest }) => Label({
...rest,
xalign: 0,
className: 'osd-music-artists',
truncate: 'end',
connections: [[player, (self) => {
self.label = player.trackArtists.length > 0 ? player.trackArtists.join(', ') : '';
}, 'notify::track-artists']]
})
const CoverArt = ({ player, ...rest }) => Box({
...rest,
className: 'osd-music-cover',
children: [
Widget.Overlay({
child: Box({ // Fallback
className: 'osd-music-cover-fallback',
homogeneous: true,
children: [Label({
className: 'icon-material txt-hugeass',
label: 'music_note',
})]
}),
overlays: [ // Real
Box({
properties: [
['updateCover', (self) => {
const player = Mpris.getPlayer();
// Player closed
// Note that cover path still remains, so we're checking title
if (!player || player.trackTitle == "") {
self.css = `background-image: none;`;
App.applyCss(`${App.configDir}/style.css`);
return;
}
const coverPath = player.coverPath;
const stylePath = `${player.coverPath}${lightDark}${COVER_COLORSCHEME_SUFFIX}`;
if (player.coverPath == lastCoverPath) { // Since 'notify::cover-path' emits on cover download complete
self.css = `background-image: url('${coverPath}');`;
}
lastCoverPath = player.coverPath;
// If a colorscheme has already been generated, skip generation
if (fileExists(stylePath)) {
self.css = `background-image: url('${coverPath}');`;
App.applyCss(stylePath);
return;
}
// Generate colors
execAsync(['bash', '-c',
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' > ${App.configDir}/scss/_musicmaterial.scss ${lightDark}`])
.then(() => {
exec(`wal -i "${player.coverPath}" -n -t -s -e -q ${lightDark}`)
exec(`bash -c "cp ~/.cache/wal/colors.scss ${App.configDir}/scss/_musicwal.scss"`)
exec(`sassc ${App.configDir}/scss/_music.scss ${stylePath}`);
self.css = `background-image: url('${coverPath}');`;
App.applyCss(`${stylePath}`);
})
.catch(print);
}],
],
className: 'osd-music-cover-art',
connections: [
[player, (self) => self._updateCover(self), 'notify::cover-path']
],
})
]
})
],
})
const TrackControls = ({ player, ...rest }) => Widget.Revealer({
revealChild: false,
transition: 'slide_right',
transitionDuration: 200,
child: Widget.Box({
...rest,
vpack: 'center',
className: 'osd-music-controls spacing-h-3',
children: [
Button({
className: 'osd-music-controlbtn',
child: Label({
className: 'icon-material osd-music-controlbtn-txt',
label: 'skip_previous',
})
}),
Button({
className: 'osd-music-controlbtn',
child: Label({
className: 'icon-material osd-music-controlbtn-txt',
label: 'skip_next',
})
}),
],
}),
connections: [[Mpris, (self) => {
const player = Mpris.getPlayer();
if (!player)
self.revealChild = false;
else
self.revealChild = true;
}, 'notify::play-back-status']]
});
const TrackSource = ({ player, ...rest }) => Widget.Revealer({
revealChild: false,
transition: 'slide_left',
transitionDuration: 200,
child: Widget.Box({
...rest,
className: 'osd-music-pill spacing-h-5',
homogeneous: true,
children: [
Label({
hpack: 'fill',
justification: 'center',
className: 'icon-nerd',
connections: [[player, (self) => {
self.label = detectMediaSource(player.trackCoverUrl);
}, 'notify::cover-path']]
}),
],
}),
connections: [[Mpris, (self) => {
const mpris = Mpris.getPlayer('');
if (!mpris)
self.revealChild = false;
else
self.revealChild = true;
}]]
});
const TrackTime = ({ player, ...rest }) => {
return Widget.Revealer({
revealChild: false,
transition: 'slide_left',
transitionDuration: 200,
child: Widget.Box({
...rest,
vpack: 'center',
className: 'osd-music-pill spacing-h-5',
children: [
Label({
connections: [[1000, (self) => {
const player = Mpris.getPlayer();
if (!player) return;
self.label = lengthStr(player.position);
}]]
}),
Label({ label: '/' }),
Label({
connections: [[Mpris, (self) => {
const player = Mpris.getPlayer();
if (!player) return;
self.label = lengthStr(player.length);
}]]
}),
],
}),
connections: [[Mpris, (self) => {
if (!player)
self.revealChild = false;
else
self.revealChild = true;
}]]
})
}
const PlayState = ({ player }) => {
var position = 0;
const trackCircProg = TrackProgress({ player: player });
return Widget.Button({
className: 'osd-music-playstate',
child: Widget.Overlay({
child: trackCircProg,
overlays: [
Widget.Button({
className: 'osd-music-playstate-btn',
onClicked: () => {
Mpris.getPlayer().playPause()
},
child: Widget.Label({
justification: 'center',
hpack: 'fill',
vpack: 'center',
connections: [[player, (label) => {
label.label = `${player.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
}, 'notify::play-back-status']],
}),
}),
],
// setup: self => Utils.timeout(1, () => {
// self.set_overlay_pass_through(self.get_children()[1], true);
// }),
passThrough: true,
})
});
}
const MusicControlsWidget = (player) => Box({
className: 'osd-music spacing-h-20',
children: [
CoverArt({ player: player, vpack: 'center' }),
Box({
vertical: true,
className: 'spacing-v-5 osd-music-info',
children: [
Box({
vertical: true,
vpack: 'center',
hexpand: true,
children: [
TrackTitle({ player: player }),
TrackArtists({ player: player }),
]
}),
Box({ vexpand: true }),
Box({
className: 'spacing-h-10',
setup: (box) => {
box.pack_start(TrackControls({ player: player }), false, false, 0);
box.pack_end(PlayState({ player: player }), false, false, 0);
box.pack_end(TrackTime({ player: player }), false, false, 0)
// box.pack_end(TrackSource({ vpack: 'center', player: player }), false, false, 0);
}
})
]
})
]
})
export default () => Widget.Revealer({
transition: 'slide_down',
transitionDuration: 170,
child: Box({
connections: [[Mpris, box => {
let foundPlayer = false;
Mpris.players.forEach((player, i) => {
if (isRealPlayer(player)) {
foundPlayer = true;
box._player = player;
box.children = [MusicControlsWidget(player)];
}
});
if (!foundPlayer) {
box._player = null;
box.get_children().forEach(ch => ch.destroy());
return;
}
}, 'notify::players']],
}),
connections: [
[showMusicControls, (revealer) => {
revealer.revealChild = showMusicControls.value;
}],
],
})
@@ -0,0 +1,82 @@
// This file is for popup notifications
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 Notification from '../../lib/notification.js';
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: { hpack: '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: 'osd-notifs 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);
notif.revealChild = false;
notif._destroyWithAnims();
}],
['notify', (box, id) => {
// console.log('new notiffy', id, Notifications.getNotification(id))
if (!id || Notifications.dnd) return;
if (!Notifications.getNotification(id)) return;
box._map.delete(id);
const notif = Notifications.getNotification(id);
const newNotif = Notification({
notifObject: notif,
isPopup: true,
});
box._map.set(id, newNotif);
box.pack_end(box._map.get(id), false, false, 0);
box.show_all();
// box.children = Array.from(box._map.values()).reverse();
}],
],
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'],
],
});
export default () => notifPopupList;
@@ -0,0 +1,10 @@
import PopupWindow from '../../lib/popupwindow.js';
import OnScreenKeyboard from "./onscreenkeyboard.js";
export default () => PopupWindow({
anchor: ['bottom'],
name: 'osk',
showClassName: 'osk-show',
hideClassName: 'osk-hide',
child: OnScreenKeyboard(),
});
@@ -0,0 +1,175 @@
const { GLib, Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
const { Box, EventBox, Button, Revealer } = Widget;
const { execAsync, exec } = Utils;
import { MaterialIcon } from '../../lib/materialicon.js';
import { separatorLine } from '../../lib/separator.js';
import { defaultOskLayout, oskLayouts } from '../../data/keyboardlayouts.js';
import { setupCursorHoverGrab } from '../../lib/cursorhover.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 topDecor = Box({
vertical: true,
children: [
Box({
hpack: 'center',
className: 'osk-dragline',
homogeneous: true,
children: [EventBox({
setup: setupCursorHoverGrab,
})]
})
]
});
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;
}
});
}
}
})
})
}))
})
}
const keyboardWindow = Box({
vexpand: true,
hexpand: true,
vertical: true,
className: 'osk-window spacing-v-5',
children: [
topDecor,
Box({
className: 'osk-body spacing-h-10',
children: [
keyboardControls,
separatorLine,
keyboardItself(keyboardJson),
],
})
],
connections: [[App, (box, name, visible) => { // Update on open
if (name == 'osk' && visible) {
keyboardWindow.setCss(`margin-bottom: -0px;`);
}
}],],
});
const gestureEvBox = EventBox({ child: keyboardWindow })
const gesture = Gtk.GestureDrag.new(gestureEvBox);
gesture.connect('drag-begin', () => {
Hyprland.sendMessage('j/cursorpos').then((out) => {
gesture.startY = JSON.parse(out).y;
}).catch(print);
});
gesture.connect('drag-update', () => {
Hyprland.sendMessage('j/cursorpos').then((out) => {
const currentY = JSON.parse(out).y;
const offset = gesture.startY - currentY;
if (offset > 0) return;
keyboardWindow.setCss(`
margin-bottom: ${offset}px;
`);
}).catch(print);
});
gesture.connect('drag-end', () => {
var offset = gesture.get_offset()[2];
if (offset > 50) {
App.closeWindow('osk');
}
else {
keyboardWindow.setCss(`
transition: margin-bottom 170ms cubic-bezier(0.05, 0.7, 0.1, 1);
margin-bottom: 0px;
`);
}
})
export default () => gestureEvBox;
+31
View File
@@ -0,0 +1,31 @@
const { Gio, GLib } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
const { execAsync, exec } = Utils;
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
function moveClientToWorkspace(address, workspace) {
Utils.execAsync(['bash', '-c', `hyprctl dispatch movetoworkspacesilent ${workspace},address:${address} &`]);
}
export function dumpToWorkspace(from, to) {
console.log('dump', from, to);
if (from == to) return;
Hyprland.clients.forEach(client => {
if (client.workspace.id == from) {
moveClientToWorkspace(client.address, to);
}
});
}
export function swapWorkspace(workspaceA, workspaceB) {
if (workspaceA == workspaceB) return;
const clientsA = [];
const clientsB = [];
Hyprland.clients.forEach(client => {
if (client.workspace.id == workspaceA) clientsA.push(client.address);
if (client.workspace.id == workspaceB) clientsB.push(client.address);
});
clientsA.forEach((address) => moveClientToWorkspace(address, workspaceB));
clientsB.forEach((address) => moveClientToWorkspace(address, workspaceA));
}
+18
View File
@@ -0,0 +1,18 @@
import { Widget } from '../../imports.js';
import { SearchAndWindows } from "./overview.js";
export default () => Widget.Window({
name: 'overview',
exclusivity: 'normal',
focusable: true,
popup: true,
visible: false,
anchor: ['top'],
layer: 'overlay',
child: Widget.Box({
vertical: true,
children: [
SearchAndWindows(),
]
}),
})
@@ -0,0 +1,136 @@
const { Gio, GLib } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
const { execAsync, exec } = Utils;
import Todo from "../../services/todo.js";
export function hasUnterminatedBackslash(inputString) {
// Use a regular expression to match a trailing odd number of backslashes
const regex = /\\+$/;
return regex.test(inputString);
}
export function launchCustomCommand(command) {
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] == '>color') { // Generate colorscheme from color picker
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.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`])
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
.catch(print);
}
else if (args[0] == '>dark') { // Dark mode
execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "" > ~/.cache/ags/user/colormode.txt`])
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
.catch(print);
}
else if (args[0] == '>badapple') { // Light mode
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/applycolor.sh --bad-apple`]).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);
}
}
export function execAndClose(command, terminal) {
App.closeWindow('overview');
if (terminal) {
execAsync([`bash`, `-c`, `foot fish -C "${command}"`, `&`]).catch(print);
}
else
execAsync(command).catch(print);
}
export function startsWithNumber(str) {
var pattern = /^\d/;
return pattern.test(str);
}
export function expandTilde(path) {
if (path.startsWith('~')) {
return GLib.get_home_dir() + path.slice(1);
} else {
return path;
}
}
function getFileIcon(fileInfo) {
let icon = fileInfo.get_icon();
if (icon) {
// Get the icon's name
return icon.get_names()[0];
} else {
// Default icon for files
return 'text-x-generic';
}
}
export function ls({ path = '~', silent = false }) {
let contents = [];
try {
let expandedPath = expandTilde(path);
if (expandedPath.endsWith('/'))
expandedPath = expandedPath.slice(0, -1);
let folder = Gio.File.new_for_path(expandedPath);
let enumerator = folder.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
let fileInfo;
while ((fileInfo = enumerator.next_file(null)) !== null) {
let fileName = fileInfo.get_display_name();
let fileType = fileInfo.get_file_type();
let item = {
parentPath: expandedPath,
name: fileName,
type: fileType === Gio.FileType.DIRECTORY ? 'folder' : 'file',
icon: getFileIcon(fileInfo),
};
// Add file extension for files
if (fileType === Gio.FileType.REGULAR) {
let fileExtension = fileName.split('.').pop();
item.type = `${fileExtension}`;
}
contents.push(item);
contents.sort((a, b) => {
const aIsFolder = a.type.startsWith('folder');
const bIsFolder = b.type.startsWith('folder');
if (aIsFolder && !bIsFolder) {
return -1;
} else if (!aIsFolder && bIsFolder) {
return 1;
} else {
return a.name.localeCompare(b.name); // Sort alphabetically within folders and files
}
});
}
} catch (e) {
if (!silent) console.log(e);
}
return contents;
}
+520
View File
@@ -0,0 +1,520 @@
const { Gdk, Gio, Gtk } = imports.gi;
import { App, Service, Utils, Variable, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } 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, setupCursorHoverGrab } from "../../lib/cursorhover.js";
import { DoubleRevealer } from "../../lib/doublerevealer.js";
import { execAndClose, expandTilde, hasUnterminatedBackslash, startsWithNumber, launchCustomCommand, ls } from './miscfunctions.js';
import {
CalculationResultButton, CustomCommandButton, DirectoryButton,
DesktopEntryButton, ExecuteCommandButton, SearchButton
} from './searchbuttons.js';
import { dumpToWorkspace, swapWorkspace } from "./actions.js";
// 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 OVERVIEW_WS_NUM_SCALE = 0.09;
const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07;
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
const searchPromptTexts = [
'Try "~/.config"',
'Try "Files"',
'Try "6*cos(pi)"',
'Try "sudo pacman -Syu"',
'Try "How to basic"',
'Drag n\' drop to move windows',
'Type to search',
]
const overviewTick = Variable(false);
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);
}
function iconExists(iconName) {
let iconTheme = Gtk.IconTheme.get_default();
return iconTheme.has_icon(iconName);
}
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;
}
if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, '-'); // Turn into kebab-case
return str;
}
const ContextWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.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: `Workspace ${i}`
});
button.connect("activate", () => {
// execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print);
actionFunc(thisWorkspace, i);
overviewTick.value = !overviewTick.value;
});
submenu.append(button);
}
menuItem.set_reserve_indicator(true);
menuItem.set_submenu(submenu);
}
})
const client = ({ address, size: [w, h], workspace: { id, name }, class: c, title, xwayland }) => {
const revealInfoCondition = (Math.min(w, h) * OVERVIEW_SCALE > 70);
if (w <= 0 || h <= 0) return null;
title = truncateTitle(title);
return Widget.Button({
className: 'overview-tasks-window',
hpack: 'center',
vpack: 'center',
onClicked: () => {
execAsync([`bash`, `-c`, `hyprctl dispatch focuswindow address:${address}`, `&`]).catch(print);
App.closeWindow('overview');
},
onMiddleClickRelease: () => execAsync([`bash`, `-c`, `hyprctl dispatch closewindow address:${address}`, `&`]).catch(print),
onSecondaryClick: (button) => {
button.toggleClassName('overview-tasks-window-selected', true);
const menu = Widget.Menu({
className: 'menu',
children: [
Widget.MenuItem({
child: Widget.Label({
xalign: 0,
label: "Close (Middle-click)",
}),
onActivate: () => {
execAsync([`bash`, `-c`, `hyprctl dispatch closewindow address:${address}`, `&`])
.catch(print);
}
}),
ContextWorkspaceArray({
label: "Dump windows to workspace",
actionFunc: dumpToWorkspace,
thisWorkspace: Number(id)
}),
ContextWorkspaceArray({
label: "Swap windows with workspace",
actionFunc: swapWorkspace,
thisWorkspace: Number(id)
}),
],
});
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({
css: `
min-width: ${Math.max(w * OVERVIEW_SCALE - 4, 1)}px;
min-height: ${Math.max(h * OVERVIEW_SCALE - 4, 1)}px;
`,
homogeneous: true,
child: Widget.Box({
vertical: true,
vpack: 'center',
className: 'spacing-v-5',
children: [
Widget.Icon({
icon: substitute(c),
size: Math.min(w, h) * OVERVIEW_SCALE / 2.5,
}),
// TODO: Add xwayland tag instead of just having italics
DoubleRevealer({
transition1: 'slide_right',
transition2: 'slide_down',
revealChild: revealInfoCondition,
child: Widget.Scrollable({
hexpand: true,
vscroll: 'never',
hscroll: 'automatic',
child: Widget.Label({
truncate: 'end',
className: `${xwayland ? 'txt txt-italic' : 'txt'}`,
css: `
font-size: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE / 14.6}px;
margin: 0px ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE / 10}px;
`,
// If the title is too short, include the class
label: (title.length <= 1 ? `${c}: ${title}` : title),
})
})
})
]
})
}),
tooltipText: `${c}: ${title}`,
setup: (button) => {
setupCursorHoverGrab(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);
});
},
});
}
const workspace = index => {
const fixed = Gtk.Fixed.new();
const WorkspaceNumber = (index) => Widget.Label({
className: 'overview-tasks-workspace-number',
label: `${index}`,
css: `
margin: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE * OVERVIEW_WS_NUM_MARGIN_SCALE}px;
font-size: ${SCREEN_HEIGHT * OVERVIEW_SCALE * OVERVIEW_WS_NUM_SCALE}px;
`,
})
const widget = Widget.Box({
className: 'overview-tasks-workspace',
vpack: 'center',
css: `
min-width: ${SCREEN_WIDTH * OVERVIEW_SCALE}px;
min-height: ${SCREEN_HEIGHT * OVERVIEW_SCALE}px;
`,
children: [Widget.EventBox({
hexpand: true,
vexpand: true,
onPrimaryClickRelease: () => {
execAsync([`bash`, `-c`, `hyprctl dispatch workspace ${index}`, `&`]).catch(print);
App.closeWindow('overview');
},
setup: eventbox => {
eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => {
overviewTick.value = !overviewTick.value;
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 => {
const [x, y] = client.at;
if (x > SCREEN_WIDTH)
client.at = [x - SCREEN_WIDTH, y];
return client;
});
fixed.get_children().forEach(ch => ch.destroy());
fixed.put(WorkspaceNumber(index), 0, 0);
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, workspaces, 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: [
// Update on change
[overviewTick, box => { if (!App.getWindow(windowName).visible) return; Utils.timeout(2, () => box._update(box)); }],
[Hyprland, box => { if (!App.getWindow(windowName).visible) return; box._update(box); }, 'client-added'],
[Hyprland, box => { if (!App.getWindow(windowName).visible) return; box._update(box); }, 'client-removed'],
// Update on show
[App, (box, name, visible) => { // Update on open
if (name == 'overview' && visible) {
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: 'overview-search-results',
vertical: true,
vexpand: true,
});
const resultsRevealer = Widget.Revealer({
transitionDuration: 200,
revealChild: false,
transition: 'slide_down',
// duration: 200,
hpack: '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,
hpack: '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,
hpack: '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',
hpack: 'center',
onAccept: (self) => { // This is when you hit Enter
const text = self.text;
if(text.length == 0) return;
const isAction = text.startsWith('>');
const isDir = (entry.text[0] == '/' || entry.text[0] == '~');
if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a workaround
try {
const fullResult = eval(text);
// copy
execAsync(['wl-copy', `${fullResult}`]).catch(print);
App.closeWindow('overview');
return;
} catch (e) {
// console.log(e);
}
}
if (isDir) {
App.closeWindow('overview');
execAsync(['bash', '-c', `xdg-open "${expandTilde(text)}"`, `&`]).catch(print);
return;
}
if (_appSearchResults.length > 0) {
App.closeWindow('overview');
_appSearchResults[0].launch();
return;
}
else if (text[0] == '>') { // Custom commands
App.closeWindow('overview');
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(['bash', '-c', `xdg-open 'https://www.google.com/search?q=${text} -site:quora.com' &`]).catch(print); // fuck quora
}
},
// Actually onChange but this is ta workaround for a bug
connections: [
['notify::text', (entry) => { // This is when you type
const isAction = entry.text[0] == '>';
const isDir = (entry.text[0] == '/' || entry.text[0] == '~');
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);
}
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 small workaround.
try {
const fullResult = eval(text);
resultsBox.add(CalculationResultButton({ result: fullResult, text: text }));
} catch (e) {
// console.log(e);
}
}
if (isDir) {
var contents = [];
contents = ls({ path: text, silent: true });
contents.forEach((item) => {
resultsBox.add(DirectoryButton(item));
})
}
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 && !hasUnterminatedBackslash(text) && 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();
}
}]
],
});
return Widget.Box({
vertical: true,
children: [
clickOutsideToClose,
Widget.Box({
hpack: '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('');
}
}],
['key-press-event', (widget, event) => { // Typing
if (event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 && widget != entry) {
Utils.timeout(1, () => entry.grab_focus());
entry.set_text(entry.text + String.fromCharCode(event.get_keyval()[1]));
entry.set_position(-1);
}
}],
],
});
};
@@ -0,0 +1,172 @@
const { Gdk, Gtk } = imports.gi;
import { App, Service, Utils, Widget } from '../../imports.js';
const { execAsync, exec } = Utils;
import { searchItem } from './searchitem.js';
import { execAndClose, startsWithNumber, launchCustomCommand } from './miscfunctions.js';
export const DirectoryButton = ({ parentPath, name, type, icon }) => {
const actionText = Widget.Revealer({
revealChild: false,
transition: "crossfade",
transitionDuration: 200,
child: Widget.Label({
className: 'overview-search-results-txt txt txt-small txt-action',
label: 'Open',
})
});
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');
execAsync(['bash', '-c', `xdg-open '${parentPath}/${name}'`, `&`]).catch(print);
},
child: Widget.Box({
children: [
Widget.Box({
vertical: false,
children: [
Widget.Box({
className: 'overview-search-results-icon',
homogeneous: true,
child: Widget.Icon({
icon: icon,
setup: (self) => Utils.timeout(1, () => {
const styleContext = self.get_parent().get_style_context();
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
self.size = Math.max(width, height, 1);
})
}),
}),
Widget.Label({
className: 'overview-search-results-txt txt txt-norm',
label: 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;
}],
]
})
}
export const CalculationResultButton = ({ result, text }) => searchItem({
materialIconName: 'calculate',
name: `Math result`,
actionName: "Copy",
content: `${result}`,
onActivate: () => {
App.closeWindow('overview');
execAsync(['wl-copy', `${result}`]).catch(print);
},
});
export 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.Box({
className: 'overview-search-results-icon',
homogeneous: true,
child: Widget.Icon({
icon: app.iconName,
setup: (self) => Utils.timeout(1, () => {
const styleContext = self.get_parent().get_style_context();
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
self.size = Math.max(width, height, 1);
})
}),
}),
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;
}],
]
})
}
export 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),
})
export const CustomCommandButton = ({ text = '' }) => searchItem({
materialIconName: 'settings_suggest',
name: 'Action',
actionName: 'Run',
content: `${text}`,
onActivate: () => {
App.closeWindow('overview');
launchCustomCommand(text);
},
});
export const SearchButton = ({ text = '' }) => searchItem({
materialIconName: 'travel_explore',
name: 'Search Google',
actionName: 'Go',
content: `${text}`,
onActivate: () => {
App.closeWindow('overview');
execAsync(['bash', '-c', `xdg-open 'https://www.google.com/search?q=${text} -site:quora.com' &`]).catch(print); // fuck quora
},
});
@@ -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 "../../lib/cursorhover.js";
import { MaterialIcon } from '../../lib/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({
hpack: 'start',
className: 'overview-search-results-txt txt txt-smallie txt-subtext',
label: `${name}`,
truncate: "end",
}),
Widget.Label({
hpack: '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;
}],
]
});
}
+36
View File
@@ -0,0 +1,36 @@
import { Widget } from '../../imports.js';
import { RoundedCorner } from "../../lib/roundedcorner.js";
export const CornerTopleft = () => Widget.Window({
name: 'cornertl',
layer: 'top',
anchor: ['top', 'left'],
exclusivity: 'normal',
visible: true,
child: RoundedCorner('topleft', { className: 'corner', }),
});
export const CornerTopright = () => Widget.Window({
name: 'cornertr',
layer: 'top',
anchor: ['top', 'right'],
exclusivity: 'normal',
visible: true,
child: RoundedCorner('topright', { className: 'corner', }),
});
export const CornerBottomleft = () => Widget.Window({
name: 'cornerbl',
layer: 'top',
anchor: ['bottom', 'left'],
exclusivity: 'normal',
visible: true,
child: RoundedCorner('bottomleft', { className: 'corner-black', }),
});
export const CornerBottomright = () => Widget.Window({
name: 'cornerbr',
layer: 'top',
anchor: ['bottom', 'right'],
exclusivity: 'normal',
visible: true,
child: RoundedCorner('bottomright', { className: 'corner-black', }),
});
+14
View File
@@ -0,0 +1,14 @@
const { Gdk, Gtk } = imports.gi;
import { Widget } from '../../imports.js';
import SessionScreen from "./sessionscreen.js";
export default () => Widget.Window({ // On-screen keyboard
name: 'session',
popup: true,
visible: false,
focusable: true,
layer: 'overlay',
exclusivity: 'ignore',
// anchor: ['top', 'bottom', 'left', 'right'],
child: SessionScreen(),
})
@@ -0,0 +1,142 @@
// 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;
import { App, Service, Utils, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
const { exec, execAsync } = Utils;
const SessionButton = (name, icon, command, props = {}) => {
const buttonDescription = Widget.Revealer({
vpack: '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',
css: `
min-width: ${SCREEN_WIDTH * 1.5}px;
min-height: ${SCREEN_HEIGHT * 1.5}px;
`, // idk why but height = screen height doesn't fill
vertical: true,
children: [
Widget.EventBox({
onPrimaryClick: () => App.closeWindow('session'),
onSecondaryClick: () => App.closeWindow('session'),
onMiddleClick: () => App.closeWindow('session'),
}),
Widget.Box({
hpack: 'center',
vexpand: true,
vertical: true,
children: [
Widget.Box({
vpack: 'center',
vertical: true,
className: 'spacing-v-15',
children: [
Widget.Box({
vertical: true,
css: '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({
hpack: 'center',
className: 'spacing-h-15',
children: [ // lock, logout, sleep
lockButton,
logoutButton,
sleepButton,
]
}),
Widget.Box({
hpack: 'center',
className: 'spacing-h-15',
children: [ // hibernate, shutdown, reboot
hibernateButton,
shutdownButton,
rebootButton,
]
}),
Widget.Box({
hpack: '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
}],
],
});
}
@@ -0,0 +1,237 @@
const { Gdk, GLib, Gtk, Pango } = imports.gi;
import { App, Utils, Widget } from '../../../imports.js';
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import ChatGPT from '../../../services/chatgpt.js';
import { MaterialIcon } from "../../../lib/materialicon.js";
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js";
import { SystemMessage, ChatMessage } from "./chatgpt_chatmessage.js";
import { ConfigToggle } from '../../../lib/configwidgets.js';
import { markdownTest } from '../../../lib/md2pango.js';
const chatGPTTabIcon = Icon({
hpack: 'center',
className: 'sidebar-chat-welcome-logo',
icon: `${App.configDir}/assets/openai-logomark.svg`,
setup: (self) => Utils.timeout(1, () => {
const styleContext = self.get_style_context();
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
self.size = Math.max(width, height, 1) * 116 / 180; // Why such a specific proportion? See https://openai.com/brand#logos
})
});
export const chatGPTInfo = Box({
vertical: true,
className: 'spacing-v-15',
children: [
Icon({
hpack: 'center',
className: 'sidebar-chat-welcome-logo',
icon: `${App.configDir}/assets/openai-logomark.svg`,
setup: (self) => Utils.timeout(1, () => {
const styleContext = self.get_style_context();
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
self.size = Math.max(width, height, 1) * 116 / 180; // Why such a specific proportion? See https://openai.com/brand#logos
})
}),
Label({
className: 'txt txt-title-small sidebar-chat-welcome-txt',
wrap: true,
justify: Gtk.Justification.CENTER,
label: 'ChatGPT',
}),
Box({
className: 'spacing-h-5',
hpack: 'center',
children: [
Label({
className: 'txt-smallie txt-subtext',
wrap: true,
justify: Gtk.Justification.CENTER,
label: 'Powered by OpenAI',
}),
Button({
className: 'txt-subtext txt-norm icon-material',
label: 'info',
tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.',
setup: setupCursorHoverInfo,
}),
]
}),
]
})
export const chatGPTSettings = Revealer({
transition: 'slide_down',
transitionDuration: 150,
revealChild: true,
connections: [
[ChatGPT, (self) => {
self.revealChild = false;
}, 'newMsg'],
[ChatGPT, (self) => {
self.revealChild = true;
}, 'clear'],
],
child: Box({
vertical: true,
hpack: 'fill',
className: 'sidebar-chat-settings',
children: [
ConfigToggle({
icon: 'cycle',
name: 'Cycle models',
desc: 'Helps avoid exceeding the API rate of 3 messages per minute.\nTurn this on if you message rapidly.',
initValue: ChatGPT.cycleModels,
onChange: (self, newValue) => {
ChatGPT.cycleModels = newValue;
},
}),
ConfigToggle({
icon: 'description',
name: 'Assistant prompt',
desc: 'Tells ChatGPT\n 1. It\'s a sidebar assistant on Linux\n 2. Be short and concise\n 3. Use markdown features extensively\nLeave this off for a vanilla ChatGPT experience.',
initValue: ChatGPT.assistantPrompt,
onChange: (self, newValue) => {
ChatGPT.assistantPrompt = newValue;
},
}),
]
})
});
export const openaiApiKeyInstructions = Box({
homogeneous: true,
children: [Revealer({
transition: 'slide_down',
transitionDuration: 150,
connections: [[ChatGPT, (self, hasKey) => {
self.revealChild = (ChatGPT.key.length == 0);
}, 'hasKey']],
child: Button({
child: Label({
useMarkup: true,
wrap: true,
className: 'txt sidebar-chat-welcome-txt',
justify: Gtk.Justification.CENTER,
label: 'An OpenAI API key is required\nYou can grab one <u>here</u>, then enter it below'
}),
setup: setupCursorHover,
onClicked: () => {
Utils.execAsync(['bash', '-c', `xdg-open https://platform.openai.com/api-keys &`]);
}
})
})]
});
export const chatGPTWelcome = Box({
vexpand: true,
homogeneous: true,
child: Box({
className: 'spacing-v-15',
vpack: 'center',
vertical: true,
children: [
chatGPTInfo,
openaiApiKeyInstructions,
chatGPTSettings,
]
})
});
export const chatContent = Box({
className: 'spacing-v-15',
vertical: true,
connections: [
[ChatGPT, (box, id) => {
const message = ChatGPT.messages[id];
if (!message) return;
box.add(ChatMessage(message))
}, 'newMsg'],
[ChatGPT, (box) => {
box.children = [chatGPTWelcome];
}, 'clear'],
[ChatGPT, (box) => {
box.children = [chatGPTWelcome];
}, 'initialized'],
]
});
export const chatGPTView = Scrollable({
className: 'sidebar-chat-viewport',
vexpand: true,
child: chatContent,
setup: (scrolledWindow) => {
scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
const vScrollbar = scrolledWindow.get_vscrollbar();
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
Utils.timeout(1, () => { // Fix click-to-scroll-widget-to-view behavior
const viewport = scrolledWindow.child;
viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined));
})
}
});
export const chatGPTCommands = Box({
className: 'spacing-h-5',
children: [
Box({ hexpand: true }),
Button({
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
onClicked: () => chatContent.add(SystemMessage(
`Key stored in:\n\`${ChatGPT.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``,
'/key')),
setup: setupCursorHover,
label: '/key',
}),
Button({
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
onClicked: () => chatContent.add(SystemMessage(
`Currently using \`${ChatGPT.modelName}\``,
'/model'
)),
setup: setupCursorHover,
label: '/model',
}),
Button({
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
onClicked: () => ChatGPT.clear(),
setup: setupCursorHover,
label: '/clear',
}),
]
});
export const chatGPTSendMessage = (text) => {
// Check if text or API key is empty
if (text.length == 0) return;
if (ChatGPT.key.length == 0) {
ChatGPT.key = text;
chatContent.add(SystemMessage(`Key saved to\n\`${ChatGPT.keyPath}\``, 'API Key'));
text = '';
return;
}
// Commands
if (text.startsWith('/')) {
if (text.startsWith('/clear')) ChatGPT.clear();
else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${ChatGPT.modelName}\``, '/model'))
else if (text.startsWith('/key')) {
const parts = text.split(' ');
if (parts.length == 1) chatContent.add(SystemMessage(`Key stored in:\n\`${ChatGPT.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``, '/key'));
else {
ChatGPT.key = parts[1];
chatContent.add(SystemMessage(`Updated API Key at\n\`${ChatGPT.keyPath}\``, '/key'));
}
}
else if (text.startsWith('/test'))
chatContent.add(SystemMessage(markdownTest, `Markdown test`));
else
chatContent.add(SystemMessage(`Invalid command.`, 'Error'))
}
else {
ChatGPT.send(text);
}
}
@@ -0,0 +1,291 @@
const { Gdk, Gio, GLib, Gtk, Pango } = imports.gi;
import { App, Utils, Widget } from '../../../imports.js';
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import { MaterialIcon } from "../../../lib/materialicon.js";
import { convert } from "../../../lib/md2pango.js";
import GtkSource from "gi://GtkSource?version=3.0";
const CUSTOM_SOURCEVIEW_SCHEME_PATH = `${App.configDir}/data/sourceviewtheme.xml`;
const CUSTOM_SCHEME_ID = 'custom';
const USERNAME = GLib.get_user_name();
const CHATGPT_CURSOR = ' >> ';
const MESSAGE_SCROLL_DELAY = 13; // In milliseconds, the time before an updated message scrolls to bottom
/////////////////////// Custom source view colorscheme /////////////////////////
function loadCustomColorScheme(filePath) {
// Read the XML file content
const file = Gio.File.new_for_path(filePath);
const [success, contents] = file.load_contents(null);
if (!success) {
logError('Failed to load the XML file.');
return;
}
// Parse the XML content and set the Style Scheme
const schemeManager = GtkSource.StyleSchemeManager.get_default();
schemeManager.append_search_path(file.get_parent().get_path());
}
loadCustomColorScheme(CUSTOM_SOURCEVIEW_SCHEME_PATH);
//////////////////////////////////////////////////////////////////////////////
function copyToClipboard(text) {
const clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
const textVariant = new GLib.Variant('s', text);
clipboard.set_text(textVariant, -1);
clipboard.store();
}
function substituteLang(str) {
const subs = [
{ from: 'javascript', to: 'js' },
];
for (const { from, to } of subs) {
if (from === str)
return to;
}
return str;
}
const HighlightedCode = (content, lang) => {
const buffer = new GtkSource.Buffer();
const sourceView = new GtkSource.View({
buffer: buffer,
wrap_mode: Gtk.WrapMode.WORD
});
const langManager = GtkSource.LanguageManager.get_default();
let displayLang = langManager.get_language(substituteLang(lang)); // Set your preferred language
if (displayLang) {
buffer.set_language(displayLang);
}
const schemeManager = GtkSource.StyleSchemeManager.get_default();
buffer.set_style_scheme(schemeManager.get_scheme(CUSTOM_SCHEME_ID));
buffer.set_text(content, -1);
return sourceView;
}
const TextBlock = (content = '') => Label({
hpack: 'fill',
className: 'txt sidebar-chat-txtblock sidebar-chat-txt',
useMarkup: true,
xalign: 0,
wrap: true,
selectable: true,
label: content,
});
const CodeBlock = (content = '', lang = 'txt') => {
const topBar = Box({
className: 'sidebar-chat-codeblock-topbar',
children: [
Label({
label: lang,
className: 'sidebar-chat-codeblock-topbar-txt',
}),
Box({
hexpand: true,
}),
Button({
className: 'sidebar-chat-codeblock-topbar-btn',
onClicked: (self) => {
// execAsync(['bash', '-c', `wl-copy '${content}'`, `&`]).catch(print);
execAsync([`wl-copy`, `${sourceView.label}`]).catch(print);
},
child: Box({
className: 'spacing-h-5',
children: [
MaterialIcon('content_copy', 'small'),
Label({
label: 'Copy',
})
]
})
})
]
})
// Source view
const sourceView = HighlightedCode(content, lang);
const codeBlock = Box({
properties: [
['updateText', (text) => {
sourceView.get_buffer().set_text(text, -1);
}]
],
className: 'sidebar-chat-codeblock',
vertical: true,
children: [
topBar,
Box({
className: 'sidebar-chat-codeblock-code',
homogeneous: true,
children: [sourceView,],
})
]
})
// const schemeIds = styleManager.get_scheme_ids();
// print("Available Style Schemes:");
// for (let i = 0; i < schemeIds.length; i++) {
// print(schemeIds[i]);
// }
return codeBlock;
}
const Divider = () => Box({
className: 'sidebar-chat-divider',
})
const MessageContent = (content) => {
const contentBox = Box({
vertical: true,
properties: [
['fullUpdate', (self, content, useCursor = false) => {
// Clear and add first text widget
contentBox.get_children().forEach(ch => ch.destroy());
contentBox.add(TextBlock())
// Loop lines. Put normal text in markdown parser
// and put code into code highlighter (TODO)
let lines = content.split('\n');
let lastProcessed = 0;
let inCode = false;
for (const [index, line] of lines.entries()) {
// Code blocks
const codeBlockRegex = /^\s*```([a-zA-Z0-9]+)?\n?/;
if (codeBlockRegex.test(line)) {
// console.log(`code at line ${index}`);
const kids = self.get_children();
const lastLabel = kids[kids.length - 1];
const blockContent = lines.slice(lastProcessed, index).join('\n');
if (!inCode) {
lastLabel.label = convert(blockContent);
contentBox.add(CodeBlock('', codeBlockRegex.exec(line)[1]));
}
else {
lastLabel._updateText(blockContent);
contentBox.add(TextBlock());
}
lastProcessed = index + 1;
inCode = !inCode;
}
// Breaks
const dividerRegex = /^\s*---/;
if (!inCode && dividerRegex.test(line)) {
const kids = self.get_children();
const lastLabel = kids[kids.length - 1];
const blockContent = lines.slice(lastProcessed, index).join('\n');
lastLabel.label = convert(blockContent);
contentBox.add(Divider());
contentBox.add(TextBlock());
lastProcessed = index + 1;
}
}
if (lastProcessed < lines.length) {
const kids = self.get_children();
const lastLabel = kids[kids.length - 1];
let blockContent = lines.slice(lastProcessed, lines.length).join('\n');
if (!inCode)
lastLabel.label = `${convert(blockContent)}${useCursor ? CHATGPT_CURSOR : ''}`;
else
lastLabel._updateText(blockContent);
}
// Debug: plain text
// contentBox.add(Label({
// hpack: 'fill',
// className: 'txt sidebar-chat-txtblock sidebar-chat-txt',
// useMarkup: false,
// xalign: 0,
// wrap: true,
// selectable: true,
// label: '------------------------------\n' + convert(content),
// }))
contentBox.show_all();
}]
]
});
contentBox._fullUpdate(contentBox, content, false);
return contentBox;
}
export const ChatMessage = (message) => {
const messageContentBox = MessageContent(message.content);
const thisMessage = Box({
className: 'sidebar-chat-message',
children: [
Box({
className: `sidebar-chat-indicator ${message.role == 'user' ? 'sidebar-chat-indicator-user' : 'sidebar-chat-indicator-bot'}`,
}),
Box({
vertical: true,
hpack: 'fill',
hexpand: true,
children: [
Label({
hpack: 'fill',
xalign: 0,
className: 'txt txt-bold sidebar-chat-name',
wrap: true,
label: (message.role == 'user' ? USERNAME : 'ChatGPT'),
}),
messageContentBox,
],
connections: [
[message, (self, isThinking) => {
messageContentBox.toggleClassName('thinking', message.thinking);
}, 'notify::thinking'],
[message, (self) => { // Message update
messageContentBox._fullUpdate(messageContentBox, message.content, message.role != 'user');
Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
const scrolledWindow = thisMessage.get_parent().get_parent();
var adjustment = scrolledWindow.get_vadjustment();
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
});
}, 'notify::content'],
[message, (label, isDone) => { // Remove the cursor
messageContentBox._fullUpdate(messageContentBox, message.content, false);
}, 'notify::done'],
]
})
]
});
return thisMessage;
}
export const SystemMessage = (content, commandName) => {
const messageContentBox = MessageContent(content);
const thisMessage = Box({
className: 'sidebar-chat-message',
children: [
Box({
className: `sidebar-chat-indicator sidebar-chat-indicator-System`,
}),
Box({
vertical: true,
hpack: 'fill',
hexpand: true,
children: [
Label({
xalign: 0,
className: 'txt txt-bold sidebar-chat-name',
wrap: true,
label: `System • ${commandName}`,
}),
messageContentBox,
],
})
],
setup: (self) => Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
const scrolledWindow = thisMessage.get_parent().get_parent();
var adjustment = scrolledWindow.get_vadjustment();
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
})
});
return thisMessage;
}
@@ -0,0 +1,88 @@
import { App, Utils, Widget } from '../../imports.js';
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js";
// APIs
import ChatGPT from '../../services/chatgpt.js';
import { chatGPTView, chatGPTCommands, chatGPTSendMessage } from './apis/chatgpt.js';
const APIS = [
{
name: 'ChatGPT',
sendCommand: chatGPTSendMessage,
contentWidget: chatGPTView,
commandBar: chatGPTCommands,
tabIcon: Box({}),
}
];
let currentApiId = 0;
const apiSwitcher = Box({
vertical: true,
children: [
Box({
homogeneous: true,
children: APIS.map(api => api.tabIcon),
}),
]
})
export const chatEntry = Entry({
className: 'sidebar-chat-entry',
hexpand: true,
connections: [
[ChatGPT, (self) => {
if (APIS[currentApiId].name != 'ChatGPT') return;
self.placeholderText = (ChatGPT.key.length > 0 ? 'Ask a question...' : 'Enter OpenAI API Key...');
}, 'hasKey']
],
onChange: (entry) => {
chatSendButton.toggleClassName('sidebar-chat-send-available', entry.text.length > 0);
},
onAccept: (entry) => {
APIS[currentApiId].sendCommand(entry.text)
entry.text = '';
},
});
const chatSendButton = Button({
className: 'txt-norm icon-material sidebar-chat-send',
vpack: 'center',
label: 'arrow_upward',
setup: setupCursorHover,
onClicked: (self) => {
APIS[currentApiId].sendCommand(chatEntry.text);
chatEntry.text = '';
},
});
const textboxArea = Box({ // Entry area
className: 'sidebar-chat-textarea spacing-h-10',
children: [
chatEntry,
chatSendButton,
]
});
const apiContentStack = Stack({
vexpand: true,
transition: 'slide_left_right',
items: APIS.map(api => [api.name, api.contentWidget]),
})
const apiCommandStack = Stack({
transition: 'slide_up_down',
items: APIS.map(api => [api.name, api.commandBar]),
})
export default Widget.Box({
vertical: true,
className: 'spacing-v-10',
homogeneous: false,
children: [
apiSwitcher,
apiContentStack,
apiCommandStack,
textboxArea,
]
});

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