From a83d93e98a2c5087d14a6272badcccddf22e4f1a Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Fri, 16 Feb 2024 17:08:11 +0700
Subject: [PATCH] ags: sync
---
.config/ags/assets/arch-symbolic.svg | 113 ++++
.config/ags/assets/debian-symbolic.svg | 91 +++
.config/ags/assets/fedora-symbolic.svg | 38 ++
.config/ags/assets/flatpak-symbolic.svg | 52 ++
.config/ags/assets/google-gemini-symbolic.svg | 38 +-
.config/ags/assets/nixos-symbolic.svg | 77 +++
.config/ags/assets/openai-symbolic.svg | 39 +-
.config/ags/assets/ubuntu-symbolic.svg | 85 +++
.config/ags/config.js | 8 +-
.config/ags/data/keyboardlayouts.js | 112 ++-
.config/ags/lib/md2pango.js | 247 ++-----
.config/ags/lib/notification.js | 6 -
.../quickscripts/nixos-trim-generations.sh | 243 +++++++
.config/ags/scripts/wayland-idle-inhibitor.py | 81 +++
.config/ags/scss/_bar.scss | 1 +
.config/ags/scss/_common.scss | 46 +-
.config/ags/scss/_dock.scss | 1 +
.config/ags/scss/_lib_classes.scss | 8 +-
.config/ags/scss/_notifications.scss | 1 +
.config/ags/scss/_osk.scss | 6 +
.config/ags/scss/_sidebars.scss | 96 ++-
.config/ags/scss/main.scss | 8 +-
.config/ags/services/chatgpt.js | 2 +-
.config/ags/services/gemini.js | 2 +-
.config/ags/style.css | 111 ++-
.config/ags/widgets/bar/music.js | 2 +-
.config/ags/widgets/bar/system.js | 63 +-
.config/ags/widgets/bar/tray.js | 11 +-
.../ags/widgets/bar/workspaces_hyprland.js | 7 +
.config/ags/widgets/dock/dock.js | 6 -
.config/ags/widgets/indicators/main.js | 4 +-
.../ags/widgets/indicators/musiccontrols.js | 266 +++++---
.../onscreenkeyboard/onscreenkeyboard.js | 78 ++-
.../ags/widgets/overview/overview_hyprland.js | 639 +++++++++++-------
.config/ags/widgets/overview/searchbuttons.js | 12 -
.config/ags/widgets/overview/windowcontent.js | 38 +-
.../widgets/sideleft/apis/ai_chatmessage.js | 3 +-
.config/ags/widgets/sideleft/apis/chatgpt.js | 16 -
.config/ags/widgets/sideleft/apis/gemini.js | 12 -
.config/ags/widgets/sideleft/apis/waifu.js | 8 +
.config/ags/widgets/sideleft/apiwidgets.js | 32 +-
.config/ags/widgets/sideleft/module.js | 30 -
.config/ags/widgets/sideleft/quickscripts.js | 11 -
.config/ags/widgets/sideleft/sideleft.js | 6 +-
.config/ags/widgets/sideleft/toolbox.js | 7 +-
.config/ags/widgets/sideleft/tools/color.js | 199 ++++++
.../ags/widgets/sideleft/tools/colorpicker.js | 284 ++++++++
.config/ags/widgets/sideleft/tools/module.js | 56 ++
.../widgets/sideleft/tools/quickscripts.js | 95 +++
.../ags/widgets/sideright/notificationlist.js | 2 +-
.config/ags/widgets/sideright/quicktoggles.js | 3 +-
.config/ags/widgets/sideright/todolist.js | 2 +-
52 files changed, 2648 insertions(+), 756 deletions(-)
create mode 100644 .config/ags/assets/arch-symbolic.svg
create mode 100644 .config/ags/assets/debian-symbolic.svg
create mode 100644 .config/ags/assets/fedora-symbolic.svg
create mode 100644 .config/ags/assets/flatpak-symbolic.svg
create mode 100644 .config/ags/assets/nixos-symbolic.svg
create mode 100644 .config/ags/assets/ubuntu-symbolic.svg
mode change 100644 => 100755 .config/ags/data/keyboardlayouts.js
create mode 100755 .config/ags/scripts/quickscripts/nixos-trim-generations.sh
create mode 100755 .config/ags/scripts/wayland-idle-inhibitor.py
delete mode 100644 .config/ags/widgets/sideleft/module.js
delete mode 100644 .config/ags/widgets/sideleft/quickscripts.js
create mode 100644 .config/ags/widgets/sideleft/tools/color.js
create mode 100644 .config/ags/widgets/sideleft/tools/colorpicker.js
create mode 100644 .config/ags/widgets/sideleft/tools/module.js
create mode 100644 .config/ags/widgets/sideleft/tools/quickscripts.js
diff --git a/.config/ags/assets/arch-symbolic.svg b/.config/ags/assets/arch-symbolic.svg
new file mode 100644
index 000000000..7de9094e0
--- /dev/null
+++ b/.config/ags/assets/arch-symbolic.svg
@@ -0,0 +1,113 @@
+
+
+
+
diff --git a/.config/ags/assets/debian-symbolic.svg b/.config/ags/assets/debian-symbolic.svg
new file mode 100644
index 000000000..252f85334
--- /dev/null
+++ b/.config/ags/assets/debian-symbolic.svg
@@ -0,0 +1,91 @@
+
+
+
+
diff --git a/.config/ags/assets/fedora-symbolic.svg b/.config/ags/assets/fedora-symbolic.svg
new file mode 100644
index 000000000..1a4e8c873
--- /dev/null
+++ b/.config/ags/assets/fedora-symbolic.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/.config/ags/assets/flatpak-symbolic.svg b/.config/ags/assets/flatpak-symbolic.svg
new file mode 100644
index 000000000..0c2bf6280
--- /dev/null
+++ b/.config/ags/assets/flatpak-symbolic.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/.config/ags/assets/google-gemini-symbolic.svg b/.config/ags/assets/google-gemini-symbolic.svg
index 9b00458f9..81f6729b9 100644
--- a/.config/ags/assets/google-gemini-symbolic.svg
+++ b/.config/ags/assets/google-gemini-symbolic.svg
@@ -3,17 +3,20 @@
diff --git a/.config/ags/assets/nixos-symbolic.svg b/.config/ags/assets/nixos-symbolic.svg
new file mode 100644
index 000000000..b697b0d1a
--- /dev/null
+++ b/.config/ags/assets/nixos-symbolic.svg
@@ -0,0 +1,77 @@
+
+
diff --git a/.config/ags/assets/openai-symbolic.svg b/.config/ags/assets/openai-symbolic.svg
index e04db75a5..8ffc912ae 100644
--- a/.config/ags/assets/openai-symbolic.svg
+++ b/.config/ags/assets/openai-symbolic.svg
@@ -1 +1,38 @@
-
\ No newline at end of file
+
+
diff --git a/.config/ags/assets/ubuntu-symbolic.svg b/.config/ags/assets/ubuntu-symbolic.svg
new file mode 100644
index 000000000..07746c9f6
--- /dev/null
+++ b/.config/ags/assets/ubuntu-symbolic.svg
@@ -0,0 +1,85 @@
+
+
+
+
diff --git a/.config/ags/config.js b/.config/ags/config.js
index 4e9c0bb96..e52568f87 100644
--- a/.config/ags/config.js
+++ b/.config/ags/config.js
@@ -1,8 +1,4 @@
"use strict";
-const gi = imports.gi;
-const availableModules = Object.keys(gi);
-print("Available modules: " + availableModules.join(', '));
-
// Import
import Gdk from 'gi://Gdk';
import App from 'resource:///com/github/Aylur/ags/app.js'
@@ -68,7 +64,7 @@ export default {
};
// Stuff that don't need to be toggled. And they're async so ugh...
-// Bar().catch(print); // Use this to debug the bar
+// Bar().catch(print); // Use this to debug the bar. Single monitor only.
forMonitors(Bar);
forMonitors(BarCornerTopleft);
-forMonitors(BarCornerTopright);
\ No newline at end of file
+forMonitors(BarCornerTopright);
diff --git a/.config/ags/data/keyboardlayouts.js b/.config/ags/data/keyboardlayouts.js
old mode 100644
new mode 100755
index 26cb23d63..cb92b8a3e
--- a/.config/ags/data/keyboardlayouts.js
+++ b/.config/ags/data/keyboardlayouts.js
@@ -71,7 +71,9 @@ export const oskLayouts = {
{ keytype: "normal", label: "\\", labelShift: "|", shape: "expand", keycode: 43 }
],
[
- { keytype: "normal", label: "Caps", shape: "caps", keycode: 58 },
+ //{ keytype: "normal", label: "Caps", shape: "caps", keycode: 58 }, // not needed as double-pressing shift does that
+ { keytype: "spacer", label: "", shape: "empty" },
+ { keytype: "spacer", label: "", shape: "empty" },
{ 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 },
@@ -86,7 +88,7 @@ export const oskLayouts = {
{ keytype: "normal", label: "Enter", shape: "expand", keycode: 28 }
],
[
- { keytype: "modkey", label: "Shift", shape: "shift", keycode: 42 },
+ { keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", 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 },
@@ -97,7 +99,7 @@ export const oskLayouts = {
{ 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: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "expand", keycode: 54 } // optional
],
[
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 29 },
@@ -110,5 +112,107 @@ export const oskLayouts = {
{ keytype: "modkey", label: "Ctrl", shape: "control", keycode: 97 }
]
]
+ },
+ qwertz_full: {
+ name: "QWERTZ - Full",
+ name_short: "DE",
+ comment: "Keyboard layout commonly used in German-speaking countries",
+ 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: "Druck", shape: "fn", keycode: 99 },
+ { keytype: "normal", label: "Entf", shape: "fn", keycode: 111 }
+ ],
+ [
+ { keytype: "normal", label: "^", labelShift: "°", labelAlt: "′", shape: "normal", keycode: 41 },
+ { keytype: "normal", label: "1", labelShift: "!", labelAlt: "¹", shape: "normal", keycode: 2 },
+ { keytype: "normal", label: "2", labelShift: "\"", labelAlt: "²", shape: "normal", keycode: 3 },
+ { keytype: "normal", label: "3", labelShift: "§", labelAlt: "³", shape: "normal", keycode: 4 },
+ { keytype: "normal", label: "4", labelShift: "$", labelAlt: "¼", shape: "normal", keycode: 5 },
+ { keytype: "normal", label: "5", labelShift: "%", labelAlt: "½", shape: "normal", keycode: 6 },
+ { keytype: "normal", label: "6", labelShift: "&", labelAlt: "¬", shape: "normal", keycode: 7 },
+ { keytype: "normal", label: "7", labelShift: "/", labelAlt: "{", shape: "normal", keycode: 8 },
+ { keytype: "normal", label: "8", labelShift: "(", labelAlt: "[", shape: "normal", keycode: 9 },
+ { keytype: "normal", label: "9", labelShift: ")", labelAlt: "]", shape: "normal", keycode: 10 },
+ { keytype: "normal", label: "0", labelShift: "=", labelAlt: "}", shape: "normal", keycode: 11 },
+ { keytype: "normal", label: "ß", labelShift: "?", labelAlt: "\\", shape: "normal", keycode: 12 },
+ { keytype: "normal", label: "´", labelShift: "`", labelAlt: "¸", shape: "normal", keycode: 13 },
+ { keytype: "normal", label: "⟵", shape: "expand", keycode: 14 }
+ ],
+ [
+ { keytype: "normal", label: "Tab ⇆", shape: "tab", keycode: 15 },
+ { keytype: "normal", label: "q", labelShift: "Q", labelAlt: "@", shape: "normal", keycode: 16 },
+ { keytype: "normal", label: "w", labelShift: "W", labelAlt: "ſ", shape: "normal", keycode: 17 },
+ { keytype: "normal", label: "e", labelShift: "E", labelAlt: "€", shape: "normal", keycode: 18 },
+ { keytype: "normal", label: "r", labelShift: "R", labelAlt: "¶", shape: "normal", keycode: 19 },
+ { keytype: "normal", label: "t", labelShift: "T", labelAlt: "ŧ", shape: "normal", keycode: 20 },
+ { keytype: "normal", label: "z", labelShift: "Z", labelAlt: "←", shape: "normal", keycode: 21 },
+ { keytype: "normal", label: "u", labelShift: "U", labelAlt: "↓", shape: "normal", keycode: 22 },
+ { keytype: "normal", label: "i", labelShift: "I", labelAlt: "→", shape: "normal", keycode: 23 },
+ { keytype: "normal", label: "o", labelShift: "O", labelAlt: "ø", shape: "normal", keycode: 24 },
+ { keytype: "normal", label: "p", labelShift: "P", labelAlt: "þ", shape: "normal", keycode: 25 },
+ { keytype: "normal", label: "ü", labelShift: "Ü", labelAlt: "¨", shape: "normal", keycode: 26 },
+ { keytype: "normal", label: "+", labelShift: "*", labelAlt: "~", shape: "normal", keycode: 27 },
+ { keytype: "normal", label: "↵", shape: "expand", keycode: 28 }
+ ],
+ [
+ //{ keytype: "normal", label: "Umschalt ⇩", shape: "caps", keycode: 58 },
+ { keytype: "spacer", label: "", shape: "empty" },
+ { keytype: "spacer", label: "", shape: "empty" },
+ { keytype: "normal", label: "a", labelShift: "A", labelAlt: "æ", shape: "normal", keycode: 30 },
+ { keytype: "normal", label: "s", labelShift: "S", labelAlt: "ſ", shape: "normal", keycode: 31 },
+ { keytype: "normal", label: "d", labelShift: "D", labelAlt: "ð", shape: "normal", keycode: 32 },
+ { keytype: "normal", label: "f", labelShift: "F", labelAlt: "đ", shape: "normal", keycode: 33 },
+ { keytype: "normal", label: "g", labelShift: "G", labelAlt: "ŋ", shape: "normal", keycode: 34 },
+ { keytype: "normal", label: "h", labelShift: "H", labelAlt: "ħ", shape: "normal", keycode: 35 },
+ { keytype: "normal", label: "j", labelShift: "J", labelAlt: "", shape: "normal", keycode: 36 },
+ { keytype: "normal", label: "k", labelShift: "K", labelAlt: "ĸ", shape: "normal", keycode: 37 },
+ { keytype: "normal", label: "l", labelShift: "L", labelAlt: "ł", shape: "normal", keycode: 38 },
+ { keytype: "normal", label: "ö", labelShift: "Ö", labelAlt: "˝", shape: "normal", keycode: 39 },
+ { keytype: "normal", label: "ä", labelShift: 'Ä', labelAlt: "^", shape: "normal", keycode: 40 },
+ { keytype: "normal", label: "#", labelShift: '\'', labelAlt: "’", shape: "normal", keycode: 43 },
+ { keytype: "spacer", label: "", shape: "empty" },
+ //{ keytype: "normal", label: "↵", shape: "expand", keycode: 28 }
+ ],
+ [
+ { keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "shift", keycode: 42 },
+ { keytype: "normal", label: "<", labelShift: ">", labelAlt: "|", shape: "normal", keycode: 86 },
+ { keytype: "normal", label: "y", labelShift: "Y", labelAlt: "»", shape: "normal", keycode: 44 },
+ { keytype: "normal", label: "x", labelShift: "X", labelAlt: "«", shape: "normal", keycode: 45 },
+ { keytype: "normal", label: "c", labelShift: "C", labelAlt: "¢", shape: "normal", keycode: 46 },
+ { keytype: "normal", label: "v", labelShift: "V", labelAlt: "„", shape: "normal", keycode: 47 },
+ { keytype: "normal", label: "b", labelShift: "B", labelAlt: "“", shape: "normal", keycode: 48 },
+ { keytype: "normal", label: "n", labelShift: "N", labelAlt: "”", shape: "normal", keycode: 49 },
+ { keytype: "normal", label: "m", labelShift: "M", labelAlt: "µ", shape: "normal", keycode: 50 },
+ { keytype: "normal", label: ",", labelShift: ";", labelAlt: "·", shape: "normal", keycode: 51 },
+ { keytype: "normal", label: ".", labelShift: ":", labelAlt: "…", shape: "normal", keycode: 52 },
+ { keytype: "normal", label: "-", labelShift: "_", labelAlt: "–", shape: "normal", keycode: 53 },
+ { keytype: "modkey", label: "Shift", labelShift: "Shift ⇧", labelCaps: "Locked ⇩", shape: "expand", keycode: 54 }, // optional
+ ],
+ [
+ { keytype: "modkey", label: "Strg", shape: "control", keycode: 29 },
+ //{ keytype: "normal", label: "", shape: "normal", keycode: 125 }, // dangerous
+ { keytype: "modkey", label: "Alt", shape: "normal", keycode: 56 },
+ { keytype: "normal", label: "Leertaste", shape: "space", keycode: 57 },
+ { keytype: "modkey", label: "Alt Gr", shape: "normal", keycode: 100 },
+ // { label: "Super", shape: "normal", keycode: 126 }, // dangerous
+ //{ keytype: "normal", label: "Menu", shape: "normal", keycode: 139 }, // doesn't work?
+ { keytype: "modkey", label: "Strg", shape: "control", keycode: 97 },
+ { keytype: "normal", label: "⇦", shape: "normal", keycode: 105 },
+ { keytype: "normal", label: "⇨", shape: "normal", keycode: 106 },
+ ]
+ ]
}
-}
+}
\ No newline at end of file
diff --git a/.config/ags/lib/md2pango.js b/.config/ags/lib/md2pango.js
index ec94dd1f0..054752b14 100644
--- a/.config/ags/lib/md2pango.js
+++ b/.config/ags/lib/md2pango.js
@@ -1,208 +1,62 @@
-// SPDX-FileCopyrightText: 2021 Uwe Jugel
-// SPDX-License-Identifier: MIT
-// This file is part of md2pango (https://github.com/ubunatic/md2pango).
+// Converts from Markdown to Pango. This does not support code blocks.
+// For illogical-impulse, code blocks are treated separately, in their own GtkSourceView widgets.
+// Partly inherited from https://github.com/ubunatic/md2pango
-const monospaceFonts = 'JetBrains Mono NF, JetBrains Mono Nerd Font, JetBrains Mono NL, SpaceMono NF, SpaceMono Nerd Font, monospace'
+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 = [
- sub_h1 = { name: H1, re: /^(#\s+)(.*)(\s*)$/, sub: "$2" },
- sub_h2 = { name: H2, re: /^(##\s+)(.*)(\s*)$/, sub: "$2" },
- sub_h3 = { name: H3, re: /^(###\s+)(.*)(\s*)$/, sub: "$2" },
- sub_h4 = { name: H4, re: /^(####\s+)(.*)(\s*)$/, sub: "$2" },
- sub_h5 = { name: H5, re: /^(#####\s+)(.*)(\s*)$/, sub: "$2" },
- { 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: "$2" },
- { name: UND, re: /(__)(\S[\s\S]*?\S)(__)/g, sub: "$2" },
- { name: EMPH, re: /\*(\S.*?\S)\*/g, sub: "$1" },
- // { name: EMPH, re: /_(\S.*?\S)_/g, sub: "$1" },
- { name: HEXCOLOR, re: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/g, sub: ` #$1 ` },
- { name: INLCODE, re: /(`)([^`]*)(`)/g, sub: ` $2 ` },
- // { name: UND, re: /(__|\*\*)(\S[\s\S]*?\S)(__|\*\*)/g, sub: "$2" },
-]
-
-const re_comment = /^\s*\s*$/
-const re_color = /^(\s*\s*)$/
-const re_reset = /()/
-const re_uri = /http[s]?:\/\/[^\s']*/
-const re_href = "/href='(http[s]?:\\/\\/[^\\s]*)'"
-const re_atag = ".*(http[s]?:\\/\\/[^\\s]*).*/"
-const re_h1line = /^===+\s*$/
-const re_h2line = /^---+\s*$/
-
-const m2p_escapes = [
- [//, ''],
- [/&/g, '&'],
- [//g, '>'],
-]
-
-const code_color_span = ""
-
-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, ' '))
+const replacements = {
+ 'indents': [
+ { name: 'BULLET', re: /^(\s*)([\*\-]\s)(.*)(\s*)$/, sub: ' $1- $3' },
+ { name: 'NUMBERING', re: /^(\s*[0-9]+\.\s)(.*)(\s*)$/, sub: ' $1 $2' },
+ ],
+ 'escapes': [
+ { name: 'COMMENT', re: //, sub: '' },
+ { name: 'AMPERSTAND', re: /&/g, sub: '&' },
+ { name: 'LESSTHAN', re: //g, sub: '>' },
+ ],
+ 'sections': [
+ { name: 'H1', re: /^(#\s+)(.*)(\s*)$/, sub: '$2' },
+ { name: 'H2', re: /^(##\s+)(.*)(\s*)$/, sub: '$2' },
+ { name: 'H3', re: /^(###\s+)(.*)(\s*)$/, sub: '$2' },
+ { name: 'H4', re: /^(####\s+)(.*)(\s*)$/, sub: '$2' },
+ { name: 'H5', re: /^(#####\s+)(.*)(\s*)$/, sub: '$2' },
+ ],
+ 'styles': [
+ { name: 'BOLD', re: /(\*\*)(\S[\s\S]*?\S)(\*\*)/g, sub: "$2" },
+ { name: 'UND', re: /(__)(\S[\s\S]*?\S)(__)/g, sub: "$2" },
+ { name: 'EMPH', re: /\*(\S.*?\S)\*/g, sub: "$1" },
+ // { name: 'EMPH', re: /_(\S.*?\S)_/g, sub: "$1" },
+ { name: 'HEXCOLOR', re: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/g, sub: '#$1' },
+ { name: 'INLCODE', re: /(`)([^`]*)(`)/g, sub: '$2' },
+ // { name: 'UND', re: /(__|\*\*)(\S[\s\S]*?\S)(__|\*\*)/g, sub: "$2" },
+ ],
}
+const replaceCategory = (text, replaces) => {
+ for (const type of replaces) {
+ text = text.replace(type.re, type.sub);
+ }
+ return text;
+}
+
+// Main function
+
export default (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('')
- color_span_open = false
- }
- }
-
- const try_open_span = () => {
- if (!color_span_open) {
- output.push('')
- color_span_open = false
- }
- }
-
+ let output = [];
+ // Replace
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(``)
- 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_must_close = false
- } else {
- result = code_color_span + ''
- tt_must_close = true
- }
- } else {
- // the code block ends now
- is_code = false
- output.push(...pad(code_lines).map(escape_line))
- code_lines = []
- result = ''
- if (tt_must_close) {
- result += ''
- 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
- href = href && href[1] == uri
- atag = href && atag[1] == uri
- if (uri && (href || atag)) {
- result = result.replace(uri, `${uri}`)
- }
-
+ let result = line;
+ result = replaceCategory(result, replacements.indents);
+ result = replaceCategory(result, replacements.escapes);
+ result = replaceCategory(result, replacements.sections);
+ result = replaceCategory(result, replacements.styles);
output.push(result)
}
-
- try_close_span()
-
- // remove trailing whitespaces
+ // Remove trailing whitespaces
output = output.map(line => line.replace(/ +$/, ''))
-
- return output.join('\n')
+ return output.join('\n');
}
export const markdownTest = `# Heading 1
@@ -227,5 +81,6 @@ export const markdownTest = `# Heading 1
myArray = [23, 123, 43, 54, '6969'];
console.log('uwu');
\`\`\`
-To update arch lincox, run \`sudo pacman -Syu\`
+- Random instruction thing
+ - To update arch lincox, run \`sudo pacman -Syu\`
`;
\ No newline at end of file
diff --git a/.config/ags/lib/notification.js b/.config/ags/lib/notification.js
index 628b01177..47f2c2061 100644
--- a/.config/ags/lib/notification.js
+++ b/.config/ags/lib/notification.js
@@ -56,12 +56,6 @@ const NotificationIcon = (notifObject) => {
Icon({
vpack: 'center',
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 * 0.7, height * 0.7, 1); // im too lazy to add another box lol
- }, self),
})
:
MaterialIcon(`${notifObject.urgency == 'critical' ? 'release_alert' : guessMessageType(notifObject.summary.toLowerCase())}`, 'hugerass', {
diff --git a/.config/ags/scripts/quickscripts/nixos-trim-generations.sh b/.config/ags/scripts/quickscripts/nixos-trim-generations.sh
new file mode 100755
index 000000000..98b59be95
--- /dev/null
+++ b/.config/ags/scripts/quickscripts/nixos-trim-generations.sh
@@ -0,0 +1,243 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+## Defaults
+keepGensDef=30; keepDaysDef=30
+keepGens=$keepGensDef; keepDays=$keepDaysDef
+
+## Usage
+usage () {
+ printf "Usage:\n\t ./trim-generations.sh \n\n
+(defaults are: Keep-Gens=$keepGensDef Keep-Days=$keepDaysDef Profile=user)\n\n"
+ printf "If you enter any parameters, you must enter all three, or none to use defaults.\n"
+ printf "Example:\n\t trim-generations.sh 15 10 home-manager\n"
+ printf " this will work on the home-manager profile and keep all generations from the\n"
+ printf "last 10 days, and keep at least 15 generations no matter how old.\n"
+ printf "\nProfiles available are:\tuser, home-manager, channels, system (root)\n"
+ printf "\n-h or --help prints this help text."
+}
+
+if [ $# -eq 1 ]; then # if help requested
+ if [ $1 = "-h" ]; then
+ usage
+ exit 1;
+ fi
+ if [ $1 = "--help" ]; then
+ usage
+ exit 2;
+ fi
+ printf "Dont recognise your option exiting..\n\n"
+ usage
+ exit 3;
+
+ elif [ $# -eq 0 ]; then # print the defaults
+ printf "The current defaults are:\n Keep-Gens=$keepGensDef Keep-Days=$keepDaysDef \n\n"
+ read -p "Keep these defaults? (y/n):" answer
+
+ case "$answer" in
+ [yY1] )
+ printf "Using defaults..\n"
+ ;;
+ [nN0] ) printf "ok, doing nothing, exiting..\n"
+ exit 6;
+ ;;
+ * ) printf "%b" "Doing nothing, exiting.."
+ exit 7;
+ ;;
+ esac
+fi
+
+## Handle parameters (and change if root)
+if [[ $EUID -ne 0 ]]; then # if not root
+ profile=$(readlink /home/$USER/.nix-profile)
+else
+ if [ -d /nix/var/nix/profiles/system ]; then # maybe this or the other
+ profile="/nix/var/nix/profiles/system"
+ elif [ -d /nix/var/nix/profiles/default ]; then
+ profile="/nix/var/nix/profiles/default"
+ else
+ echo "Cant find profile for root. Exiting"
+ exit 8
+ fi
+fi
+if (( $# < 1 )); then
+ printf "Keeping default: $keepGensDef generations OR $keepDaysDef days, whichever is more\n"
+elif [[ $# -le 2 ]]; then
+ printf "\nError: Not enough arguments.\n\n" >&2
+ usage
+ exit 1
+elif (( $# > 4)); then
+ printf "\nError: Too many arguments.\n\n" >&2
+ usage
+ exit 2
+else
+ if [ $1 -lt 1 ]; then
+ printf "using Gen numbers less than 1 not recommended. Setting to min=1\n"
+ read -p "is that ok? (y/n): " asnwer
+ #printf "$asnwer"
+ case "$asnwer" in
+ [yY1] )
+ printf "ok, continuing..\n"
+ ;;
+ [nN0] )
+ printf "ok, doing nothing, exiting..\n"
+ exit 6;
+ ;;
+ * )
+ printf "%b" "Doing nothing, exiting.."
+ exit 7;
+ ;;
+ esac
+ fi
+ if [ $2 -lt 0 ]; then
+ printf "using negative days number not recommended. Setting to min=0\n"
+ read -p "is that ok? (y/n): " asnwer
+
+ case "$asnwer" in
+ [yY1] )
+ printf "ok, continuing..\n"
+ ;;
+ [nN0] )
+ printf "ok, doing nothing, exiting..\n"
+ exit 6;
+ ;;
+ * )
+ printf "%b" "Doing nothing, exiting.."
+ exit 7;
+ ;;
+ esac
+ fi
+ keepGens=$1; keepDays=$2;
+ (( keepGens < 1 )) && keepGens=1
+ (( keepDays < 0 )) && keepDays=0
+ if [[ $EUID -ne 0 ]]; then
+ if [[ $3 == "user" ]] || [[ $3 == "default" ]]; then
+ profile=$(readlink /home/$USER/.nix-profile)
+ elif [[ $3 == "home-manager" ]]; then
+ # home-manager defaults to $XDG_STATE_HOME; otherwise, use
+ # `home-manager generations` and `nix-store --query --roots
+ # /nix/store/...` to figure out what reference is keeping the old
+ # generations alive.
+ profile="${XDG_STATE_HOME:-$HOME/.local/state}/nix/profiles/home-manager"
+ elif [[ $3 == "channels" ]]; then
+ profile="/nix/var/nix/profiles/per-user/$USER/channels"
+ else
+ printf "\nError: Do not understand your third argument. Should be one of: (user / home-manager/ channels)\n\n"
+ usage
+ exit 3
+ fi
+ else
+ if [[ $3 == "system" ]]; then
+ profile="/nix/var/nix/profiles/system"
+ elif [[ $3 == "user" ]] || [[ $3 == "default" ]]; then
+ profile="/nix/var/nix/profiles/default"
+ else
+ printf "\nError: Do not understand your third argument. Should be one of: (user / system)\n\n"
+ usage
+ exit 3
+ fi
+ fi
+ printf "OK! \t Keep Gens = $keepGens \t Keep Days = $keepDays\n\n"
+fi
+
+printf "Operating on profile: \t $profile\n\n"
+
+## Runs at the end, to decide whether to delete profiles that match chosen parameters.
+choose () {
+ local default="$1"
+ local prompt="$2"
+ local answer
+
+ read -p "$prompt" answer
+ [ -z "$answer" ] && answer="$default"
+
+ case "$answer" in
+ [yY1] ) #printf "answered yes!\n"
+ nix-env --delete-generations -p $profile ${!gens[@]}
+ exit 0
+ ;;
+ [nN0] ) printf "Ok doing nothing exiting..\n"
+ exit 6;
+ ;;
+ * ) printf "%b" "Unexpected answer '$answer'!" >&2
+ exit 7;
+ ;;
+ esac
+} # end of function choose
+
+# printf "profile = $profile\n\n"
+## Query nix-env for generations list
+IFS=$'\n' nixGens=( $(nix-env --list-generations -p $profile | sed 's:^\s*::; s:\s*$::' | tr '\t' ' ' | tr -s ' ') )
+timeNow=$(date +%s)
+
+## Get info on oldest generation
+IFS=' ' read -r -a oldestGenArr <<< "${nixGens[0]}"
+oldestGen=${oldestGenArr[0]}
+oldestDate=${oldestGenArr[1]}
+printf "%-30s %s\n" "oldest generation:" $oldestGen
+#oldestDate=${nixGens[0]:3:19}
+printf "%-30s %s\n" "oldest generation created:" $oldestDate
+oldestTime=$(date -d "$oldestDate" +%s)
+oldestElapsedSecs=$((timeNow-oldestTime))
+oldestElapsedMins=$((oldestElapsedSecs/60))
+oldestElapsedHours=$((oldestElapsedMins/60))
+oldestElapsedDays=$((oldestElapsedHours/24))
+printf "%-30s %s\n" "minutes before now:" $oldestElapsedMins
+printf "%-30s %s\n" "hours before now:" $oldestElapsedHours
+printf "%-30s %s\n\n" "days before now:" $oldestElapsedDays
+
+## Get info on current generation
+for i in "${nixGens[@]}"; do
+ IFS=' ' read -r -a iGenArr <<< "$i"
+ genNumber=${iGenArr[0]}
+ genDate=${iGenArr[1]}
+ if [[ "$i" =~ current ]]; then
+ currentGen=$genNumber
+ printf "%-30s %s\n" "current generation:" $currentGen
+ currentDate=$genDate
+ printf "%-30s %s\n" "current generation created:" $currentDate
+ currentTime=$(date -d "$currentDate" +%s)
+ currentElapsedSecs=$((timeNow-currentTime))
+ currentElapsedMins=$((currentElapsedSecs/60))
+ currentElapsedHours=$((currentElapsedMins/60))
+ currentElapsedDays=$((currentElapsedHours/24))
+ printf "%-30s %s\n" "minutes before now:" $currentElapsedMins
+ printf "%-30s %s\n" "hours before now:" $currentElapsedHours
+ printf "%-30s %s\n\n" "days before now:" $currentElapsedDays
+ fi
+done
+
+## Compare oldest and current generations
+timeBetweenOldestAndCurrent=$((currentTime-oldestTime))
+elapsedDays=$((timeBetweenOldestAndCurrent/60/60/24))
+generationsDiff=$((currentGen-oldestGen))
+
+## Figure out what we should do, based on generations and options
+if [[ elapsedDays -le keepDays ]]; then
+ printf "All generations are no more than $keepDays days older than current generation. \nOldest gen days difference from current gen: $elapsedDays \n\n\tNothing to do!\n"
+ exit 4;
+elif [[ generationsDiff -lt keepGens ]]; then
+ printf "Oldest generation ($oldestGen) is only $generationsDiff generations behind current ($currentGen). \n\n\t Nothing to do!\n"
+ exit 5;
+else
+ printf "\tSomething to do...\n"
+ declare -a gens
+ for i in "${nixGens[@]}"; do
+ IFS=' ' read -r -a iGenArr <<< "$i"
+ genNumber=${iGenArr[0]}
+ genDiff=$((currentGen-genNumber))
+ genDate=${iGenArr[1]}
+ genTime=$(date -d "$genDate" +%s)
+ elapsedSecs=$((timeNow-genTime))
+ genDaysOld=$((elapsedSecs/60/60/24))
+ if [[ genDaysOld -gt keepDays ]] && [[ genDiff -ge keepGens ]]; then
+ gens["$genNumber"]="$genDate, $genDaysOld day(s) old"
+ fi
+ done
+ printf "\nFound the following generation(s) to delete:\n"
+ for K in "${!gens[@]}"; do
+ printf "generation $K \t ${gens[$K]}\n"
+ done
+ printf "\n"
+ choose "y" "Do you want to delete these? [Y/n]: "
+fi
diff --git a/.config/ags/scripts/wayland-idle-inhibitor.py b/.config/ags/scripts/wayland-idle-inhibitor.py
new file mode 100755
index 000000000..5a6d0e442
--- /dev/null
+++ b/.config/ags/scripts/wayland-idle-inhibitor.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+
+import sys
+from dataclasses import dataclass
+from signal import SIGINT, SIGTERM, signal
+from threading import Event
+
+from pywayland.client.display import Display
+from pywayland.protocol.idle_inhibit_unstable_v1.zwp_idle_inhibit_manager_v1 import (
+ ZwpIdleInhibitManagerV1,
+)
+from pywayland.protocol.wayland.wl_compositor import WlCompositor
+from pywayland.protocol.wayland.wl_registry import WlRegistryProxy
+from pywayland.protocol.wayland.wl_surface import WlSurface
+
+
+@dataclass
+class GlobalRegistry:
+ surface: WlSurface | None = None
+ inhibit_manager: ZwpIdleInhibitManagerV1 | None = None
+
+
+def handle_registry_global(
+ wl_registry: WlRegistryProxy, id_num: int, iface_name: str, version: int
+) -> None:
+ global_registry: GlobalRegistry = wl_registry.user_data or GlobalRegistry()
+
+ if iface_name == "wl_compositor":
+ compositor = wl_registry.bind(id_num, WlCompositor, version)
+ global_registry.surface = compositor.create_surface() # type: ignore
+ elif iface_name == "zwp_idle_inhibit_manager_v1":
+ global_registry.inhibit_manager = wl_registry.bind(
+ id_num, ZwpIdleInhibitManagerV1, version
+ )
+
+
+def main() -> None:
+ done = Event()
+ signal(SIGINT, lambda _, __: done.set())
+ signal(SIGTERM, lambda _, __: done.set())
+
+ global_registry = GlobalRegistry()
+
+ display = Display()
+ display.connect()
+
+ registry = display.get_registry() # type: ignore
+ registry.user_data = global_registry
+ registry.dispatcher["global"] = handle_registry_global
+
+ def shutdown() -> None:
+ display.dispatch()
+ display.roundtrip()
+ display.disconnect()
+
+ display.dispatch()
+ display.roundtrip()
+
+ if global_registry.surface is None or global_registry.inhibit_manager is None:
+ print("Wayland seems not to support idle_inhibit_unstable_v1 protocol.")
+ shutdown()
+ sys.exit(1)
+
+ inhibitor = global_registry.inhibit_manager.create_inhibitor( # type: ignore
+ global_registry.surface
+ )
+
+ display.dispatch()
+ display.roundtrip()
+
+ print("Inhibiting idle...")
+ done.wait()
+ print("Shutting down...")
+
+ inhibitor.destroy()
+
+ shutdown()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.config/ags/scss/_bar.scss b/.config/ags/scss/_bar.scss
index 7ad45ed44..f83566092 100644
--- a/.config/ags/scss/_bar.scss
+++ b/.config/ags/scss/_bar.scss
@@ -285,6 +285,7 @@ $bar_subgroup_bg: $surfaceVariant;
@include element_decel;
min-height: 1.032rem;
min-width: 1.032rem;
+ font-size: 1.032rem;
}
.bar-statusicons {
diff --git a/.config/ags/scss/_common.scss b/.config/ags/scss/_common.scss
index 0af4e65e6..f7530f59b 100644
--- a/.config/ags/scss/_common.scss
+++ b/.config/ags/scss/_common.scss
@@ -40,14 +40,14 @@ menu {
animation-iteration-count: 1;
}
-menubar > menuitem {
+menubar>menuitem {
border-radius: 0.545rem;
-gtk-outline-radius: 0.545rem;
min-width: 13.636rem;
min-height: 2.727rem;
}
-menu > menuitem {
+menu>menuitem {
padding: 0.4em 1.5rem;
background: transparent;
transition: 0.2s ease background;
@@ -55,11 +55,12 @@ menu > menuitem {
-gtk-outline-radius: 0.545rem;
}
-menu > menuitem:hover,
-menu > menuitem:focus {
+menu>menuitem:hover,
+menu>menuitem:focus {
background-color: mix($surfaceVariant, $onSurfaceVariant, 90%);
}
-menu > menuitem:active {
+
+menu>menuitem:active {
background-color: mix($surfaceVariant, $onSurfaceVariant, 80%);
}
@@ -93,6 +94,33 @@ tooltip {
border: 1px solid $onSurfaceVariant;
}
+/////////////////////////////////////////
+// Emoji Chooser structure
+// popover
+// ├── box.emoji-searchbar
+// │ ╰── entry.search
+// ╰── box.emoji-toolbar
+// ├── button.image-button.emoji-section
+// ├── ...
+// ╰── button.image-button.emoji-section
+
+popover {
+ @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: 40ms;
+ animation-timing-function: ease-out;
+ animation-iteration-count: 1;
+}
+
+
+/////////////////////////////////////////
+
.configtoggle-box {
padding: 0.205rem 0.341rem;
border: 0.136rem solid transparent;
@@ -145,17 +173,17 @@ tooltip {
border: 0.068rem solid $outline;
}
-.segment-container > *:first-child {
+.segment-container>*:first-child {
border-top-left-radius: 9999px;
border-bottom-left-radius: 9999px;
}
-.segment-container > * {
+.segment-container>* {
border-right: 0.068rem solid $outline;
padding: 0.341rem 0.682rem;
}
-.segment-container > *:last-child {
+.segment-container>*:last-child {
border-right: 0rem solid transparent;
border-top-right-radius: 9999px;
border-bottom-right-radius: 9999px;
@@ -203,4 +231,4 @@ tooltip {
.gap-h-15 {
min-width: 1.023rem;
-}
+}
\ No newline at end of file
diff --git a/.config/ags/scss/_dock.scss b/.config/ags/scss/_dock.scss
index d9c0001a9..2965726ab 100644
--- a/.config/ags/scss/_dock.scss
+++ b/.config/ags/scss/_dock.scss
@@ -22,6 +22,7 @@
.dock-app-icon {
min-width: 3.409rem;
min-height: 3.409rem;
+ font-size: 3.409rem;
}
.dock-separator {
diff --git a/.config/ags/scss/_lib_classes.scss b/.config/ags/scss/_lib_classes.scss
index 844afc932..6cd3f893b 100644
--- a/.config/ags/scss/_lib_classes.scss
+++ b/.config/ags/scss/_lib_classes.scss
@@ -55,12 +55,12 @@
margin: 10px;
}
-.txt-badonkers {
+.txt-gigantic {
@include mainfont;
font-size: 3rem;
}
-.txt-tiddies {
+.txt-massive {
@include mainfont;
font-size: 2.7273rem;
}
@@ -127,6 +127,10 @@
@include actiontext;
}
+.txt-thin {
+ font-weight: 300;
+}
+
.txt-semibold {
font-weight: 500;
}
diff --git a/.config/ags/scss/_notifications.scss b/.config/ags/scss/_notifications.scss
index a5de8e7c5..2eab85052 100644
--- a/.config/ags/scss/_notifications.scss
+++ b/.config/ags/scss/_notifications.scss
@@ -95,6 +95,7 @@ $notif_surface: $t_background;
@include full-rounding;
min-width: 3.409rem;
min-height: 3.409rem;
+ font-size: 3.409rem;
}
.notif-icon-material {
diff --git a/.config/ags/scss/_osk.scss b/.config/ags/scss/_osk.scss
index ced6bf7ff..eca128c76 100644
--- a/.config/ags/scss/_osk.scss
+++ b/.config/ags/scss/_osk.scss
@@ -110,3 +110,9 @@ $osk_key_fontsize: 1.091rem;
background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 70%);
font-size: $osk_key_fontsize;
}
+
+.osk-key-empty, .osk-key-empty:hover, .osk-key-empty:focus {
+ min-width: $osk_key_width;
+ min-height: $osk_key_height;
+ background-color: transparent;
+}
diff --git a/.config/ags/scss/_sidebars.scss b/.config/ags/scss/_sidebars.scss
index c6b00853b..51a3b5dde 100644
--- a/.config/ags/scss/_sidebars.scss
+++ b/.config/ags/scss/_sidebars.scss
@@ -445,15 +445,96 @@ $onChatgpt: $onPrimary;
.sidebar-module {
@include normal-rounding;
@include group-padding;
- background-color: $t_surface;
+ background-color: $l_l_t_surfaceVariant;
+ padding: 0.682rem;
}
.sidebar-module-btn-arrow {
@include full-rounding;
@include icon-material;
- background-color: $t_surfaceVariant;
+ background-color: $l_l_t_surfaceVariant;
min-width: 1.705rem;
min-height: 1.705rem;
+
+ &:hover {
+ background-color: $hovercolor;
+ }
+}
+
+.sidebar-module-scripts-button {
+ @include full-rounding;
+ @include icon-material;
+ background-color: $l_l_t_surfaceVariant;
+ min-width: 1.705rem;
+ min-height: 1.705rem;
+
+ &:hover {
+ background-color: $hovercolor;
+ }
+
+ &:active {
+ background-color: $activecolor;
+ }
+}
+
+$colorpicker_rounding: 0.341rem;
+
+.sidebar-module-colorpicker-wrapper {
+ padding: 0.341rem;
+}
+
+.sidebar-module-colorpicker-cursorwrapper {
+ padding: 0.341rem 0.136rem;
+}
+
+.sidebar-module-colorpicker-hue {
+ min-height: 13.636rem;
+ min-width: 1.091rem;
+ border-radius: $colorpicker_rounding;
+}
+
+.sidebar-module-colorpicker-hue-cursor {
+ background-color: $onBackground;
+ border: 0.136rem solid $onBackground;
+ min-height: 0.136rem;
+ margin-top: -0.136rem;
+ border-radius: $colorpicker_rounding;
+}
+
+.sidebar-module-colorpicker-saturationandlightness-wrapper {
+ padding: 0.341rem;
+}
+
+.sidebar-module-colorpicker-saturationandlightness {
+ min-height: 13.636rem;
+ min-width: 13.636rem;
+ border-radius: $colorpicker_rounding;
+}
+
+.sidebar-module-colorpicker-saturationandlightness-cursorwrapper {
+ padding: 0.341rem;
+ margin-top: -0.409rem;
+ margin-left: -0.409rem;
+}
+
+.sidebar-module-colorpicker-saturationandlightness-cursor {
+ @include full-rounding;
+ border: 0.136rem solid white;
+ min-width: 0.682rem;
+ min-height: 0.682rem;
+ margin-top: -0.409rem;
+ margin-left: -0.409rem;
+}
+
+.sidebar-module-colorpicker-result-area {
+ padding: 0.341rem;
+}
+
+.sidebar-module-colorpicker-result-box {
+ border-radius: $colorpicker_rounding;
+ min-width: 2.045rem;
+ min-height: 0.682rem;
+ padding: 0.341rem;
}
.sidebar-chat-apiswitcher {
@@ -467,6 +548,7 @@ $onChatgpt: $onPrimary;
@include full-rounding;
min-width: 2.182rem;
min-height: 2.182rem;
+ font-size: 1.406rem;
color: $onSurface;
}
@@ -642,10 +724,10 @@ $onChatgpt: $onPrimary;
.sidebar-chat-welcome-logo {
@include full-rounding;
@include element_decel;
+ @include icon-material;
min-height: 4.773rem;
min-width: 4.773rem;
- @include icon-material;
- font-size: 2.727rem;
+ font-size: 3.076rem;
background-color: $onBackground;
color: $background;
}
@@ -762,9 +844,7 @@ $waifu_image_overlay_transparency: 0.7;
@include full-rounding;
min-width: 1.875rem;
min-height: 1.875rem;
- background-color: rgba(0,
- 0,
- 0,
+ background-color: rgba(0, 0, 0,
$waifu_image_overlay_transparency ); // Fixed cuz on image
color: rgba(255, 255, 255, $waifu_image_overlay_transparency);
}
@@ -776,4 +856,4 @@ $waifu_image_overlay_transparency: 0.7;
.sidebar-waifu-image-action:active {
background-color: rgba(60, 60, 60, $waifu_image_overlay_transparency);
-}
\ No newline at end of file
+}
diff --git a/.config/ags/scss/main.scss b/.config/ags/scss/main.scss
index 83a2e2de3..ea1f378fe 100644
--- a/.config/ags/scss/main.scss
+++ b/.config/ags/scss/main.scss
@@ -1,8 +1,8 @@
// Reset
-* {
- all: unset;
-}
-// *:not(tooltip) { all: unset; }
+// * {
+// all: unset;
+// }
+*:not(popover) { all: unset; }
// Colors
@import './material'; // Material colors
diff --git a/.config/ags/services/chatgpt.js b/.config/ags/services/chatgpt.js
index 793d1a50f..fcae27f3f 100644
--- a/.config/ags/services/chatgpt.js
+++ b/.config/ags/services/chatgpt.js
@@ -118,8 +118,8 @@ class ChatGPTService extends Service {
_assistantPrompt = true;
_messages = [];
_cycleModels = true;
- _temperature = 0.9;
_requestCount = 0;
+ _temperature = 0.9;
_modelIndex = 0;
_key = '';
_decoder = new TextDecoder();
diff --git a/.config/ags/services/gemini.js b/.config/ags/services/gemini.js
index 02b501765..edbc4ad7d 100644
--- a/.config/ags/services/gemini.js
+++ b/.config/ags/services/gemini.js
@@ -135,8 +135,8 @@ class GeminiService extends Service {
_assistantPrompt = true;
_messages = [];
_cycleModels = true;
- _temperature = 0.9;
_requestCount = 0;
+ _temperature = 0.9;
_modelIndex = 0;
_key = '';
_decoder = new TextDecoder();
diff --git a/.config/ags/style.css b/.config/ags/style.css
index e6f208c4e..8e7efd5d2 100644
--- a/.config/ags/style.css
+++ b/.config/ags/style.css
@@ -1,4 +1,4 @@
-* {
+*:not(popover) {
all: unset; }
@keyframes flyin-top {
@@ -56,11 +56,11 @@
text-shadow: 1px 2px 8px rgba(0, 0, 0, 0.69);
margin: 10px; }
-.txt-badonkers {
+.txt-gigantic {
font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter", "Roboto", "Ubuntu", "Noto Sans", sans-serif;
font-size: 3rem; }
-.txt-tiddies {
+.txt-massive {
font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter", "Roboto", "Ubuntu", "Noto Sans", sans-serif;
font-size: 2.7273rem; }
@@ -109,6 +109,9 @@
.txt-action {
color: #cbc0c1; }
+.txt-thin {
+ font-weight: 300; }
+
.txt-semibold {
font-weight: 500; }
@@ -486,6 +489,21 @@ tooltip {
color: #d6c1c4;
border: 1px solid #d6c1c4; }
+popover {
+ border-top: 1px solid rgba(63, 56, 57, 0.121);
+ border-left: 1px solid rgba(63, 56, 57, 0.121);
+ border-right: 1px solid rgba(49, 42, 43, 0.1105);
+ border-bottom: 1px solid rgba(49, 42, 43, 0.1105);
+ padding: 0.681rem;
+ background: #3d3234;
+ color: #d6c1c4;
+ border-radius: 1.159rem;
+ -gtk-outline-radius: 1.159rem;
+ animation-name: appear;
+ animation-duration: 40ms;
+ animation-timing-function: ease-out;
+ animation-iteration-count: 1; }
+
.configtoggle-box {
padding: 0.205rem 0.341rem;
border: 0.136rem solid transparent; }
@@ -809,7 +827,8 @@ tooltip {
-gtk-outline-radius: 9999px;
transition: 300ms cubic-bezier(0, 0.55, 0.45, 1);
min-height: 1.032rem;
- min-width: 1.032rem; }
+ min-width: 1.032rem;
+ font-size: 1.032rem; }
.bar-statusicons {
border-radius: 9999px;
@@ -986,7 +1005,8 @@ tooltip {
.dock-app-icon {
min-width: 3.409rem;
- min-height: 3.409rem; }
+ min-height: 3.409rem;
+ font-size: 3.409rem; }
.dock-separator {
min-width: 0.068rem;
@@ -1332,6 +1352,11 @@ tooltip {
background-color: rgba(107, 93, 95, 0.31);
font-size: 1.091rem; }
+.osk-key-empty, .osk-key-empty:hover, .osk-key-empty:focus {
+ min-width: 2.5rem;
+ min-height: 2.5rem;
+ background-color: transparent; }
+
.sidebar-right {
transition: 300ms cubic-bezier(0.1, 1, 0, 1);
border-top: 1px solid rgba(171, 160, 161, 0.19);
@@ -1709,15 +1734,79 @@ tooltip {
border-radius: 1.159rem;
-gtk-outline-radius: 1.159rem;
padding: 0.341rem;
- background-color: rgba(34, 27, 28, 0.31); }
+ background-color: rgba(61, 50, 52, 0.45);
+ padding: 0.682rem; }
.sidebar-module-btn-arrow {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
font-family: "Material Symbols Rounded", "MaterialSymbolsRounded", "Material Symbols Outlined", "Material Symbols Sharp";
- background-color: rgba(61, 50, 52, 0.31);
+ background-color: rgba(61, 50, 52, 0.45);
min-width: 1.705rem;
min-height: 1.705rem; }
+ .sidebar-module-btn-arrow:hover {
+ background-color: rgba(128, 128, 128, 0.3); }
+
+.sidebar-module-scripts-button {
+ border-radius: 9999px;
+ -gtk-outline-radius: 9999px;
+ font-family: "Material Symbols Rounded", "MaterialSymbolsRounded", "Material Symbols Outlined", "Material Symbols Sharp";
+ background-color: rgba(61, 50, 52, 0.45);
+ min-width: 1.705rem;
+ min-height: 1.705rem; }
+ .sidebar-module-scripts-button:hover {
+ background-color: rgba(128, 128, 128, 0.3); }
+ .sidebar-module-scripts-button:active {
+ background-color: rgba(128, 128, 128, 0.7); }
+
+.sidebar-module-colorpicker-wrapper {
+ padding: 0.341rem; }
+
+.sidebar-module-colorpicker-cursorwrapper {
+ padding: 0.341rem 0.136rem; }
+
+.sidebar-module-colorpicker-hue {
+ min-height: 13.636rem;
+ min-width: 1.091rem;
+ border-radius: 0.341rem; }
+
+.sidebar-module-colorpicker-hue-cursor {
+ background-color: #ecdfe0;
+ border: 0.136rem solid #ecdfe0;
+ min-height: 0.136rem;
+ margin-top: -0.136rem;
+ border-radius: 0.341rem; }
+
+.sidebar-module-colorpicker-saturationandlightness-wrapper {
+ padding: 0.341rem; }
+
+.sidebar-module-colorpicker-saturationandlightness {
+ min-height: 13.636rem;
+ min-width: 13.636rem;
+ border-radius: 0.341rem; }
+
+.sidebar-module-colorpicker-saturationandlightness-cursorwrapper {
+ padding: 0.341rem;
+ margin-top: -0.409rem;
+ margin-left: -0.409rem; }
+
+.sidebar-module-colorpicker-saturationandlightness-cursor {
+ border-radius: 9999px;
+ -gtk-outline-radius: 9999px;
+ border: 0.136rem solid white;
+ min-width: 0.682rem;
+ min-height: 0.682rem;
+ margin-top: -0.409rem;
+ margin-left: -0.409rem; }
+
+.sidebar-module-colorpicker-result-area {
+ padding: 0.341rem; }
+
+.sidebar-module-colorpicker-result-box {
+ border-radius: 0.341rem;
+ min-width: 2.045rem;
+ min-height: 0.682rem;
+ padding: 0.341rem; }
.sidebar-chat-apiswitcher {
border-radius: 9999px;
@@ -1731,6 +1820,7 @@ tooltip {
-gtk-outline-radius: 9999px;
min-width: 2.182rem;
min-height: 2.182rem;
+ font-size: 1.406rem;
color: #ecdfe0; }
.sidebar-chat-apiswitcher-icon-enabled {
@@ -1879,10 +1969,10 @@ tooltip {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
transition: 300ms cubic-bezier(0, 0.55, 0.45, 1);
+ font-family: "Material Symbols Rounded", "MaterialSymbolsRounded", "Material Symbols Outlined", "Material Symbols Sharp";
min-height: 4.773rem;
min-width: 4.773rem;
- font-family: "Material Symbols Rounded", "MaterialSymbolsRounded", "Material Symbols Outlined", "Material Symbols Sharp";
- font-size: 2.727rem;
+ font-size: 3.076rem;
background-color: #ecdfe0;
color: #110d0e; }
@@ -2106,7 +2196,8 @@ tooltip {
border-radius: 9999px;
-gtk-outline-radius: 9999px;
min-width: 3.409rem;
- min-height: 3.409rem; }
+ min-height: 3.409rem;
+ font-size: 3.409rem; }
.notif-icon-material {
background-color: #5c3f45;
diff --git a/.config/ags/widgets/bar/music.js b/.config/ags/widgets/bar/music.js
index ae986086a..03f104400 100644
--- a/.config/ags/widgets/bar/music.js
+++ b/.config/ags/widgets/bar/music.js
@@ -154,7 +154,7 @@ export default () => {
className: 'spacing-h-10 margin-left-10',
children: [
BarResource('Swap Usage', 'swap_horiz', `free | awk '/^Swap/ {if ($2 > 0) printf("%.2f\\n", ($3/$2) * 100); else print "0";}'`),
- BarResource('CPU Usage', 'settings_motion_mode', `top -bn1 | grep Cpu | awk '{print $2}'`),
+ BarResource('CPU Usage', 'settings_motion_mode', `top -bn1 | grep Cpu | sed 's/\\,/\\./g' | awk '{print $2}'`),
]
}),
setup: (self) => self.hook(Mpris, label => {
diff --git a/.config/ags/widgets/bar/system.js b/.config/ags/widgets/bar/system.js
index 1ca2fcc9a..631890736 100644
--- a/.config/ags/widgets/bar/system.js
+++ b/.config/ags/widgets/bar/system.js
@@ -13,6 +13,13 @@ const BATTERY_LOW = 20;
const WEATHER_CACHE_FOLDER = `${GLib.get_user_cache_dir()}/ags/weather`;
Utils.exec(`mkdir -p ${WEATHER_CACHE_FOLDER}`);
+let WEATHER_CITY = '';
+try {
+ WEATHER_CITY = GLib.getenv('AGS_WEATHER_CITY');
+} catch (e) {
+ print(e);
+}
+
const BatBatteryProgress = () => {
const _updateProgress = (circprog) => { // Set circular progress value
circprog.css = `font-size: ${Math.abs(Battery.percent)}px;`
@@ -155,15 +162,24 @@ const BatteryModule = () => Stack({
],
setup: (self) => self.poll(900000, async (self) => {
const WEATHER_CACHE_PATH = WEATHER_CACHE_FOLDER + '/wttr.in.txt';
- Utils.execAsync('curl ipinfo.io')
+ const updateWeatherForCity = (city) => execAsync(`curl https://wttr.in/${city}?format=j1`)
.then(output => {
- return JSON.parse(output)['city'].toLowerCase();
- })
- .then((city) => execAsync(`curl https://wttr.in/${city}?format=j1`)
- .then(output => {
- const weather = JSON.parse(output);
- Utils.writeFile(JSON.stringify(weather), WEATHER_CACHE_PATH)
- .catch(print);
+ const weather = JSON.parse(output);
+ Utils.writeFile(JSON.stringify(weather), WEATHER_CACHE_PATH)
+ .catch(print);
+ const weatherCode = weather.current_condition[0].weatherCode;
+ const weatherDesc = weather.current_condition[0].weatherDesc[0].value;
+ const temperature = weather.current_condition[0].temp_C;
+ const feelsLike = weather.current_condition[0].FeelsLikeC;
+ const weatherSymbol = WEATHER_SYMBOL[WWO_CODE[weatherCode]];
+ self.children[0].label = weatherSymbol;
+ self.children[1].label = `${temperature}℃ • Feels like ${feelsLike}℃`;
+ self.tooltipText = weatherDesc;
+ }).catch((err) => {
+ try { // Read from cache
+ const weather = JSON.parse(
+ Utils.readFile(WEATHER_CACHE_PATH)
+ );
const weatherCode = weather.current_condition[0].weatherCode;
const weatherDesc = weather.current_condition[0].weatherDesc[0].value;
const temperature = weather.current_condition[0].temp_C;
@@ -172,23 +188,20 @@ const BatteryModule = () => Stack({
self.children[0].label = weatherSymbol;
self.children[1].label = `${temperature}℃ • Feels like ${feelsLike}℃`;
self.tooltipText = weatherDesc;
- }).catch((err) => {
- try { // Read from cache
- const weather = JSON.parse(
- Utils.readFile(WEATHER_CACHE_PATH)
- );
- const weatherCode = weather.current_condition[0].weatherCode;
- const weatherDesc = weather.current_condition[0].weatherDesc[0].value;
- const temperature = weather.current_condition[0].temp_C;
- const feelsLike = weather.current_condition[0].FeelsLikeC;
- const weatherSymbol = WEATHER_SYMBOL[WWO_CODE[weatherCode]];
- self.children[0].label = weatherSymbol;
- self.children[1].label = `${temperature}℃ • Feels like ${feelsLike}℃`;
- self.tooltipText = weatherDesc;
- } catch (err) {
- print(err);
- }
- }));
+ } catch (err) {
+ print(err);
+ }
+ });
+ if (WEATHER_CITY != '' && WEATHER_CITY != null) {
+ updateWeatherForCity(WEATHER_CITY);
+ }
+ else {
+ Utils.execAsync('curl ipinfo.io')
+ .then(output => {
+ return JSON.parse(output)['city'].toLowerCase();
+ })
+ .then(updateWeatherForCity);
+ }
}),
})
}),
diff --git a/.config/ags/widgets/bar/tray.js b/.config/ags/widgets/bar/tray.js
index 67d65a833..69c48e23a 100644
--- a/.config/ags/widgets/bar/tray.js
+++ b/.config/ags/widgets/bar/tray.js
@@ -10,15 +10,8 @@ const SysTrayItem = (item) => Button({
className: 'bar-systray-item',
child: Icon({
hpack: 'center',
- setup: (self) => {
- self.hook(item, (self) => self.icon = item.icon);
- 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); // im too lazy to add another box lol
- })
- },
+ icon: item.icon,
+ setup: (self) => self.hook(item, (self) => self.icon = item.icon),
}),
setup: (self) => self
.hook(item, (self) => self.tooltipMarkup = item['tooltip-markup'])
diff --git a/.config/ags/widgets/bar/workspaces_hyprland.js b/.config/ags/widgets/bar/workspaces_hyprland.js
index ae841ebe1..41887c5f8 100644
--- a/.config/ags/widgets/bar/workspaces_hyprland.js
+++ b/.config/ags/widgets/bar/workspaces_hyprland.js
@@ -21,6 +21,7 @@ const WorkspaceContents = (count = 10) => {
attribute: {
initialized: false,
workspaceMask: 0,
+ workspaceGroup: 0,
updateMask: (self) => {
const offset = Math.floor((Hyprland.active.workspace.id - 1) / count) * NUM_OF_WORKSPACES_SHOWN;
// if (self.attribute.initialized) return; // We only need this to run once
@@ -46,6 +47,12 @@ const WorkspaceContents = (count = 10) => {
setup: (area) => area
.hook(Hyprland.active.workspace, (self) => {
self.setCss(`font-size: ${(Hyprland.active.workspace.id - 1) % count + 1}px;`);
+ const previousGroup = self.attribute.workspaceGroup;
+ const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / count);
+ if (currentGroup !== previousGroup) {
+ self.attribute.updateMask(self);
+ self.attribute.workspaceGroup = currentGroup;
+ }
})
.hook(Hyprland, (self) => self.attribute.updateMask(self), 'notify::workspaces')
.on('draw', Lang.bind(area, (area, cr) => {
diff --git a/.config/ags/widgets/dock/dock.js b/.config/ags/widgets/dock/dock.js
index f0192d8cc..9a63d3586 100644
--- a/.config/ags/widgets/dock/dock.js
+++ b/.config/ags/widgets/dock/dock.js
@@ -59,12 +59,6 @@ const AppButton = ({ icon, ...rest }) => Widget.Revealer({
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({
diff --git a/.config/ags/widgets/indicators/main.js b/.config/ags/widgets/indicators/main.js
index 698594768..db3fc4c8c 100644
--- a/.config/ags/widgets/indicators/main.js
+++ b/.config/ags/widgets/indicators/main.js
@@ -1,7 +1,7 @@
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import Indicator from '../../services/indicator.js';
import IndicatorValues from './indicatorvalues.js';
-// import MusicControls from './musiccontrols.js';
+import MusicControls from './musiccontrols.js';
import ColorScheme from './colorscheme.js';
import NotificationPopups from './notificationpopups.js';
@@ -23,7 +23,7 @@ export default (monitor = 0) => Widget.Window({
css: 'min-height: 2px;',
children: [
IndicatorValues(),
- // MusicControls(),
+ MusicControls(),
NotificationPopups(),
ColorScheme(),
]
diff --git a/.config/ags/widgets/indicators/musiccontrols.js b/.config/ags/widgets/indicators/musiccontrols.js
index f817eca4a..922d5f5ba 100644
--- a/.config/ags/widgets/indicators/musiccontrols.js
+++ b/.config/ags/widgets/indicators/musiccontrols.js
@@ -1,4 +1,4 @@
-const { Gio, GLib } = imports.gi;
+const { Gdk, GdkPixbuf, Gio, GLib, Gtk } = imports.gi;
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
@@ -27,8 +27,9 @@ var lastCoverPath = '';
function isRealPlayer(player) {
return (
- !player.busName.startsWith('org.mpris.MediaPlayer2.firefox') &&
- !player.busName.startsWith('org.mpris.MediaPlayer2.playerctld')
+ !player.busName.startsWith('org.mpris.MediaPlayer2.firefox') && // Firefox mpris dbus is useless
+ !player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') && // Doesn't have cover art
+ !player.busName.endsWith('.mpd') // Non-instance mpd bus
);
}
@@ -69,7 +70,7 @@ function getTrackfont(player) {
return DEFAULT_MUSIC_FONT;
}
function trimTrackTitle(title) {
- if(!title) return '';
+ if (!title) return '';
const cleanRegexes = [
/【[^】]*】/, // Touhou n weeb stuff
/\[FREE DOWNLOAD\]/, // F-777
@@ -80,7 +81,7 @@ function trimTrackTitle(title) {
const TrackProgress = ({ player, ...rest }) => {
const _updateProgress = (circprog) => {
- const player = Mpris.getPlayer();
+ // 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;`
@@ -122,69 +123,123 @@ const TrackArtists = ({ player, ...rest }) => Label({
}, '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({
- attribute: {
- 'updateCover': (self) => {
- const player = Mpris.getPlayer();
+const CoverArt = ({ player, ...rest }) => {
+ const fallbackCoverArt = Box({ // Fallback
+ className: 'osd-music-cover-fallback',
+ homogeneous: true,
+ children: [Label({
+ className: 'icon-material txt-gigantic txt-thin',
+ label: 'music_note',
+ })]
+ });
+ const coverArtDrawingArea = Widget.DrawingArea({ className: 'osd-music-cover-art' });
+ const coverArtDrawingAreaStyleContext = coverArtDrawingArea.get_style_context();
+ const realCoverArt = Box({
+ className: 'osd-music-cover-art',
+ homogeneous: true,
+ children: [coverArtDrawingArea],
+ attribute: {
+ 'pixbuf': null,
+ 'showImage': (self, imagePath) => {
+ const borderRadius = coverArtDrawingAreaStyleContext.get_property('border-radius', Gtk.StateFlags.NORMAL);
+ const frameHeight = coverArtDrawingAreaStyleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
+ const frameWidth = coverArtDrawingAreaStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
+ let imageHeight = frameHeight;
+ let imageWidth = frameWidth;
+ // Get image dimensions
+ execAsync(['identify', '-format', '{"w":%w,"h":%h}', imagePath])
+ .then((output) => {
+ const imageDimensions = JSON.parse(output);
+ const imageAspectRatio = imageDimensions.w / imageDimensions.h;
+ const displayedAspectRatio = imageWidth / imageHeight;
+ if (imageAspectRatio >= displayedAspectRatio) {
+ imageWidth = imageHeight * imageAspectRatio;
+ } else {
+ imageHeight = imageWidth / imageAspectRatio;
+ }
+ // Real stuff
+ // TODO: fix memory leak(?)
+ // if (self.attribute.pixbuf) {
+ // self.attribute.pixbuf.unref();
+ // self.attribute.pixbuf = null;
+ // }
+ self.attribute.pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(imagePath, imageWidth, imageHeight);
- // 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;
- }
+ coverArtDrawingArea.set_size_request(frameWidth, frameHeight);
+ coverArtDrawingArea.connect("draw", (widget, cr) => {
+ // Clip a rounded rectangle area
+ cr.arc(borderRadius, borderRadius, borderRadius, Math.PI, 1.5 * Math.PI);
+ cr.arc(frameWidth - borderRadius, borderRadius, borderRadius, 1.5 * Math.PI, 2 * Math.PI);
+ cr.arc(frameWidth - borderRadius, frameHeight - borderRadius, borderRadius, 0, 0.5 * Math.PI);
+ cr.arc(borderRadius, frameHeight - borderRadius, borderRadius, 0.5 * Math.PI, Math.PI);
+ cr.closePath();
+ cr.clip();
+ // Paint image as bg, centered
+ Gdk.cairo_set_source_pixbuf(cr, self.attribute.pixbuf,
+ frameWidth / 2 - imageWidth / 2,
+ frameHeight / 2 - imageHeight / 2
+ );
+ cr.paint();
+ });
+ }).catch(print)
+ },
+ 'updateCover': (self) => {
+ // const player = Mpris.getPlayer(); // Maybe no need to re-get player.. can't remember why I had this
+ // 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;
+ 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
+ // Utils.timeout(200, () => { self.css = `background-image: url('${coverPath}');`; });
+ Utils.timeout(200, () => self.attribute.showImage(self, 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;
- }
+ // If a colorscheme has already been generated, skip generation
+ if (fileExists(stylePath)) {
+ // Utils.timeout(200, () => { self.css = `background-image: url('${coverPath}');`; });
+ self.attribute.showImage(self, 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(`cp ${GLib.get_user_cache_dir()}/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',
- $: [
- [player, (self) => self.attribute.updateCover(self), 'notify::cover-path']
- ],
- })
- ]
- })
- ],
-})
+ // 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(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${App.configDir}/scss/_musicwal.scss`);
+ exec(`sassc ${App.configDir}/scss/_music.scss ${stylePath}`);
+ // self.css = `background-image: url('${coverPath}');`;
+ Utils.timeout(200, () => self.attribute.showImage(self, coverPath));
+ App.applyCss(`${stylePath}`);
+ })
+ .catch(print);
+ },
+ },
+ setup: (self) => self
+ .hook(player, (self) => {
+ self.attribute.updateCover(self);
+ }, 'notify::cover-path')
+ ,
+ });
+ return Box({
+ ...rest,
+ className: 'osd-music-cover',
+ children: [
+ Widget.Overlay({
+ child: fallbackCoverArt,
+ overlays: [realCoverArt],
+ })
+ ],
+ })
+}
const TrackControls = ({ player, ...rest }) => Widget.Revealer({
revealChild: false,
@@ -197,7 +252,7 @@ const TrackControls = ({ player, ...rest }) => Widget.Revealer({
children: [
Button({
className: 'osd-music-controlbtn',
- onClicked: () => execAsync('playerctl previous').catch(print),
+ onClicked: () => player.previous(),
child: Label({
className: 'icon-material osd-music-controlbtn-txt',
label: 'skip_previous',
@@ -205,9 +260,7 @@ const TrackControls = ({ player, ...rest }) => Widget.Revealer({
}),
Button({
className: 'osd-music-controlbtn',
- onClicked: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"`'])
- .catch(print)
- ,
+ onClicked: () => player.next(),
child: Label({
className: 'icon-material osd-music-controlbtn-txt',
label: 'skip_next',
@@ -215,8 +268,8 @@ const TrackControls = ({ player, ...rest }) => Widget.Revealer({
}),
],
}),
- setup: (self) => szelf.hook(Mpris, (self) => {
- const player = Mpris.getPlayer();
+ setup: (self) => self.hook(Mpris, (self) => {
+ // const player = Mpris.getPlayer();
if (!player)
self.revealChild = false;
else
@@ -264,7 +317,7 @@ const TrackTime = ({ player, ...rest }) => {
children: [
Label({
setup: (self) => self.poll(1000, (self) => {
- const player = Mpris.getPlayer();
+ // const player = Mpris.getPlayer();
if (!player) return;
self.label = lengthStr(player.position);
}),
@@ -272,7 +325,7 @@ const TrackTime = ({ player, ...rest }) => {
Label({ label: '/' }),
Label({
setup: (self) => self.hook(Mpris, (self) => {
- const player = Mpris.getPlayer();
+ // const player = Mpris.getPlayer();
if (!player) return;
self.label = lengthStr(player.length);
}),
@@ -296,7 +349,7 @@ const PlayState = ({ player }) => {
overlays: [
Widget.Button({
className: 'osd-music-playstate-btn',
- onClicked: () => execAsync('playerctl play-pause').catch(print),
+ onClicked: () => player.playPause(),
child: Widget.Label({
justification: 'center',
hpack: 'fill',
@@ -313,7 +366,7 @@ const PlayState = ({ player }) => {
}
const MusicControlsWidget = (player) => Box({
- className: 'osd-music spacing-h-20',
+ className: 'osd-music spacing-h-20 test',
children: [
CoverArt({ player: player, vpack: 'center' }),
Box({
@@ -344,35 +397,62 @@ const MusicControlsWidget = (player) => Box({
]
})
-export default () => MarginRevealer({
+export default () => Revealer({
transition: 'slide_down',
+ transitionDuration: 150,
revealChild: false,
- showClass: 'osd-show',
- hideClass: 'osd-hide',
child: Box({
setup: (self) => self.hook(Mpris, box => {
- let foundPlayer = false;
-
+ box.children.forEach(child => {
+ child.destroy();
+ child = null;
+ });
Mpris.players.forEach((player, i) => {
if (isRealPlayer(player)) {
- foundPlayer = true;
- box.children = [MusicControlsWidget(player)];
+ const newInstance = MusicControlsWidget(player);
+ box.add(newInstance);
}
});
-
- if (!foundPlayer) {
- const children = box.get_children();
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
- child.destroy();
- child = null;
- }
- return;
- }
}, 'notify::players'),
}),
setup: (self) => self.hook(showMusicControls, (revealer) => {
- if (showMusicControls.value) revealer.attribute.show();
- else revealer.attribute.hide();
+ revealer.revealChild = showMusicControls.value;
}),
})
+
+// export default () => MarginRevealer({
+// transition: 'slide_down',
+// revealChild: false,
+// showClass: 'osd-show',
+// hideClass: 'osd-hide',
+// child: Box({
+// setup: (self) => self.hook(Mpris, box => {
+// let foundPlayer = false;
+// Mpris.players.forEach((player, i) => {
+// if (isRealPlayer(player)) {
+// foundPlayer = true;
+// box.children.forEach(child => {
+// child.destroy();
+// child = null;
+// });
+// const newInstance = MusicControlsWidget(player);
+// box.children = [newInstance];
+// }
+// });
+
+// if (!foundPlayer) {
+// const children = box.get_children();
+// for (let i = 0; i < children.length; i++) {
+// const child = children[i];
+// child.destroy();
+// child = null;
+// }
+// return;
+// }
+// }, 'notify::players'),
+// }),
+// setup: (self) => self.hook(showMusicControls, (revealer) => {
+// if (showMusicControls.value) revealer.attribute.show();
+// else revealer.attribute.hide();
+// }),
+// })
diff --git a/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js b/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js
index e77b91788..d641436d1 100644
--- a/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js
+++ b/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js
@@ -1,11 +1,10 @@
const { Gtk } = imports.gi;
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
-import Service from 'resource:///com/github/Aylur/ags/service.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { Box, EventBox, Button, Revealer } = Widget;
-const { execAsync, exec } = Utils;
+const { execAsync } = Utils;
import { MaterialIcon } from '../../lib/materialicon.js';
import { separatorLine } from '../../lib/separator.js';
import { defaultOskLayout, oskLayouts } from '../../data/keyboardlayouts.js';
@@ -21,6 +20,18 @@ function releaseAllKeys() {
.then(console.log('[OSK] Released all keys'))
.catch(print);
}
+class ShiftMode {
+ static Off = new ShiftMode('Off');
+ static Normal = new ShiftMode('Normal');
+ static Locked = new ShiftMode('Locked');
+
+ constructor(name) {
+ this.name = name;
+ }
+ toString() {
+ return `ShiftMode.${this.name}`;
+ }
+}
var modsPressed = false;
const topDecor = Box({
@@ -76,6 +87,10 @@ const keyboardControls = Box({
]
})
+var shiftMode = ShiftMode.Off;
+var shiftButton;
+var rightShiftButton;
+var allButtons = [];
const keyboardItself = (kbJson) => {
return Box({
vertical: true,
@@ -88,14 +103,32 @@ const keyboardItself = (kbJson) => {
className: `osk-key osk-key-${key.shape}`,
hexpand: ["space", "expand"].includes(key.shape),
label: key.label,
+ attribute:
+ {key: key},
setup: (button) => {
let pressed = false;
+ allButtons = allButtons.concat(button);
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`);
+
+ if (shiftMode == ShiftMode.Normal) {
+ shiftMode = ShiftMode.Off;
+ if (typeof shiftButton !== 'undefined') {
+ execAsync(`ydotool key 42:0`);
+ shiftButton.toggleClassName('osk-key-active', false);
+ }
+ if (typeof rightShiftButton !== 'undefined') {
+ execAsync(`ydotool key 54:0`);
+ rightShiftButton.toggleClassName('osk-key-active', false);
+ }
+ allButtons.forEach(button => {
+ if (typeof button.attribute.key.labelShift !== 'undefined') button.label = button.attribute.key.label;
+ })
+ }
});
}
else if (key.keytype == "modkey") {
@@ -104,14 +137,53 @@ const keyboardItself = (kbJson) => {
execAsync(`ydotool key ${key.keycode}:0`);
button.toggleClassName('osk-key-active', false);
pressed = false;
+ if (key.keycode == 100) { // Alt Gr button
+ allButtons.forEach(button => { if (typeof button.attribute.key.labelAlt !== 'undefined') button.label = button.attribute.key.label; });
+ }
}
else {
execAsync(`ydotool key ${key.keycode}:1`);
button.toggleClassName('osk-key-active', true);
- pressed = true;
+ if (!(key.keycode == 42 || key.keycode == 54)) pressed = true;
+ else switch (shiftMode.name) { // This toggles the shift button state
+ case "Off": {
+ shiftMode = ShiftMode.Normal;
+ allButtons.forEach(button => { if (typeof button.attribute.key.labelShift !== 'undefined') button.label = button.attribute.key.labelShift; })
+ if (typeof shiftButton !== 'undefined') {
+ shiftButton.toggleClassName('osk-key-active', true);
+ }
+ if (typeof rightShiftButton !== 'undefined') {
+ rightShiftButton.toggleClassName('osk-key-active', true);
+ }
+ } break;
+ case "Normal": {
+ shiftMode = ShiftMode.Locked;
+ if (typeof shiftButton !== 'undefined') shiftButton.label = key.labelCaps;
+ if (typeof rightShiftButton !== 'undefined') rightShiftButton.label = key.labelCaps;
+ } break;
+ case "Locked": {
+ shiftMode = ShiftMode.Off;
+ if (typeof shiftButton !== 'undefined') {
+ shiftButton.label = key.label;
+ shiftButton.toggleClassName('osk-key-active', false);
+ }
+ if (typeof rightShiftButton !== 'undefined') {
+ rightShiftButton.label = key.label;
+ rightShiftButton.toggleClassName('osk-key-active', false);
+ }
+ execAsync(`ydotool key ${key.keycode}:0`);
+
+ allButtons.forEach(button => { if (typeof button.attribute.key.labelShift !== 'undefined') button.label = button.attribute.key.label; }
+ )};
+ }
+ if (key.keycode == 100) { // Alt Gr button
+ allButtons.forEach(button => { if (typeof button.attribute.key.labelAlt !== 'undefined') button.label = button.attribute.key.labelAlt; });
+ }
modsPressed = true;
}
});
+ if (key.keycode == 42) shiftButton = button;
+ else if (key.keycode == 54) rightShiftButton = button;
}
}
})
diff --git a/.config/ags/widgets/overview/overview_hyprland.js b/.config/ags/widgets/overview/overview_hyprland.js
index 053caba6c..0ce537be5 100644
--- a/.config/ags/widgets/overview/overview_hyprland.js
+++ b/.config/ags/widgets/overview/overview_hyprland.js
@@ -3,6 +3,7 @@
// - Active ws hook optimization: only update when moving to next group
//
const { Gdk, Gtk } = imports.gi;
+const { Gravity } = imports.gi.Gdk;
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
import App from 'resource:///com/github/Aylur/ags/app.js';
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
@@ -48,260 +49,398 @@ function substitute(str) {
if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, '-'); // Turn into kebab-case
return str;
}
+export default () => {
+ const clientMap = new Map();
+ let workspaceGroup = 0;
+ const ContextMenuWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({
+ label: `${label}`,
+ setup: (menuItem) => {
+ let submenu = new Gtk.Menu();
+ submenu.className = 'menu';
-const ContextMenuWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({
- label: `${label}`,
- setup: (menuItem) => {
- let submenu = new Gtk.Menu();
- submenu.className = 'menu';
-
- const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
- const startWorkspace = offset + 1;
- const endWorkspace = startWorkspace + NUM_OF_WORKSPACES_SHOWN - 1;
- for (let i = startWorkspace; i <= endWorkspace; i++) {
- let button = new Gtk.MenuItem({
- label: `Workspace ${i}`
- });
- button.connect("activate", () => {
- // execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print);
- actionFunc(thisWorkspace, i);
- });
- submenu.append(button);
- }
- menuItem.set_reserve_indicator(true);
- menuItem.set_submenu(submenu);
- }
-})
-
-const Window = ({ address, at: [x, y], size: [w, h], workspace: { id, name }, class: c, title, xwayland }) => {
- const revealInfoCondition = (Math.min(w, h) * OVERVIEW_SCALE > 70);
- if (w <= 0 || h <= 0 || (c === '' && title === '')) return null;
- if (x + w <= 0) x += (Math.floor(x / SCREEN_WIDTH) * SCREEN_WIDTH);
- else if (x < 0) { x = 0; w = x + w; }
- if (y + h <= 0) x += (Math.floor(y / SCREEN_HEIGHT) * SCREEN_HEIGHT);
- else if (y < 0) { y = 0; h = y + h; }
-
- if (x >= SCREEN_WIDTH) x %= SCREEN_WIDTH;
- else if (x + w > SCREEN_WIDTH) w = SCREEN_WIDTH - x;
- if (y >= SCREEN_HEIGHT) y %= SCREEN_HEIGHT;
- else if (y + h > SCREEN_HEIGHT) h = SCREEN_HEIGHT - y;
-
- // title = truncateTitle(title);
- return Widget.Button({
- attribute: { x, y },
- className: 'overview-tasks-window',
- hpack: 'center',
- vpack: 'center',
- onClicked: () => {
- Hyprland.sendMessage(`dispatch focuswindow address:${address}`);
- App.closeWindow('overview');
- },
- onMiddleClickRelease: () => Hyprland.sendMessage(`dispatch closewindow address:${address}`),
- 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: () => Hyprland.sendMessage(`dispatch closewindow address:${address}`),
- }),
- ContextMenuWorkspaceArray({
- label: "Dump windows to workspace",
- actionFunc: dumpToWorkspace,
- thisWorkspace: Number(id)
- }),
- ContextMenuWorkspaceArray({
- 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
- Widget.Revealer({
- transition: 'slide_down',
- revealChild: revealInfoCondition,
- 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 clientMap = new Map();
- 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,
- onPrimaryClick: () => {
- Hyprland.sendMessage(`dispatch workspace ${index}`)
- 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) => {
- Hyprland.sendMessage(`dispatch movetoworkspacesilent ${index},address:${data.get_text()}`)
+ const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
+ const startWorkspace = offset + 1;
+ const endWorkspace = startWorkspace + NUM_OF_WORKSPACES_SHOWN - 1;
+ for (let i = startWorkspace; i <= endWorkspace; i++) {
+ let button = new Gtk.MenuItem({
+ label: `Workspace ${i}`
+ });
+ button.connect("activate", () => {
+ // execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print);
+ actionFunc(thisWorkspace, i);
overviewTick.setValue(!overviewTick.value);
});
- },
- child: fixed,
- })],
- });
- widget.clear = () => {
- fixed.get_children().forEach(ch => ch.destroy());
- const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
- fixed.put(WorkspaceNumber(offset + index), 0, 0);
- }
- widget.set = (clientJson) => {
- // if(clientMap.get(clientJson.address)) clientMap.get(clientJson.address).destroy();
- const newWindow = Window(clientJson);
- if (newWindow === null) return;
- // clientMap.set(clientJson.address, newWindow);
- fixed.put(newWindow,
- Math.max(0, newWindow.attribute.x * OVERVIEW_SCALE),
- Math.max(0, newWindow.attribute.y * OVERVIEW_SCALE)
- );
- };
- // widget.unset = (clientAddress) => {
- // if(clientMap.get(clientAddress)) {
- // clientMap.get(clientAddress).destroy();
- // clientMap.delete(clientAddress);
- // }
- // };
- widget.show = () => {
- 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),
- attribute: {
- update: (box) => {
- const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
- if (!App.getWindow(windowName).visible) return;
- execAsync('hyprctl -j clients').then(clients => {
- const allClients = JSON.parse(clients);
- const kids = box.get_children();
- kids.forEach(kid => kid.clear());
- for (let i = 0; i < allClients.length; i++) {
- const client = allClients[i];
- if (offset + startWorkspace <= client.workspace.id &&
- client.workspace.id <= offset + startWorkspace + workspaces) {
- kids[client.workspace.id - (offset + startWorkspace)]
- ?.set(client);
- }
- }
- kids.forEach(kid => kid.show());
-
- }).catch(print);
+ submenu.append(button);
+ }
+ menuItem.set_reserve_indicator(true);
+ menuItem.set_submenu(submenu);
}
- },
- setup: (box) => box
- .hook(overviewTick, (box) => box.attribute.update(box))
- .hook(Hyprland, (box, clientAddress) => {
- box.attribute.update(box)
- }, 'client-removed')
- .hook(Hyprland, (box, clientAddress) => {
- box.attribute.update(box);
- }, 'client-added')
- .hook(Hyprland.active.workspace, (box) => box.attribute.update(box))
- .hook(App, (box, name, visible) => { // Update on open
- if (name == 'overview' && visible) box.attribute.update(box);
+ })
+
+ const Window = ({ address, at: [x, y], size: [w, h], workspace: { id, name }, class: c, title, xwayland }, screenCoords) => {
+ const revealInfoCondition = (Math.min(w, h) * OVERVIEW_SCALE > 70);
+ if (w <= 0 || h <= 0 || (c === '' && title === '')) return null;
+ // Non-primary monitors
+ if (screenCoords.x != 0) x -= screenCoords.x;
+ if (screenCoords.y != 0) y -= screenCoords.y;
+ // Other offscreen adjustments
+ if (x + w <= 0) x += (Math.floor(x / SCREEN_WIDTH) * SCREEN_WIDTH);
+ else if (x < 0) { w = x + w; x = 0; }
+ if (y + h <= 0) x += (Math.floor(y / SCREEN_HEIGHT) * SCREEN_HEIGHT);
+ else if (y < 0) { h = y + h; y = 0; }
+ // Truncate if offscreen
+ if (x + w > SCREEN_WIDTH) w = SCREEN_WIDTH - x;
+ if (y + h > SCREEN_HEIGHT) h = SCREEN_HEIGHT - y;
+
+ const appIcon = Widget.Icon({
+ icon: substitute(c),
+ size: Math.min(w, h) * OVERVIEW_SCALE / 2.5,
+ });
+ return Widget.Button({
+ attribute: {
+ address, x, y, w, h, ws: id,
+ updateIconSize: (self) => {
+ appIcon.size = Math.min(self.attribute.w, self.attribute.h) * OVERVIEW_SCALE / 2.5;
+ },
+ },
+ className: 'overview-tasks-window',
+ hpack: 'start',
+ vpack: 'start',
+ css: `
+ margin-left: ${Math.round(x * OVERVIEW_SCALE)}px;
+ margin-top: ${Math.round(y * OVERVIEW_SCALE)}px;
+ margin-right: -${Math.round((x + w) * OVERVIEW_SCALE)}px;
+ margin-bottom: -${Math.round((y + h) * OVERVIEW_SCALE)}px;
+ `,
+ onClicked: (self) => {
+ Hyprland.sendMessage(`dispatch focuswindow address:${address}`);
+ App.closeWindow('overview');
+ },
+ onMiddleClickRelease: () => Hyprland.sendMessage(`dispatch closewindow address:${address}`),
+ 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: () => Hyprland.sendMessage(`dispatch closewindow address:${address}`),
+ }),
+ ContextMenuWorkspaceArray({
+ label: "Dump windows to workspace",
+ actionFunc: dumpToWorkspace,
+ thisWorkspace: Number(id)
+ }),
+ ContextMenuWorkspaceArray({
+ 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_widget(button.get_parent(), Gravity.SOUTH, Gravity.NORTH, null); // Show menu below the button
+ button.connect("destroy", () => menu.destroy());
+ },
+ child: Widget.Box({
+ homogeneous: true,
+ child: Widget.Box({
+ vertical: true,
+ vpack: 'center',
+ className: 'spacing-v-5',
+ children: [
+ appIcon,
+ // TODO: Add xwayland tag instead of just having italics
+ // Widget.Revealer({
+ // transition: 'slide_down',
+ // revealChild: revealInfoCondition,
+ // 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 = Widget.Fixed({
+ // attribute: {
+ // put: (widget, x, y) => {
+ // fixed.put(widget, x, y);
+ // },
+ // move: (widget, x, y) => {
+ // fixed.move(widget, x, y);
+ // },
+ // }
+ // });
+ const fixed = Widget.Box({
+ attribute: {
+ put: (widget, x, y) => {
+ if (!widget.attribute) return;
+ // Note: x and y are already multiplied by OVERVIEW_SCALE
+ const newCss = `
+ margin-left: ${Math.round(x)}px;
+ margin-top: ${Math.round(y)}px;
+ margin-right: -${Math.round(x + (widget.attribute.w * OVERVIEW_SCALE))}px;
+ margin-bottom: -${Math.round(y + (widget.attribute.h * OVERVIEW_SCALE))}px;
+ `;
+ widget.css = newCss;
+ fixed.pack_start(widget, false, false, 0);
+ },
+ move: (widget, x, y) => {
+ if (!widget) return;
+ if (!widget.attribute) return;
+ // Note: x and y are already multiplied by OVERVIEW_SCALE
+ const newCss = `
+ margin-left: ${Math.round(x)}px;
+ margin-top: ${Math.round(y)}px;
+ margin-right: -${Math.round(x + (widget.attribute.w * OVERVIEW_SCALE))}px;
+ margin-bottom: -${Math.round(y + (widget.attribute.h * OVERVIEW_SCALE))}px;
+ `;
+ widget.css = newCss;
+ },
+ }
})
- ,
-});
+ 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,
+ onPrimaryClick: () => {
+ Hyprland.sendMessage(`dispatch workspace ${index}`)
+ 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) => {
+ Hyprland.sendMessage(`dispatch movetoworkspacesilent ${index},address:${data.get_text()}`)
+ overviewTick.setValue(!overviewTick.value);
+ });
+ },
+ child: fixed,
+ })],
+ });
+ const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
+ fixed.attribute.put(WorkspaceNumber(offset + index), 0, 0);
+ widget.clear = () => {
+ const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
+ clientMap.forEach((client, address) => {
+ if (!client || client.ws !== offset + index) return;
+ client.destroy();
+ client = null;
+ clientMap.delete(address);
+ });
+ }
+ widget.set = (clientJson, screenCoords) => {
+ let c = clientMap.get(clientJson.address);
+ if (c) {
+ if (c.attribute?.ws !== clientJson.workspace.id) {
+ c.destroy();
+ c = null;
+ clientMap.delete(clientJson.address);
+ }
+ else if (c) {
+ c.attribute.w = clientJson.size[0];
+ c.attribute.h = clientJson.size[1];
+ c.attribute.updateIconSize(c);
+ fixed.attribute.move(c,
+ Math.max(0, clientJson.at[0] * OVERVIEW_SCALE),
+ Math.max(0, clientJson.at[1] * OVERVIEW_SCALE)
+ );
+ return;
+ }
+ }
+ const newWindow = Window(clientJson, screenCoords);
+ if (newWindow === null) return;
+ // clientMap.set(clientJson.address, newWindow);
+ fixed.attribute.put(newWindow,
+ Math.max(0, newWindow.attribute.x * OVERVIEW_SCALE),
+ Math.max(0, newWindow.attribute.y * OVERVIEW_SCALE)
+ );
+ clientMap.set(clientJson.address, newWindow);
+ };
+ widget.unset = (clientAddress) => {
+ const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
+ let c = clientMap.get(clientAddress);
+ if (!c) return;
+ c.destroy();
+ c = null;
+ clientMap.delete(clientAddress);
+ };
+ widget.show = () => {
+ fixed.show_all();
+ }
+ return widget;
+ };
+ const arr = (s, n) => {
+ const array = [];
+ for (let i = 0; i < n; i++)
+ array.push(s + i);
-export default () => Widget.Revealer({
- revealChild: true,
- transition: 'slide_down',
- transitionDuration: 200,
- child: Widget.Box({
- vertical: true,
- className: 'overview-tasks',
- children: Array.from({ length: NUM_OF_WORKSPACE_ROWS }, (_, index) =>
- OverviewRow({
- startWorkspace: 1 + index * NUM_OF_WORKSPACE_COLS,
- workspaces: NUM_OF_WORKSPACE_COLS,
- })
- )
- }),
-});
+ return array;
+ };
+
+ const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => Widget.Box({
+ children: arr(startWorkspace, workspaces).map(Workspace),
+ attribute: {
+ monitorMap: [],
+ getMonitorMap: (box) => {
+ execAsync('hyprctl -j monitors').then(monitors => {
+ box.attribute.monitorMap = JSON.parse(monitors).reduce((acc, item) => {
+ acc[item.id] = { x: item.x, y: item.y };
+ return acc;
+ }, {});
+ });
+ },
+ update: (box) => {
+ const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
+ if (!App.getWindow(windowName).visible) return;
+ Hyprland.sendMessage('j/clients').then(clients => {
+ const allClients = JSON.parse(clients);
+ const kids = box.get_children();
+ kids.forEach(kid => kid.clear());
+ // console.log('----------------------------');
+ for (let i = 0; i < allClients.length; i++) {
+ const client = allClients[i];
+ const childID = client.workspace.id - (offset + startWorkspace);
+ if (offset + startWorkspace <= client.workspace.id &&
+ client.workspace.id <= offset + startWorkspace + workspaces) {
+ const screenCoords = box.attribute.monitorMap[client.monitor];
+ if (kids[childID]) {
+ kids[childID].set(client, screenCoords);
+ }
+ continue;
+ }
+ // const modID = client.workspace.id % NUM_OF_WORKSPACES_SHOWN;
+ // console.log(`[${startWorkspace} -> ${startWorkspace + workspaces - 1}] ? (${client.workspace.id} == ${modID})`);
+ // // console.log(`[${startWorkspace} -> ${startWorkspace + workspaces}] ? (${modID})`);
+ // if (startWorkspace <= modID && modID < startWorkspace + workspaces) {
+ // console.log('i care');
+ // const clientWidget = clientMap.get(client.address);
+ // console.log(childID, kids[childID], clientWidget);
+ // if (kids[childID] && clientWidget) {
+ // console.log('hmm remove', clientWidget.attribute)
+ // kids[childID].remove(clientWidget);
+ // }
+ // }
+ }
+ kids.forEach(kid => kid.show());
+ }).catch(print);
+ },
+ updateWorkspace: (box, id) => {
+ const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
+ if (!( // Not in range, ignore
+ offset + startWorkspace <= id &&
+ id <= offset + startWorkspace + workspaces
+ )) return;
+ // if (!App.getWindow(windowName).visible) return;
+ Hyprland.sendMessage('j/clients').then(clients => {
+ const allClients = JSON.parse(clients);
+ const kids = box.get_children();
+ for (let i = 0; i < allClients.length; i++) {
+ const client = allClients[i];
+ if (client.workspace.id != id) continue;
+ const screenCoords = box.attribute.monitorMap[client.monitor];
+ kids[id - (offset + startWorkspace)]?.set(client, screenCoords);
+ }
+ kids[id - (offset + startWorkspace)]?.show();
+ }).catch(print);
+ },
+ },
+ setup: (box) => {
+ box.attribute.getMonitorMap(box);
+ box
+ .hook(overviewTick, (box) => box.attribute.update(box))
+ .hook(Hyprland, (box, clientAddress) => {
+ const offset = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN) * NUM_OF_WORKSPACES_SHOWN;
+ const kids = box.get_children();
+ const client = Hyprland.getClient(clientAddress);
+ if (!client) return;
+ const id = client.workspace.id;
+
+ box.attribute.updateWorkspace(box, id);
+ kids[id - (offset + startWorkspace)]?.unset(clientAddress);
+ }, 'client-removed')
+ .hook(Hyprland, (box, clientAddress) => {
+ const client = Hyprland.getClient(clientAddress);
+ if (!client) return;
+ box.attribute.updateWorkspace(box, client.workspace.id);
+ }, 'client-added')
+ .hook(Hyprland.active.workspace, (box) => {
+ const previousGroup = box.attribute.workspaceGroup;
+ const currentGroup = Math.floor((Hyprland.active.workspace.id - 1) / NUM_OF_WORKSPACES_SHOWN);
+ if (currentGroup !== previousGroup) {
+ box.attribute.update(box);
+ workspaceGroup = currentGroup;
+ }
+ // box.attribute.update(box);
+ })
+ .hook(App, (box, name, visible) => { // Update on open
+ if (name == 'overview' && visible) box.attribute.update(box);
+ })
+ },
+ });
+
+ return Widget.Revealer({
+ revealChild: true,
+ transition: 'slide_down',
+ transitionDuration: 200,
+ child: Widget.Box({
+ vertical: true,
+ className: 'overview-tasks',
+ children: Array.from({ length: NUM_OF_WORKSPACE_ROWS }, (_, index) =>
+ OverviewRow({
+ startWorkspace: 1 + index * NUM_OF_WORKSPACE_COLS,
+ workspaces: NUM_OF_WORKSPACE_COLS,
+ })
+ )
+ }),
+ });
+}
\ No newline at end of file
diff --git a/.config/ags/widgets/overview/searchbuttons.js b/.config/ags/widgets/overview/searchbuttons.js
index 8ea12c887..15130ad05 100644
--- a/.config/ags/widgets/overview/searchbuttons.js
+++ b/.config/ags/widgets/overview/searchbuttons.js
@@ -38,12 +38,6 @@ export const DirectoryButton = ({ parentPath, name, type, 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({
@@ -112,12 +106,6 @@ export const DesktopEntryButton = (app) => {
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({
diff --git a/.config/ags/widgets/overview/windowcontent.js b/.config/ags/widgets/overview/windowcontent.js
index 68b9d34a4..5a9dd8882 100644
--- a/.config/ags/widgets/overview/windowcontent.js
+++ b/.config/ags/widgets/overview/windowcontent.js
@@ -1,4 +1,4 @@
-const { Gtk } = imports.gi;
+const { Gdk, Gtk } = imports.gi;
import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
@@ -76,7 +76,7 @@ export const SearchAndWindows = () => {
child: Widget.Label({
className: 'overview-search-prompt txt-small txt',
label: 'Type to search'
- })
+ }),
});
const entryIconRevealer = Widget.Revealer({
@@ -217,7 +217,10 @@ export const SearchAndWindows = () => {
entry,
Widget.Box({
className: 'overview-search-icon-box',
- setup: box => box.pack_start(entryPromptRevealer, true, true, 0),
+ setup: (box) => {
+ box.pack_start(entryPromptRevealer, true, true, 0)
+ // enableClickthrough(box);
+ },
}),
entryIcon,
]
@@ -233,10 +236,31 @@ export const SearchAndWindows = () => {
}
})
.on('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);
+ const keyval = event.get_keyval()[1];
+ const modstate = event.get_state()[1];
+ if (modstate & Gdk.ModifierType.CONTROL_MASK) { // Ctrl held
+ if (keyval == Gdk.KEY_b)
+ entry.set_position(Math.max(entry.get_position() - 1, 0));
+ else if (keyval == Gdk.KEY_f)
+ entry.set_position(Math.min(entry.get_position() + 1, entry.get_text().length));
+ else if (keyval == Gdk.KEY_n) { // simulate Down arrow
+ entry.get_root_window().simulate_key_press(Gdk.KEY_Down, Gdk.ModifierType.NONE);
+ // entry.get_root_window().simulate_key_release(Gdk.KEY_Down, Gdk.ModifierType.NONE);
+ }
+ else if (keyval == Gdk.KEY_k) { // Delete to end
+ const text = entry.get_text();
+ const pos = entry.get_position();
+ const newText = text.slice(0, pos);
+ entry.set_text(newText);
+ entry.set_position(newText.length);
+ }
+ }
+ else { // Ctrl not held
+ if (keyval >= 32 && keyval <= 126 && widget != entry) {
+ Utils.timeout(1, () => entry.grab_focus());
+ entry.set_text(entry.text + String.fromCharCode(keyval));
+ entry.set_position(-1);
+ }
}
})
,
diff --git a/.config/ags/widgets/sideleft/apis/ai_chatmessage.js b/.config/ags/widgets/sideleft/apis/ai_chatmessage.js
index 66b589cd1..d2cf8d20a 100644
--- a/.config/ags/widgets/sideleft/apis/ai_chatmessage.js
+++ b/.config/ags/widgets/sideleft/apis/ai_chatmessage.js
@@ -44,6 +44,7 @@ function copyToClipboard(text) {
function substituteLang(str) {
const subs = [
{ from: 'javascript', to: 'js' },
+ { from: 'bash', to: 'sh' },
];
for (const { from, to } of subs) {
@@ -58,7 +59,7 @@ const HighlightedCode = (content, lang) => {
const buffer = new GtkSource.Buffer();
const sourceView = new GtkSource.View({
buffer: buffer,
- wrap_mode: Gtk.WrapMode.WORD
+ wrap_mode: Gtk.WrapMode.NONE
});
const langManager = GtkSource.LanguageManager.get_default();
let displayLang = langManager.get_language(substituteLang(lang)); // Set your preferred language
diff --git a/.config/ags/widgets/sideleft/apis/chatgpt.js b/.config/ags/widgets/sideleft/apis/chatgpt.js
index 91de66250..16e54b161 100644
--- a/.config/ags/widgets/sideleft/apis/chatgpt.js
+++ b/.config/ags/widgets/sideleft/apis/chatgpt.js
@@ -19,14 +19,6 @@ export const chatGPTTabIcon = Icon({
hpack: 'center',
className: 'sidebar-chat-apiswitcher-icon',
icon: `openai-symbolic`,
- setup: (self) => Utils.timeout(513, () => { // stupid condition race
- 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);
- // console.log(Math.round(Math.max(width, height, 1)));
- self.size = Math.max(width, height, 1) * 116 / 180;
- // ↑ Why such a specific proportion? See https://openai.com/brand#logos
- })
});
const ChatGPTInfo = () => {
@@ -34,14 +26,6 @@ const ChatGPTInfo = () => {
hpack: 'center',
className: 'sidebar-chat-welcome-logo',
icon: `openai-symbolic`,
- setup: (self) => Utils.timeout(513, () => { // stupid condition race
- 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);
- // console.log(Math.round(Math.max(width, height, 1)));
- self.size = Math.max(width, height, 1) * 116 / 180;
- // ↑ Why such a specific proportion? See https://openai.com/brand#logos
- })
});
return Box({
vertical: true,
diff --git a/.config/ags/widgets/sideleft/apis/gemini.js b/.config/ags/widgets/sideleft/apis/gemini.js
index 0931c9730..6d9da681d 100644
--- a/.config/ags/widgets/sideleft/apis/gemini.js
+++ b/.config/ags/widgets/sideleft/apis/gemini.js
@@ -20,12 +20,6 @@ export const geminiTabIcon = Icon({
hpack: 'center',
className: 'sidebar-chat-apiswitcher-icon',
icon: `google-gemini-symbolic`,
- setup: (self) => Utils.timeout(513, () => { // stupid condition race
- 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;
- })
})
const GeminiInfo = () => {
@@ -33,12 +27,6 @@ const GeminiInfo = () => {
hpack: 'center',
className: 'sidebar-chat-welcome-logo',
icon: `google-gemini-symbolic`,
- setup: (self) => Utils.timeout(513, () => { // stupid condition race
- 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;
- })
});
return Box({
vertical: true,
diff --git a/.config/ags/widgets/sideleft/apis/waifu.js b/.config/ags/widgets/sideleft/apis/waifu.js
index ef31b6d66..94da96032 100644
--- a/.config/ags/widgets/sideleft/apis/waifu.js
+++ b/.config/ags/widgets/sideleft/apis/waifu.js
@@ -417,6 +417,14 @@ export const sendMessage = (text) => {
true
));
}
+ else if (text.startsWith('/place')) {
+ const newImage = WaifuImage(['/place']);
+ waifuContent.add(newImage);
+ Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update(
+ DummyTag(400, 600, 'https://placewaifu.com/image/400/600', '#F0A235'),
+ true
+ ));
+ }
}
else WaifuService.fetch(text);
diff --git a/.config/ags/widgets/sideleft/apiwidgets.js b/.config/ags/widgets/sideleft/apiwidgets.js
index 4ac117f53..f754fae37 100644
--- a/.config/ags/widgets/sideleft/apiwidgets.js
+++ b/.config/ags/widgets/sideleft/apiwidgets.js
@@ -13,19 +13,12 @@ import Gemini from '../../services/gemini.js';
import { geminiView, geminiCommands, sendMessage as geminiSendMessage, geminiTabIcon } from './apis/gemini.js';
import { chatGPTView, chatGPTCommands, sendMessage as chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js';
-const TextView = Widget.subclass(Gtk.TextView, "AgsTextView");
+import { enableClickthrough } from '../../lib/roundedcorner.js';
+const TextView = Widget.subclass(Gtk.TextView, "AgsTextView");
const EXPAND_INPUT_THRESHOLD = 30;
const APIS = [
- {
- name: 'Assistant (ChatGPT 3.5)',
- sendCommand: chatGPTSendMessage,
- contentWidget: chatGPTView,
- commandBar: chatGPTCommands,
- tabIcon: chatGPTTabIcon,
- placeholderText: 'Message ChatGPT...',
- },
{
name: 'Assistant (Gemini Pro)',
sendCommand: geminiSendMessage,
@@ -34,6 +27,14 @@ const APIS = [
tabIcon: geminiTabIcon,
placeholderText: 'Message Gemini...',
},
+ {
+ name: 'Assistant (ChatGPT 3.5)',
+ sendCommand: chatGPTSendMessage,
+ contentWidget: chatGPTView,
+ commandBar: chatGPTCommands,
+ tabIcon: chatGPTTabIcon,
+ placeholderText: 'Message ChatGPT...',
+ },
{
name: 'Waifus',
sendCommand: waifuSendMessage,
@@ -141,6 +142,7 @@ const chatPlaceholderRevealer = Revealer({
transition: 'crossfade',
transitionDuration: 200,
child: chatPlaceholder,
+ setup: enableClickthrough,
});
const textboxArea = Box({ // Entry area
@@ -159,12 +161,20 @@ const textboxArea = Box({ // Entry area
const apiContentStack = Stack({
vexpand: true,
transition: 'slide_left_right',
- items: APIS.map(api => [api.name, api.contentWidget]),
+ transitionDuration: 160,
+ children: APIS.reduce((acc, api) => {
+ acc[api.name] = api.contentWidget;
+ return acc;
+ }, {}),
})
const apiCommandStack = Stack({
transition: 'slide_up_down',
- items: APIS.map(api => [api.name, api.commandBar]),
+ transitionDuration: 160,
+ children: APIS.reduce((acc, api) => {
+ acc[api.name] = api.commandBar;
+ return acc;
+ }, {}),
})
function switchToTab(id) {
diff --git a/.config/ags/widgets/sideleft/module.js b/.config/ags/widgets/sideleft/module.js
deleted file mode 100644
index 319b5a35c..000000000
--- a/.config/ags/widgets/sideleft/module.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import Widget from 'resource:///com/github/Aylur/ags/widget.js';
-const { Box, Button, Label } = Widget;
-
-export const SidebarModule = ({
- name,
- child
-}) => {
- return Box({
- className: 'sidebar-module',
- vertical: true,
- children: [
- Button({
- child: Box({
- children: [
- Label({
- className: 'txt-small txt',
- label: `${name}`,
- }),
- Box({
- hexpand: true,
- }),
- Label({
- className: 'sidebar-module-btn-arrow',
- })
- ]
- })
- })
- ]
- });
-}
\ No newline at end of file
diff --git a/.config/ags/widgets/sideleft/quickscripts.js b/.config/ags/widgets/sideleft/quickscripts.js
deleted file mode 100644
index 13bf9c1a1..000000000
--- a/.config/ags/widgets/sideleft/quickscripts.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Widget from 'resource:///com/github/Aylur/ags/widget.js';
-import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
-const { execAsync, exec } = Utils;
-const { Box, Button, EventBox, Label, Scrollable } = Widget;
-import { SidebarModule } from './module.js';
-
-export const QuickScripts = () => SidebarModule({
- name: 'Quick scripts',
- child: Box({
- })
-})
\ No newline at end of file
diff --git a/.config/ags/widgets/sideleft/sideleft.js b/.config/ags/widgets/sideleft/sideleft.js
index fadf7cad0..d67d87ade 100644
--- a/.config/ags/widgets/sideleft/sideleft.js
+++ b/.config/ags/widgets/sideleft/sideleft.js
@@ -30,7 +30,11 @@ let currentTabId = 0;
export const contentStack = Stack({
vexpand: true,
transition: 'slide_left_right',
- items: contents.map(item => [item.name, item.content]),
+ transitionDuration: 160,
+ children: contents.reduce((acc, item) => {
+ acc[item.name] = item.content;
+ return acc;
+ }, {}),
})
function switchToTab(id) {
diff --git a/.config/ags/widgets/sideleft/toolbox.js b/.config/ags/widgets/sideleft/toolbox.js
index 0c14beded..9ecb98b92 100644
--- a/.config/ags/widgets/sideleft/toolbox.js
+++ b/.config/ags/widgets/sideleft/toolbox.js
@@ -2,15 +2,18 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { Box, Button, EventBox, Label, Revealer, Scrollable, Stack } = Widget;
const { execAsync, exec } = Utils;
-import { QuickScripts } from './quickscripts.js';
+import QuickScripts from './tools/quickscripts.js';
+import ColorPicker from './tools/colorpicker.js';
export default Scrollable({
hscroll: "never",
vscroll: "automatic",
child: Box({
vertical: true,
+ className: 'spacing-v-10',
children: [
- // QuickScripts(),
+ QuickScripts(),
+ ColorPicker(),
]
})
});
diff --git a/.config/ags/widgets/sideleft/tools/color.js b/.config/ags/widgets/sideleft/tools/color.js
new file mode 100644
index 000000000..f93b56b57
--- /dev/null
+++ b/.config/ags/widgets/sideleft/tools/color.js
@@ -0,0 +1,199 @@
+// It's weird, I know
+const { Gio, GLib } = imports.gi;
+import Service from 'resource:///com/github/Aylur/ags/service.js';
+import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
+const { exec, execAsync } = Utils;
+
+const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
+
+export class ColorPickerSelection extends Service {
+ static {
+ Service.register(this, {
+ 'picked': [],
+ 'assigned': ['int'],
+ 'hue': [],
+ 'sl': [],
+ });
+ }
+
+ _hue = 198;
+ _xAxis = 94;
+ _yAxis = 80;
+
+ get hue() { return this._hue; }
+ set hue(value) {
+ this._hue = clamp(value, 0, 360);
+ this.emit('hue');
+ this.emit('picked');
+ this.emit('changed');
+ }
+ get xAxis() { return this._xAxis; }
+ set xAxis(value) {
+ this._xAxis = clamp(value, 0, 100);
+ this.emit('sl');
+ this.emit('picked');
+ this.emit('changed');
+ }
+ get yAxis() { return this._yAxis; }
+ set yAxis(value) {
+ this._yAxis = clamp(value, 0, 100);
+ this.emit('sl');
+ this.emit('picked');
+ this.emit('changed');
+ }
+ setColorFromHex(hexString, id) {
+ const hsl = hexToHSL(hexString);
+ this._hue = hsl.hue;
+ this._xAxis = hsl.saturation;
+ // this._yAxis = hsl.lightness;
+ this._yAxis = (100 - hsl.saturation / 2) / 100 * hsl.lightness;
+ // console.log(this._hue, this._xAxis, this._yAxis)
+ this.emit('assigned', id);
+ this.emit('changed');
+ }
+
+ constructor() {
+ super();
+ this.emit('changed');
+ }
+}
+
+
+export function hslToRgbValues(h, s, l) {
+ h /= 360;
+ s /= 100;
+ l /= 100;
+ let r, g, b;
+ if (s === 0) {
+ r = g = b = l; // achromatic
+ } else {
+ const hue2rgb = (p, q, t) => {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ };
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+ const to255 = x => Math.round(x * 255);
+ r = to255(r);
+ g = to255(g);
+ b = to255(b);
+ return `${Math.round(r)},${Math.round(g)},${Math.round(b)}`;
+ // return `rgb(${r},${g},${b})`;
+}
+export function hslToHex(h, s, l) {
+ h /= 360;
+ s /= 100;
+ l /= 100;
+ let r, g, b;
+ if (s === 0) {
+ r = g = b = l; // achromatic
+ } else {
+ const hue2rgb = (p, q, t) => {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ };
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+ const toHex = x => {
+ const hex = Math.round(x * 255).toString(16);
+ return hex.length === 1 ? "0" + hex : hex;
+ };
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
+}
+
+// export function hexToHSL(hex) {
+// // Remove the '#' if present
+// hex = hex.replace(/^#/, '');
+// // Parse the hex value into RGB components
+// const bigint = parseInt(hex, 16);
+// const r = (bigint >> 16) & 255;
+// const g = (bigint >> 8) & 255;
+// const b = bigint & 255;
+// // Normalize RGB values to range [0, 1]
+// const normalizedR = r / 255;
+// const normalizedG = g / 255;
+// const normalizedB = b / 255;
+// // Find the maximum and minimum values
+// const max = Math.max(normalizedR, normalizedG, normalizedB);
+// const min = Math.min(normalizedR, normalizedG, normalizedB);
+// // Calculate the lightness
+// const lightness = (max + min) / 2;
+// // If the color is grayscale, set saturation to 0
+// if (max === min) {
+// return {
+// hue: 0,
+// saturation: 0,
+// lightness: lightness * 100 // Convert to percentage
+// };
+// }
+// // Calculate the saturation
+// const d = max - min;
+// const saturation = lightness > 0.5 ? d / (2 - max - min) : d / (max + min);
+// // Calculate the hue
+// let hue;
+// if (max === normalizedR) {
+// hue = ((normalizedG - normalizedB) / d + (normalizedG < normalizedB ? 6 : 0)) * 60;
+// } else if (max === normalizedG) {
+// hue = ((normalizedB - normalizedR) / d + 2) * 60;
+// } else {
+// hue = ((normalizedR - normalizedG) / d + 4) * 60;
+// }
+// return {
+// hue: Math.round(hue),
+// saturation: Math.round(saturation * 100), // Convert to percentage
+// lightness: Math.round(lightness * 100) // Convert to percentage
+// };
+// }
+
+export function hexToHSL(hex) {
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+
+ var r = parseInt(result[1], 16);
+ var g = parseInt(result[2], 16);
+ var b = parseInt(result[3], 16);
+
+ r /= 255, g /= 255, b /= 255;
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2;
+
+ if (max == min) {
+ h = s = 0; // achromatic
+ } else {
+ var d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch (max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+
+ s = s * 100;
+ s = Math.round(s);
+ l = l * 100;
+ l = Math.round(l);
+ h = Math.round(360 * h);
+
+ return {
+ hue: h,
+ saturation: s,
+ lightness: l
+ };
+}
diff --git a/.config/ags/widgets/sideleft/tools/colorpicker.js b/.config/ags/widgets/sideleft/tools/colorpicker.js
new file mode 100644
index 000000000..016eeba6d
--- /dev/null
+++ b/.config/ags/widgets/sideleft/tools/colorpicker.js
@@ -0,0 +1,284 @@
+// TODO: Make selection update when entry changes
+const { Gtk } = imports.gi;
+import App from 'resource:///com/github/Aylur/ags/app.js';
+import Variable from 'resource:///com/github/Aylur/ags/variable.js';
+import Widget from 'resource:///com/github/Aylur/ags/widget.js';
+import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
+const { execAsync, exec } = Utils;
+const { Box, Button, Entry, EventBox, Icon, Label, Overlay, Scrollable } = Widget;
+import SidebarModule from './module.js';
+import { MaterialIcon } from '../../../lib/materialicon.js';
+import { setupCursorHover } from '../../../lib/cursorhover.js';
+
+import { ColorPickerSelection, hslToHex, hslToRgbValues, hexToHSL } from './color.js';
+
+const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
+
+export default () => {
+ const selectedColor = new ColorPickerSelection();
+ function shouldUseBlackColor() {
+ return ((selectedColor.xAxis < 40 || (45 <= selectedColor.hue && selectedColor.hue <= 195)) &&
+ selectedColor.yAxis > 60);
+ }
+ const colorBlack = 'rgba(0,0,0,0.9)';
+ const colorWhite = 'rgba(255,255,255,0.9)';
+ const hueRange = Box({
+ homogeneous: true,
+ className: 'sidebar-module-colorpicker-wrapper',
+ children: [Box({
+ className: 'sidebar-module-colorpicker-hue',
+ css: `background: linear-gradient(to bottom, #ff6666, #ffff66, #66dd66, #66ffff, #6666ff, #ff66ff, #ff6666);`,
+ })],
+ });
+ const hueSlider = Box({
+ vpack: 'start',
+ className: 'sidebar-module-colorpicker-cursorwrapper',
+ css: `margin-top: ${13.636 * selectedColor.hue / 360}rem;`,
+ homogeneous: true,
+ children: [Box({
+ className: 'sidebar-module-colorpicker-hue-cursor',
+ })],
+ setup: (self) => self.hook(selectedColor, () => {
+ const widgetHeight = hueRange.children[0].get_allocated_height();
+ self.setCss(`margin-top: ${13.636 * selectedColor.hue / 360}rem;`)
+ }),
+ });
+ const hueSelector = Box({
+ children: [EventBox({
+ child: Overlay({
+ child: hueRange,
+ overlays: [hueSlider],
+ }),
+ attribute: {
+ clicked: false,
+ setHue: (self, event) => {
+ const widgetHeight = hueRange.children[0].get_allocated_height();
+ const [_, cursorX, cursorY] = event.get_coords();
+ const cursorYPercent = clamp(cursorY / widgetHeight, 0, 1);
+ selectedColor.hue = Math.round(cursorYPercent * 360);
+ }
+ },
+ setup: (self) => self
+ .on('motion-notify-event', (self, event) => {
+ if (!self.attribute.clicked) return;
+ self.attribute.setHue(self, event);
+ })
+ .on('button-press-event', (self, event) => {
+ if (!(event.get_button()[1] === 1)) return; // We're only interested in left-click here
+ self.attribute.clicked = true;
+ self.attribute.setHue(self, event);
+ })
+ .on('button-release-event', (self) => self.attribute.clicked = false)
+ ,
+ })]
+ });
+ const saturationAndLightnessRange = Box({
+ homogeneous: true,
+ children: [Box({
+ className: 'sidebar-module-colorpicker-saturationandlightness',
+ attribute: {
+ update: (self) => {
+ // css: `background: linear-gradient(to right, #ffffff, color);`,
+ self.setCss(`background:
+ linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,1)),
+ linear-gradient(to right, #ffffff, ${hslToHex(selectedColor.hue, 100, 50)});
+ `);
+ },
+ },
+ setup: (self) => self
+ .hook(selectedColor, self.attribute.update, 'hue')
+ .hook(selectedColor, self.attribute.update, 'assigned')
+ ,
+ })],
+ });
+ const saturationAndLightnessCursor = Box({
+ className: 'sidebar-module-colorpicker-saturationandlightness-cursorwrapper',
+ children: [Box({
+ vpack: 'start',
+ hpack: 'start',
+ homogeneous: true,
+ css: `
+ margin-left: ${13.636 * selectedColor.xAxis / 100}rem;
+ margin-top: ${13.636 * (100 - selectedColor.yAxis) / 100}rem;
+ `, // Why 13.636rem? see class name in stylesheet
+ attribute: {
+ update: (self) => {
+ const allocation = saturationAndLightnessRange.children[0].get_allocation();
+ self.setCss(`
+ margin-left: ${13.636 * selectedColor.xAxis / 100}rem;
+ margin-top: ${13.636 * (100 - selectedColor.yAxis) / 100}rem;
+ `); // Why 13.636rem? see class name in stylesheet
+ }
+ },
+ setup: (self) => self
+ .hook(selectedColor, self.attribute.update, 'sl')
+ .hook(selectedColor, self.attribute.update, 'assigned')
+ ,
+ children: [Box({
+ className: 'sidebar-module-colorpicker-saturationandlightness-cursor',
+ css: `
+ background-color: ${hslToHex(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100))};
+ border-color: ${shouldUseBlackColor() ? colorBlack : colorWhite};
+ `,
+ attribute: {
+ update: (self) => {
+ self.setCss(`
+ background-color: ${hslToHex(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100))};
+ border-color: ${shouldUseBlackColor() ? colorBlack : colorWhite};
+ `);
+ }
+ },
+ setup: (self) => self
+ .hook(selectedColor, self.attribute.update, 'sl')
+ .hook(selectedColor, self.attribute.update, 'hue')
+ .hook(selectedColor, self.attribute.update, 'assigned')
+ ,
+ })],
+ })]
+ });
+ const saturationAndLightnessSelector = Box({
+ homogeneous: true,
+ className: 'sidebar-module-colorpicker-saturationandlightness-wrapper',
+ children: [EventBox({
+ child: Overlay({
+ child: saturationAndLightnessRange,
+ overlays: [saturationAndLightnessCursor],
+ }),
+ attribute: {
+ clicked: false,
+ setSaturationAndLightness: (self, event) => {
+ const allocation = saturationAndLightnessRange.children[0].get_allocation();
+ const [_, cursorX, cursorY] = event.get_coords();
+ const cursorXPercent = clamp(cursorX / allocation.width, 0, 1);
+ const cursorYPercent = clamp(cursorY / allocation.height, 0, 1);
+ selectedColor.xAxis = Math.round(cursorXPercent * 100);
+ selectedColor.yAxis = Math.round(100 - cursorYPercent * 100);
+ }
+ },
+ setup: (self) => self
+ .on('motion-notify-event', (self, event) => {
+ if (!self.attribute.clicked) return;
+ self.attribute.setSaturationAndLightness(self, event);
+ })
+ .on('button-press-event', (self, event) => {
+ if (!(event.get_button()[1] === 1)) return; // We're only interested in left-click here
+ self.attribute.clicked = true;
+ self.attribute.setSaturationAndLightness(self, event);
+ })
+ .on('button-release-event', (self) => self.attribute.clicked = false)
+ ,
+ })]
+ });
+ const resultColorBox = Box({
+ className: 'sidebar-module-colorpicker-result-box',
+ homogeneous: true,
+ css: `background-color: ${hslToHex(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100))};`,
+ children: [Label({
+ className: 'txt txt-small',
+ label: 'Result',
+ }),],
+ attribute: {
+ update: (self) => {
+ self.setCss(`background-color: ${hslToHex(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100))};`);
+ self.children[0].setCss(`color: ${shouldUseBlackColor() ? colorBlack : colorWhite};`)
+ }
+ },
+ setup: (self) => self
+ .hook(selectedColor, self.attribute.update, 'sl')
+ .hook(selectedColor, self.attribute.update, 'hue')
+ .hook(selectedColor, self.attribute.update, 'assigned')
+ ,
+ });
+ const ResultBox = ({ colorSystemName, updateCallback, copyCallback }) => Box({
+ children: [
+ Box({
+ vertical: true,
+ hexpand: true,
+ children: [
+ Label({
+ xalign: 0,
+ className: 'txt-tiny',
+ label: colorSystemName,
+ }),
+ Overlay({
+ child: Entry({
+ widthChars: 10,
+ className: 'txt-small techfont',
+ attribute: {
+ id: 0,
+ update: updateCallback,
+ },
+ setup: (self) => self
+ .hook(selectedColor, self.attribute.update, 'sl')
+ .hook(selectedColor, self.attribute.update, 'hue')
+ .hook(selectedColor, self.attribute.update, 'assigned')
+ // .on('activate', (self) => {
+ // const newColor = self.text;
+ // if (newColor.length != 7) return;
+ // selectedColor.setColorFromHex(self.text, self.attribute.id);
+ // })
+ ,
+ }),
+ })
+ ]
+ }),
+ Button({
+ child: MaterialIcon('content_copy', 'norm'),
+ onClicked: (self) => {
+ copyCallback(self);
+ self.child.label = 'done';
+ Utils.timeout(1000, () => self.child.label = 'content_copy');
+ },
+ setup: setupCursorHover,
+ })
+ ]
+ });
+ const resultHex = ResultBox({
+ colorSystemName: 'Hex',
+ updateCallback: (self, id) => {
+ if (id && self.attribute.id === id) return;
+ self.text = hslToHex(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100));
+ },
+ copyCallback: () => Utils.execAsync(['wl-copy', `${hslToHex(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100))}`]),
+ })
+ const resultRgb = ResultBox({
+ colorSystemName: 'RGB',
+ updateCallback: (self, id) => {
+ if (id && self.attribute.id === id) return;
+ self.text = hslToRgbValues(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100));
+ },
+ copyCallback: () => Utils.execAsync(['wl-copy', `rgb(${hslToRgbValues(selectedColor.hue, selectedColor.xAxis, selectedColor.yAxis / (1 + selectedColor.xAxis / 100))})`]),
+ })
+ const resultHsl = ResultBox({
+ colorSystemName: 'HSL',
+ updateCallback: (self, id) => {
+ if (id && self.attribute.id === id) return;
+ self.text = `${selectedColor.hue},${selectedColor.xAxis}%,${Math.round(selectedColor.yAxis / (1 + selectedColor.xAxis / 100))}%`;
+ },
+ copyCallback: () => Utils.execAsync(['wl-copy', `hsl(${selectedColor.hue},${selectedColor.xAxis}%,${Math.round(selectedColor.yAxis / (1 + selectedColor.xAxis / 100))}%)`]),
+ })
+ const result = Box({
+ className: 'sidebar-module-colorpicker-result-area spacing-v-5 txt',
+ hexpand: true,
+ vertical: true,
+ children: [
+ resultColorBox,
+ resultHex,
+ resultRgb,
+ resultHsl,
+ ]
+ })
+ return SidebarModule({
+ icon: MaterialIcon('colorize', 'norm'),
+ name: 'Color picker',
+ revealChild: false,
+ child: Box({
+ className: 'spacing-h-5',
+ children: [
+ hueSelector,
+ saturationAndLightnessSelector,
+ result,
+ ]
+ })
+ });
+}
\ No newline at end of file
diff --git a/.config/ags/widgets/sideleft/tools/module.js b/.config/ags/widgets/sideleft/tools/module.js
new file mode 100644
index 000000000..eae27147e
--- /dev/null
+++ b/.config/ags/widgets/sideleft/tools/module.js
@@ -0,0 +1,56 @@
+import Widget from 'resource:///com/github/Aylur/ags/widget.js';
+import { setupCursorHover } from '../../../lib/cursorhover.js';
+import { MaterialIcon } from '../../../lib/materialicon.js';
+const { Box, Button, Icon, Label, Revealer } = Widget;
+
+export default ({
+ icon,
+ name,
+ child,
+ revealChild = true,
+}) => {
+ const headerButtonIcon = MaterialIcon(revealChild ? 'expand_less' : 'expand_more', 'norm');
+ const header = Button({
+ onClicked: () => {
+ content.revealChild = !content.revealChild;
+ headerButtonIcon.label = content.revealChild ? 'expand_less' : 'expand_more';
+ },
+ setup: setupCursorHover,
+ child: Box({
+ className: 'txt spacing-h-10',
+ children: [
+ icon,
+ Label({
+ className: 'txt-norm',
+ label: `${name}`,
+ }),
+ Box({
+ hexpand: true,
+ }),
+ Box({
+ className: 'sidebar-module-btn-arrow',
+ homogeneous: true,
+ children: [headerButtonIcon],
+ })
+ ]
+ })
+ });
+ const content = Revealer({
+ revealChild: revealChild,
+ transition: 'slide_down',
+ transitionDuration: 200,
+ child: Box({
+ className: 'margin-top-5',
+ homogeneous: true,
+ children: [child],
+ }),
+ });
+ return Box({
+ className: 'sidebar-module',
+ vertical: true,
+ children: [
+ header,
+ content,
+ ]
+ });
+}
\ No newline at end of file
diff --git a/.config/ags/widgets/sideleft/tools/quickscripts.js b/.config/ags/widgets/sideleft/tools/quickscripts.js
new file mode 100644
index 000000000..3ff907dc2
--- /dev/null
+++ b/.config/ags/widgets/sideleft/tools/quickscripts.js
@@ -0,0 +1,95 @@
+const { Gtk } = imports.gi;
+import App from 'resource:///com/github/Aylur/ags/app.js';
+import Widget from 'resource:///com/github/Aylur/ags/widget.js';
+import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
+const { execAsync, exec } = Utils;
+const { Box, Button, EventBox, Icon, Label, Scrollable } = Widget;
+import SidebarModule from './module.js';
+import { MaterialIcon } from '../../../lib/materialicon.js';
+import { setupCursorHover } from '../../../lib/cursorhover.js';
+
+Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets`);
+const distroID = exec(`bash -c 'cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2'`).trim();
+const isDebianDistro = (distroID == 'linuxmint' || distroID == 'ubuntu' || distroID == 'debian' || distroID == 'zorin' || distroID == 'pop' || distroID == 'raspbian' || distroID == 'kali' || distroID == 'elementary');
+const isArchDistro = (distroID == 'arch' || distroID == 'endeavouros');
+const hasFlatpak = !!exec(`bash -c 'command -v flatpak'`);
+
+const scripts = [
+ {
+ icon: 'nixos-symbolic',
+ name: 'Trim system generations to 5',
+ command: `sudo ${App.configDir}/scripts/quickscripts/nixos-trim-generations.sh 5 0 system`,
+ enabled: distroID == 'nixos',
+ },
+ {
+ icon: 'nixos-symbolic',
+ name: 'Trim home manager generations to 5',
+ command: `${App.configDir}/scripts/quickscripts/nixos-trim-generations.sh 5 0 home-manager`,
+ enabled: distroID == 'nixos',
+ },
+ {
+ icon: 'ubuntu-symbolic',
+ name: 'Update packages',
+ command: `sudo apt update && sudo apt upgrade -y`,
+ enabled: isDebianDistro,
+ },
+ {
+ icon: 'fedora-symbolic',
+ name: 'Update packages',
+ command: `sudo dnf upgrade -y`,
+ enabled: distroID == 'fedora',
+ },
+ {
+ icon: 'arch-symbolic',
+ name: 'Update packages',
+ command: `sudo pacman -Syyu`,
+ enabled: isArchDistro,
+ },
+ {
+ icon: 'flatpak-symbolic',
+ name: 'Uninstall unused flatpak packages',
+ command: `flatpak uninstall --unused`,
+ enabled: hasFlatpak,
+ },
+];
+
+export default () => SidebarModule({
+ icon: MaterialIcon('code', 'norm'),
+ name: 'Quick scripts',
+ child: Box({
+ vertical: true,
+ className: 'spacing-v-5',
+ children: scripts.map((script) => {
+ if (!script.enabled) return null;
+ const scriptStateIcon = MaterialIcon('not_started', 'norm');
+ return Box({
+ className: 'spacing-h-5 txt',
+ children: [
+ Icon({
+ className: 'sidebar-module-btn-icon txt-large',
+ icon: script.icon,
+ }),
+ Label({
+ className: 'txt-small',
+ hpack: 'start',
+ hexpand: true,
+ label: script.name,
+ tooltipText: script.command,
+ }),
+ Button({
+ className: 'sidebar-module-scripts-button',
+ child: scriptStateIcon,
+ onClicked: () => {
+ App.closeWindow('sideleft');
+ execAsync([`bash`, `-c`, `foot fish -C "${script.command}"`]).catch(print)
+ .then(() => {
+ scriptStateIcon.label = 'done';
+ })
+ },
+ setup: setupCursorHover,
+ }),
+ ],
+ })
+ }),
+ })
+});
\ No newline at end of file
diff --git a/.config/ags/widgets/sideright/notificationlist.js b/.config/ags/widgets/sideright/notificationlist.js
index f3fa95111..f30802de4 100644
--- a/.config/ags/widgets/sideright/notificationlist.js
+++ b/.config/ags/widgets/sideright/notificationlist.js
@@ -20,7 +20,7 @@ export default (props) => {
vertical: true,
className: 'spacing-v-5',
children: [
- MaterialIcon('notifications_active', 'badonkers'),
+ MaterialIcon('notifications_active', 'gigantic'),
Label({ label: 'No notifications', className: 'txt-small' }),
]
}),
diff --git a/.config/ags/widgets/sideright/quicktoggles.js b/.config/ags/widgets/sideright/quicktoggles.js
index 9ba354ddf..6d55f2611 100644
--- a/.config/ags/widgets/sideright/quicktoggles.js
+++ b/.config/ags/widgets/sideright/quicktoggles.js
@@ -1,4 +1,5 @@
const { GLib } = imports.gi;
+import App from 'resource:///com/github/Aylur/ags/app.js';
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
@@ -151,7 +152,7 @@ export const ModuleIdleInhibitor = (props = {}) => Widget.Button({ // TODO: Make
self.toggleClassName('sidebar-button-active', self.attribute.enabled);
if (self.attribute.enabled) {
self.attribute.inhibitor = Utils.subprocess(
- ['wayland-idle-inhibitor.py'],
+ [`${App.configDir}/scripts/wayland-idle-inhibitor.py`],
(output) => print(output),
(err) => logError(err),
self,
diff --git a/.config/ags/widgets/sideright/todolist.js b/.config/ags/widgets/sideright/todolist.js
index 3efea6ad6..30a7941bf 100644
--- a/.config/ags/widgets/sideright/todolist.js
+++ b/.config/ags/widgets/sideright/todolist.js
@@ -92,7 +92,7 @@ const todoItems = (isDone) => Widget.Scrollable({
vpack: 'center',
className: 'txt',
children: [
- MaterialIcon(`${isDone ? 'checklist' : 'check_circle'}`, 'badonkers'),
+ MaterialIcon(`${isDone ? 'checklist' : 'check_circle'}`, 'gigantic'),
Label({ label: `${isDone ? 'Finished tasks will go here' : 'Nothing here!'}` })
]
})