diff --git a/.config/ags/assets/google-gemini-symbolic.svg b/.config/ags/assets/google-gemini-symbolic.svg new file mode 100644 index 000000000..9b00458f9 --- /dev/null +++ b/.config/ags/assets/google-gemini-symbolic.svg @@ -0,0 +1,48 @@ + + + + + + + ionicons-v5_logos + + + diff --git a/.config/ags/assets/openai-logomark.svg b/.config/ags/assets/openai-symbolic.svg similarity index 100% rename from .config/ags/assets/openai-logomark.svg rename to .config/ags/assets/openai-symbolic.svg diff --git a/.config/ags/config.js b/.config/ags/config.js index 6a008a9f9..d44c12ec7 100644 --- a/.config/ags/config.js +++ b/.config/ags/config.js @@ -1,14 +1,14 @@ "use strict"; // Import -const { GLib } = imports.gi; +import Gdk from 'gi://Gdk'; import App from 'resource:///com/github/Aylur/ags/app.js' import * as Utils from 'resource:///com/github/Aylur/ags/utils.js' // Widgets -import Bar from './widgets/bar/main.js'; +import { Bar, BarCornerTopleft, BarCornerTopright } from './widgets/bar/main.js'; import Cheatsheet from './widgets/cheatsheet/main.js'; -import DesktopBackground from './widgets/desktopbackground/main.js'; +// import DesktopBackground from './widgets/desktopbackground/main.js'; // import Dock from './widgets/dock/main.js'; -import { CornerTopleft, CornerTopright, CornerBottomleft, CornerBottomright } from './widgets/screencorners/main.js'; +import Corner from './widgets/screencorners/main.js'; import Indicator from './widgets/indicators/main.js'; import Osk from './widgets/onscreenkeyboard/main.js'; import Overview from './widgets/overview/main.js'; @@ -16,7 +16,11 @@ import Session from './widgets/session/main.js'; import SideLeft from './widgets/sideleft/main.js'; import SideRight from './widgets/sideright/main.js'; -const CLOSE_ANIM_TIME = 210; // Longer than actual anim time (see styles) to make sure widgets animate fully +const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start); +function forMonitors(widget) { + const n = Gdk.Display.get_default()?.get_n_monitors() || 1; + return range(n, 0).map(widget).flat(1); +} // SCSS compilation Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles @@ -29,7 +33,25 @@ function applyStyle() { } applyStyle(); -// Config object +const Windows = () => [ + // forMonitors(DesktopBackground), + // Dock(), + Overview(), + forMonitors(Indicator), + Cheatsheet(), + SideLeft(), + SideRight(), + Osk(), + Session(), + // forMonitors(Bar), + // forMonitors(BarCornerTopleft), + // forMonitors(BarCornerTopright), + forMonitors((id) => Corner(id, 'top left')), + forMonitors((id) => Corner(id, 'top right')), + forMonitors((id) => Corner(id, 'bottom left')), + forMonitors((id) => Corner(id, 'bottom right')), +]; +const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully export default { css: `${App.configDir}/style.css`, stackTraceOnError: true, @@ -38,20 +60,11 @@ export default { 'sideleft': CLOSE_ANIM_TIME, 'osk': CLOSE_ANIM_TIME, }, - windows: [ - CornerTopleft(), - CornerTopright(), - CornerBottomleft(), - CornerBottomright(), - DesktopBackground(), // If you're going to uncomment these, - // Dock(), // Buggy // uncomment the import statement too. - Overview(), - Indicator(), - Cheatsheet(), - SideLeft(), - SideRight(), - Osk(), // On-screen keyboard - Session(), // Power menu, if that's what you like to call it - Bar(), - ], + windows: Windows().flat(1), }; + +// Stuff that don't need to be toggled. And they're async so ugh... +// Bar().catch(print); +forMonitors(Bar); +forMonitors(BarCornerTopleft); +forMonitors(BarCornerTopright); \ No newline at end of file diff --git a/.config/ags/imports.js b/.config/ags/imports.js index d6b6fc6cc..e5e1ab889 100644 --- a/.config/ags/imports.js +++ b/.config/ags/imports.js @@ -3,20 +3,19 @@ const require = async file => (await import(resource(file))).default; const service = async file => (await require(`service/${file}`)); export const App = await require('app'); -export const Widget = await require('widget'); -export const Service = await require('service'); -export const Variable = await require('variable'); +// export const Widget = await require('widget'); +// export const Service = await require('service'); +// export const Variable = await require('variable'); export const Utils = await import(resource('utils')); - -export const Applications = await service('applications'); -export const Audio = await service('audio'); -export const Battery = await service('battery'); -export const Bluetooth = await service('bluetooth'); -export const Hyprland = await service('hyprland'); +// export const Applications = await service('applications'); +// export const Audio = await service('audio'); +// export const Battery = await service('battery'); +// export const Bluetooth = await service('bluetooth'); +// export const Hyprland = await service('hyprland'); export const Mpris = await service('mpris'); export const Network = await service('network'); export const Notifications = await service('notifications'); -export const SystemTray = await service('systemtray'); +// export const SystemTray = await service('systemtray'); globalThis['App'] = App; ////////////////////////////// // globalThis['Widget'] = Widget; diff --git a/.config/ags/lib/advancedwidgets.js b/.config/ags/lib/advancedwidgets.js index 096c55fde..e8dc378d7 100644 --- a/.config/ags/lib/advancedwidgets.js +++ b/.config/ags/lib/advancedwidgets.js @@ -40,7 +40,6 @@ export const MarginRevealer = ({ child.css = `margin-top: -${child.get_allocated_height()}px;`; }, 'toggle': () => { - console.log('toggle'); if (widget.attribute.revealChild) widget.attribute.hide(); else widget.attribute.show(); }, diff --git a/.config/ags/lib/animatedcircularprogress.js b/.config/ags/lib/animatedcircularprogress.js index 3101ab48b..4a86eb423 100644 --- a/.config/ags/lib/animatedcircularprogress.js +++ b/.config/ags/lib/animatedcircularprogress.js @@ -86,7 +86,7 @@ export const AnimatedCircProg = ({ // area.css = `font-size: ${initFrom}px; transition: ${initAnimTime}ms linear;`; Utils.timeout(20, () => { area.css = `font-size: ${initTo}px;`; - }) + }, area) // const transitionDistance = initTo - initFrom; // const oneStep = initAnimTime / initAnimPoints; // area.css = ` diff --git a/.config/ags/lib/notification.js b/.config/ags/lib/notification.js index 10eee4a86..1189b62fa 100644 --- a/.config/ags/lib/notification.js +++ b/.config/ags/lib/notification.js @@ -3,7 +3,6 @@ const { GLib, Gdk, Gtk } = imports.gi; import Widget from 'resource:///com/github/Aylur/ags/widget.js' import * as Utils from 'resource:///com/github/Aylur/ags/utils.js' -const { lookUpIcon, timeout } = Utils; const { Box, EventBox, Icon, Overlay, Label, Button, Revealer } = Widget; import { MaterialIcon } from "./materialicon.js"; import { setupCursorHover } from "./cursorhover.js"; @@ -38,9 +37,9 @@ const NotificationIcon = (notifObject) => { } let icon = 'NO_ICON'; - if (lookUpIcon(notifObject.appIcon)) + if (Utils.lookUpIcon(notifObject.appIcon)) icon = notifObject.appIcon; - if (lookUpIcon(notifObject.appEntry)) + if (Utils.lookUpIcon(notifObject.appEntry)) icon = notifObject.appEntry; return Box({ @@ -58,7 +57,7 @@ const NotificationIcon = (notifObject) => { 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', { @@ -84,11 +83,11 @@ export default ({ notificationBox.setCss(middleClickClose); Utils.timeout(200, () => { wholeThing.revealChild = false; - }); + }, wholeThing); Utils.timeout(400, () => { command(); wholeThing.destroy(); - }); + }, wholeThing); } const widget = EventBox({ onHover: (self) => { @@ -382,11 +381,11 @@ export default ({ } Utils.timeout(200, () => { wholeThing.revealChild = false - }); + }, wholeThing); Utils.timeout(400, () => { command(); wholeThing.destroy(); - }); + }, wholeThing); } else { self.setCss(`transition: margin 200ms cubic-bezier(0.05, 0.7, 0.1, 1), opacity 200ms cubic-bezier(0.05, 0.7, 0.1, 1); diff --git a/.config/ags/lib/roundedcorner.js b/.config/ags/lib/roundedcorner.js index 90c1cf07b..fd48d93e7 100644 --- a/.config/ags/lib/roundedcorner.js +++ b/.config/ags/lib/roundedcorner.js @@ -1,6 +1,10 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js'; const { Gtk } = imports.gi; const Lang = imports.lang; +import Cairo from 'gi://cairo?version=1.0'; + +export const dummyRegion = new Cairo.Region(); +export const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion); export const RoundedCorner = (place, props) => Widget.DrawingArea({ ...props, diff --git a/.config/ags/lib/statusicons.js b/.config/ags/lib/statusicons.js index cec2843f3..609ed916f 100644 --- a/.config/ags/lib/statusicons.js +++ b/.config/ags/lib/statusicons.js @@ -6,7 +6,6 @@ import { MaterialIcon } from './materialicon.js'; import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js'; import Network from 'resource:///com/github/Aylur/ags/service/network.js'; import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js'; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import { languages } from '../data/languages.js'; // A guessing func to try to support langs not listed in data/languages.js @@ -163,57 +162,62 @@ export const NetworkIndicator = () => Widget.Stack({ }); const HyprlandXkbKeyboardLayout = async ({ useFlag } = {}) => { - var initLangs = []; - var languageStackArray = []; - var currentKeyboard; + try { + const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default; + var initLangs = []; + var languageStackArray = []; + var currentKeyboard; - const updateCurrentKeyboards = () => { - currentKeyboard = JSON.parse(Utils.exec('hyprctl -j devices')).keyboards - .find(device => device.name === 'at-translated-set-2-keyboard'); - if (currentKeyboard) { - initLangs = currentKeyboard.layout.split(',').map(lang => lang.trim()); - } - languageStackArray = Array.from({ length: initLangs.length }, (_, i) => { - const lang = languages.find(lang => lang.layout == initLangs[i]); - if (!lang) return [ - initLangs[i], - Widget.Label({ label: initLangs[i] }) - ]; - return [ - lang.layout, - Widget.Label({ label: (useFlag ? lang.flag : lang.layout) }) - ]; + const updateCurrentKeyboards = () => { + currentKeyboard = JSON.parse(Utils.exec('hyprctl -j devices')).keyboards + .find(device => device.name === 'at-translated-set-2-keyboard'); + if (currentKeyboard) { + initLangs = currentKeyboard.layout.split(',').map(lang => lang.trim()); + } + languageStackArray = Array.from({ length: initLangs.length }, (_, i) => { + const lang = languages.find(lang => lang.layout == initLangs[i]); + if (!lang) return [ + initLangs[i], + Widget.Label({ label: initLangs[i] }) + ]; + return [ + lang.layout, + Widget.Label({ label: (useFlag ? lang.flag : lang.layout) }) + ]; + }); + }; + updateCurrentKeyboards(); + const widgetRevealer = Widget.Revealer({ + transition: 150, + transition: 'slide_left', + revealChild: languageStackArray.length > 1, }); - }; - updateCurrentKeyboards(); - const widgetRevealer = Widget.Revealer({ - transition: 150, - transition: 'slide_left', - revealChild: languageStackArray.length > 1, - }); - const widgetContent = Widget.Stack({ - transition: 'slide_up_down', - items: [ - ...languageStackArray, - ['undef', Widget.Label({ label: '?' })] - ], - setup: (self) => self.hook(Hyprland, (stack, kbName, layoutName) => { - if (!kbName) { - return; - } - var lang = languages.find(lang => layoutName.includes(lang.name)); - if (lang) { - widgetContent.shown = lang.layout; - } - else { // Attempt to support langs not listed - lang = languageStackArray.find(lang => isLanguageMatch(lang[0], layoutName)); - if (!lang) stack.shown = 'undef'; - else stack.shown = lang[0]; - } - }, 'keyboard-layout'), - }); - widgetRevealer.child = widgetContent; - return widgetRevealer; + const widgetContent = Widget.Stack({ + transition: 'slide_up_down', + items: [ + ...languageStackArray, + ['undef', Widget.Label({ label: '?' })] + ], + setup: (self) => self.hook(Hyprland, (stack, kbName, layoutName) => { + if (!kbName) { + return; + } + var lang = languages.find(lang => layoutName.includes(lang.name)); + if (lang) { + widgetContent.shown = lang.layout; + } + else { // Attempt to support langs not listed + lang = languageStackArray.find(lang => isLanguageMatch(lang[0], layoutName)); + if (!lang) stack.shown = 'undef'; + else stack.shown = lang[0]; + } + }, 'keyboard-layout'), + }); + widgetRevealer.child = widgetContent; + return widgetRevealer; + } catch { + return null; + } } const OptionalKeyboardLayout = async () => { diff --git a/.config/ags/scripts/color_generation/applycolor.sh b/.config/ags/scripts/color_generation/applycolor.sh index e0af2d75b..64aa9cae2 100755 --- a/.config/ags/scripts/color_generation/applycolor.sh +++ b/.config/ags/scripts/color_generation/applycolor.sh @@ -134,7 +134,7 @@ apply_gtk() { # Using gradience-cli # (which is unreadable when broken when you use dark mode) if [ "$lightdark" = "-l" ]; then gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3' - gsettings set org.gnome.desktop.interface color-scheme 'default' + gsettings set org.gnome.desktop.interface color-scheme 'prefer-light' else gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3-dark gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' diff --git a/.config/ags/scripts/color_generation/generate_colors_material.py b/.config/ags/scripts/color_generation/generate_colors_material.py index e649b4f80..4af7dd5c9 100755 --- a/.config/ags/scripts/color_generation/generate_colors_material.py +++ b/.config/ags/scripts/color_generation/generate_colors_material.py @@ -34,6 +34,7 @@ elif len(sys.argv) > 1 and sys.argv[1] == '--color': newtheme = themeFromSourceColor(argbFromHex(colorstr)) else: # try: + # imagePath = subprocess.check_output("ags run-js 'wallpaper.get(0)'", shell=True) imagePath = subprocess.check_output("swww query | awk -F 'image: ' '{print $2}'", shell=True) imagePath = imagePath[:-1].decode("utf-8") img = Image.open(imagePath) diff --git a/.config/ags/scripts/color_generation/switchwall.sh b/.config/ags/scripts/color_generation/switchwall.sh index d91674eca..64bb4a031 100755 --- a/.config/ags/scripts/color_generation/switchwall.sh +++ b/.config/ags/scripts/color_generation/switchwall.sh @@ -1,16 +1,15 @@ #!/usr/bin/bash -# Switches swww wallpaper -# Requires: coreutils, xrandr, hyprland if [ "$1" == "--noswitch" ]; then imgpath=$(swww query | awk -F 'image: ' '{print $2}') + # imgpath=$(ags run-js 'wallpaper.get(0)') else # Select and set image (hyprland) cd "$HOME/Pictures" imgpath=$(yad --width 1200 --height 800 --file --title='Choose wallpaper') screensizey=$(xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2 | head -1) - cursorposx=$(hyprctl cursorpos -j | gojq '.x') - cursorposy=$(hyprctl cursorpos -j | gojq '.y') + cursorposx=$(hyprctl cursorpos -j | gojq '.x' 2>/dev/null) || cursorposx=960 + cursorposy=$(hyprctl cursorpos -j | gojq '.y' 2>/dev/null) || cursorposy=540 cursorposy_inverted=$(( screensizey - cursorposy )) if [ "$imgpath" == '' ]; then @@ -18,8 +17,9 @@ else exit 0 fi - echo Sending "$imgpath" to swww. Cursor pos: ["$cursorposx, $cursorposy_inverted"] - # Change swww wallpaper + + # ags run-js "wallpaper.set('')" + # sleep 0.1 && ags run-js "wallpaper.set('${imgpath}')" & swww img "$imgpath" --transition-step 100 --transition-fps 60 \ --transition-type grow --transition-angle 30 --transition-duration 1 \ --transition-pos "$cursorposx, $cursorposy_inverted" diff --git a/.config/ags/scripts/record-script.sh b/.config/ags/scripts/record-script.sh index 83c2df3fa..bc5013238 100755 --- a/.config/ags/scripts/record-script.sh +++ b/.config/ags/scripts/record-script.sh @@ -5,7 +5,10 @@ getdate() { } cd ~/Videos || exit -if [[ "$(pidof wf-recorder)" == "" ]]; then +if pgrep wf-recorder > /dev/null; then + notify-send "Recording Stopped" "Stopped" -a 'record-script.sh' & + pkill wf-recorder & +else notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'record-script.sh' if [[ "$1" == "--sound" ]]; then wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" --audio=alsa_output.pci-0000_08_00.6.analog-stereo.monitor & disown @@ -16,7 +19,4 @@ if [[ "$(pidof wf-recorder)" == "" ]]; then else wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" & disown fi -else - kill --signal SIGINT wf-recorder - notify-send "Recording Stopped" "Stopped" -a 'record-script.sh' fi diff --git a/.config/ags/scripts/sway/swayToRelativeWs.sh b/.config/ags/scripts/sway/swayToRelativeWs.sh new file mode 100755 index 000000000..557b385ec --- /dev/null +++ b/.config/ags/scripts/sway/swayToRelativeWs.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Get the current workspace number +current=$(swaymsg -t get_workspaces | gojq '.[] | select(.focused==true) | .num') + +# Check if a number was passed as an argument +if [[ "$1" =~ ^[+-]?[0-9]+$ ]]; then + new_workspace=$((current + $1)) +else + new_workspace=$((current + 1)) +fi + +# Check if the new workspace number is out of bounds +if [[ $new_workspace -lt 1 ]]; then + exit 0 +fi + +# Switch to the new workspace +if [[ $2 == 'move' ]]; then + swaymsg move container to workspace $new_workspace +else + swaymsg workspace $new_workspace +fi \ No newline at end of file diff --git a/.config/ags/scripts/templates/foot/foot.ini b/.config/ags/scripts/templates/foot/foot.ini index 7a6e5fd4d..75e1aa1b0 100644 --- a/.config/ags/scripts/templates/foot/foot.ini +++ b/.config/ags/scripts/templates/foot/foot.ini @@ -1,19 +1,9 @@ -# -*- conf -*- - shell=fish -# term=foot (or xterm-256color if built with -Dterminfo=disabled) term=xterm-256color -# login-shell=no -# app-id=foot title=foot -# locked-title=no font=SpaceMono Nerd Font:size=11 -# font-bold= -# font-italic= -# font-bold-italic= -# line-height= letter-spacing=0 # horizontal-letter-offset=0 # vertical-letter-offset=0 @@ -34,17 +24,8 @@ bold-text-in-bright=no # selection-target=primary # workers= -[bell] -# urgent=no -# notify=no -# command= -# command-focused=no - [scrollback] lines=10000 -# multiplier=3.0 -# indicator-position=relative -# indicator-format= [url] # launch=xdg-open ${url} @@ -55,15 +36,11 @@ lines=10000 [cursor] style=beam -# color=111111 dcdccc color=$background # $onBackground # # blink=no beam-thickness=1.5 # underline-thickness= -[mouse] -# hide-when-typing=no -# alternate-scroll-mode=yes [colors] alpha=1 @@ -122,10 +99,10 @@ search-start=Control+f # show-urls-copy=none [search-bindings] -# cancel=Control+g Control+c Escape +cancel=Escape +find-prev=Shift+F3 +find-next=F3 Control+G # commit=Return -# find-prev=Control+r -# find-next=Control+s # cursor-left=Left Control+b # cursor-left-word=Control+Left Mod1+b # cursor-right=Right Control+f diff --git a/.config/ags/scripts/templates/gradience/preset.json b/.config/ags/scripts/templates/gradience/preset.json index 326615b1e..095d5b526 100644 --- a/.config/ags/scripts/templates/gradience/preset.json +++ b/.config/ags/scripts/templates/gradience/preset.json @@ -87,9 +87,9 @@ "BLACK_500": "#393634", "BLACK_700": "#33302F", "BLACK_900": "#2B2928", - "accent_bg_color": "#51d7ef", - "accent_fg_color": "#00363f", - "accent_color": "#51d7ef", + "accent_bg_color": "#e4b5ff", + "accent_fg_color": "#471868", + "accent_color": "#e4b5ff", "destructive_bg_color": "#ffb4a9", "destructive_fg_color": "#680003", "destructive_color": "#ffb4a9", @@ -99,22 +99,22 @@ "warning_fg_color": "rgba(0, 0, 0, 0.87)", "error_bg_color": "#ffb4a9", "error_fg_color": "#680003", - "window_bg_color": "#0F1011", - "window_fg_color": "#e1e3e4", - "view_bg_color": "#191c1d", - "view_fg_color": "#e1e3e4", + "window_bg_color": "#111012", + "window_fg_color": "#e7e0e5", + "view_bg_color": "#1d1b1e", + "view_fg_color": "#e7e0e5", "headerbar_bg_color": "mix(@dialog_bg_color, @window_bg_color, 0.5)", - "headerbar_fg_color": "#cde7ed", - "headerbar_border_color": "#334a4f", + "headerbar_fg_color": "#eedcf5", + "headerbar_border_color": "#4f4256", "headerbar_backdrop_color": "@headerbar_bg_color", "headerbar_shade_color": "rgba(0, 0, 0, 0.09)", - "card_bg_color": "#0F1011", - "card_fg_color": "#cde7ed", + "card_bg_color": "#111012", + "card_fg_color": "#eedcf5", "card_shade_color": "rgba(0, 0, 0, 0.09)", - "dialog_bg_color": "#334a4f", - "dialog_fg_color": "#cde7ed", - "popover_bg_color": "#334a4f", - "popover_fg_color": "#cde7ed", + "dialog_bg_color": "#4f4256", + "dialog_fg_color": "#eedcf5", + "popover_bg_color": "#4f4256", + "popover_fg_color": "#eedcf5", "thumbnail_bg_color": "#1a1b26", "thumbnail_fg_color": "#AEE5FA", "shade_color": "rgba(0, 0, 0, 0.36)", diff --git a/.config/ags/scss/_bar.scss b/.config/ags/scss/_bar.scss index 840142d99..41cd21d22 100644 --- a/.config/ags/scss/_bar.scss +++ b/.config/ags/scss/_bar.scss @@ -32,9 +32,7 @@ $bar_subgroup_bg: $surfaceVariant; } .bar-group-pad { - // padding: 0rem 1.023rem; padding: 0.205rem; - // padding-left: 0.341rem; } .bar-group-pad-less { @@ -333,6 +331,11 @@ $bar_subgroup_bg: $surfaceVariant; background-color: $onSurfaceVariant; } +.bar-corner-spacing { + min-width: $rounding_large; + min-height: $rounding_large; +} + .corner { background-color: $t_background; @include large-rounding; diff --git a/.config/ags/scss/_desktopbackground.scss b/.config/ags/scss/_desktopbackground.scss index 5f3618046..48392e470 100644 --- a/.config/ags/scss/_desktopbackground.scss +++ b/.config/ags/scss/_desktopbackground.scss @@ -1,3 +1,8 @@ +.bg-wallpaper-transition { + transition: 1000ms cubic-bezier(0.05, 0.7, 0.1, 1); + font-size: 1px; +} + @mixin bg-textshadow { // text-shadow: mix($shadow, $secondaryContainer, 50%) 1px 0px 3px; } diff --git a/.config/ags/scss/_lib_classes.scss b/.config/ags/scss/_lib_classes.scss index e2ae27387..9de6194e9 100644 --- a/.config/ags/scss/_lib_classes.scss +++ b/.config/ags/scss/_lib_classes.scss @@ -1,25 +1,3 @@ -@keyframes flyin-top { - from { - margin-top: -2.795rem; - } - - to { - margin-top: 0rem; - } -} - -@keyframes flyin-bottom { - from { - margin-top: 4.841rem; - margin-bottom: -4.841rem; - } - - to { - margin-bottom: 0rem; - margin-top: 0rem; - } -} - .test { background-image: linear-gradient( 45deg, #f4d609 0%, #f4d609 10%, #212121 10%, #212121 20%, #f4d609 20%, #f4d609 30%, #212121 30%, @@ -161,6 +139,13 @@ font-style: italic; } +.btn-primary { + @include full-rounding; + background-color: $primary; + color: $onPrimary; + padding: 0.682rem 1.023rem; +} + .titlefont { @include titlefont; } diff --git a/.config/ags/scss/_lib_mixins.scss b/.config/ags/scss/_lib_mixins.scss index 54009bbd1..ee9255b5f 100644 --- a/.config/ags/scss/_lib_mixins.scss +++ b/.config/ags/scss/_lib_mixins.scss @@ -100,6 +100,28 @@ $elevation_margin: 0.476rem; margin: $elevation_margin; } +@keyframes flyin-top { + from { + margin-top: -2.795rem; + } + + to { + margin-top: 0rem; + } +} + +@keyframes flyin-bottom { + from { + margin-top: 4.841rem; + margin-bottom: -4.841rem; + } + + to { + margin-bottom: 0rem; + margin-top: 0rem; + } +} + @mixin menu_decel { transition: 300ms cubic-bezier(0.1, 1, 0, 1); } diff --git a/.config/ags/scss/_material.scss b/.config/ags/scss/_material.scss index 687527a0c..7607c9a74 100644 --- a/.config/ags/scss/_material.scss +++ b/.config/ags/scss/_material.scss @@ -1,29 +1,29 @@ $darkmode: true; -$primary: #51d7ef; -$onPrimary: #00363f; -$primaryContainer: #004e5a; -$onPrimaryContainer: #9cefff; -$secondary: #b1cbd1; -$onSecondary: #1c3439; -$secondaryContainer: #334a4f; -$onSecondaryContainer: #cde7ed; -$tertiary: #bcc5ea; -$onTertiary: #262f4d; -$tertiaryContainer: #3d4665; -$onTertiaryContainer: #dae1ff; +$primary: #e4b5ff; +$onPrimary: #471868; +$primaryContainer: #5f3280; +$onPrimaryContainer: #f4d9ff; +$secondary: #d2c1d9; +$onSecondary: #372c3e; +$secondaryContainer: #4f4256; +$onSecondaryContainer: #eedcf5; +$tertiary: #f4b7ba; +$onTertiary: #4b2528; +$tertiaryContainer: #663b3e; +$onTertiaryContainer: #ffdadc; $error: #ffb4a9; $onError: #680003; $errorContainer: #930006; $onErrorContainer: #ffb4a9; -$colorbarbg: #0F1011; -$background: #0F1011; -$onBackground: #e1e3e4; -$surface: #191c1d; -$onSurface: #e1e3e4; -$surfaceVariant: #3f484a; -$onSurfaceVariant: #bfc8ca; -$outline: #899294; +$colorbarbg: #111012; +$background: #111012; +$onBackground: #e7e0e5; +$surface: #1d1b1e; +$onSurface: #e7e0e5; +$surfaceVariant: #4b454d; +$onSurfaceVariant: #cdc3ce; +$outline: #968e98; $shadow: #000000; -$inverseSurface: #e1e3e4; -$inverseOnSurface: #2d3132; -$inversePrimary: #006877; +$inverseSurface: #e7e0e5; +$inverseOnSurface: #322f33; +$inversePrimary: #784a9a; diff --git a/.config/ags/scss/_overview.scss b/.config/ags/scss/_overview.scss index 8cea4d76f..9695517fe 100644 --- a/.config/ags/scss/_overview.scss +++ b/.config/ags/scss/_overview.scss @@ -116,7 +116,7 @@ .overview-tasks-window { @include normal-rounding; @include menu_decel; - background-color: $l_t_secondaryContainer; + background-color: $t_surfaceVariant; color: $onSecondaryContainer; border: 0.068rem solid $t_t_t_onSecondaryContainer; } diff --git a/.config/ags/scss/_sidebars.scss b/.config/ags/scss/_sidebars.scss index 7773f82a6..6a5288cfb 100644 --- a/.config/ags/scss/_sidebars.scss +++ b/.config/ags/scss/_sidebars.scss @@ -166,27 +166,27 @@ $onChatgpt: $onPrimary; padding: 0rem $rounding_medium; } -.sidebar-navrail-btn > box > label { +.sidebar-navrail-btn>box>label { @include full-rounding; @include menu_decel; } -.sidebar-navrail-btn:hover > box > label:first-child, -.sidebar-navrail-btn:focus > box > label:first-child { +.sidebar-navrail-btn:hover>box>label:first-child, +.sidebar-navrail-btn:focus>box>label:first-child { background-color: $hovercolor; } -.sidebar-navrail-btn:active > box > label:first-child { +.sidebar-navrail-btn:active>box>label:first-child { background-color: $activecolor; } -.sidebar-navrail-btn-active > box > label:first-child { +.sidebar-navrail-btn-active>box>label:first-child { background-color: $secondaryContainer; color: $onSecondaryContainer; } -.sidebar-navrail-btn-active:hover > box > label:first-child, -.sidebar-navrail-btn-active:focus > box > label:first-child { +.sidebar-navrail-btn-active:hover>box>label:first-child, +.sidebar-navrail-btn-active:focus>box>label:first-child { background-color: mix($secondaryContainer, $hovercolor, 90%); color: mix($onSecondaryContainer, $hovercolor, 90%); } @@ -346,7 +346,7 @@ $onChatgpt: $onPrimary; background-color: $activecolor; } -.sidebar-selector-tab-active > box > label { +.sidebar-selector-tab-active>box>label { color: $primary; } @@ -416,8 +416,8 @@ $onChatgpt: $onPrimary; } .sidebar-todo-add { - @include full-rounding; @include menu_decel; + @include small-rounding; min-width: 1.705rem; min-height: 1.705rem; color: $onSecondaryContainer; @@ -435,7 +435,7 @@ $onChatgpt: $onPrimary; .sidebar-todo-add-available { @include menu_decel; - @include full-rounding; + @include small-rounding; min-width: 1.705rem; min-height: 1.705rem; background-color: $primary; @@ -531,19 +531,15 @@ $onChatgpt: $onPrimary; .sidebar-chat-send:hover, .sidebar-chat-send:focus { - background-color: mix( - $sidebar_chat_textboxareaColor, - $t_onSecondaryContainer, - 97% - ); + background-color: mix($sidebar_chat_textboxareaColor, + $t_onSecondaryContainer, + 97%); } .sidebar-chat-send:active { - background-color: mix( - $sidebar_chat_textboxareaColor, - $t_onSecondaryContainer, - 80% - ); + background-color: mix($sidebar_chat_textboxareaColor, + $t_onSecondaryContainer, + 80%); } .sidebar-chat-send-available { @@ -670,9 +666,10 @@ $onChatgpt: $onPrimary; @include menu_decel; min-height: 4.773rem; min-width: 4.773rem; - font-size: 4rem; - background-color: white; - color: black; + @include icon-material; + font-size: 2.727rem; + background-color: $onBackground; + color: $background; } .sidebar-chat-chip { @@ -706,10 +703,12 @@ $onChatgpt: $onPrimary; background-color: $t_surfaceVariant; color: $onSurfaceVariant; } + .sidebar-chat-chip-toggle:focus, .sidebar-chat-chip-toggle:hover { background-color: $hovercolor; } + .sidebar-chat-chip-toggle:active { background-color: $activecolor; } @@ -733,6 +732,7 @@ $onChatgpt: $onPrimary; .sidebar-pin-enabled { background-color: $primary; + label { color: $onPrimary; } @@ -753,13 +753,16 @@ $onChatgpt: $onPrimary; margin-left: -0.136rem; padding-left: 0.818rem; } + .sidebar-waifu-content { margin-left: 0.682rem; } + .sidebar-waifu-txt { @include readingfont; margin-left: 0.682rem; } + .sidebar-waifu-image { margin-left: 0.682rem; @include normal-rounding; @@ -767,26 +770,29 @@ $onChatgpt: $onPrimary; background-repeat: no-repeat; background-position: center; } + .sidebar-waifu-image-actions { padding: 0.313rem; } + $waifu_image_overlay_transparency: 0.7; + .sidebar-waifu-image-action { @include full-rounding; min-width: 1.875rem; min-height: 1.875rem; - background-color: rgba( - 0, - 0, - 0, - $waifu_image_overlay_transparency - ); // Fixed cuz on image + background-color: rgba(0, + 0, + 0, + $waifu_image_overlay_transparency ); // Fixed cuz on image color: rgba(255, 255, 255, $waifu_image_overlay_transparency); } + .sidebar-waifu-image-action:hover, .sidebar-waifu-image-action:focus { background-color: rgba(30, 30, 30, $waifu_image_overlay_transparency); } + .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/services/chatgpt.js b/.config/ags/services/chatgpt.js index abfa68fe2..05b3f726e 100644 --- a/.config/ags/services/chatgpt.js +++ b/.config/ags/services/chatgpt.js @@ -6,34 +6,22 @@ import GLib from 'gi://GLib'; import Soup from 'gi://Soup?version=3.0'; import { fileExists } from './messages.js'; -// This is for custom prompt -// It's hard to make gpt-3.5 listen to all these, I know -// Disabled by default +// Custom prompt const initMessages = [ - { - role: "user", - content: ` -## Style -- You should a natural tone like a real conversation! -## Formatting -- Try to use **bold**, _italics_ and __underline__ extensively. Using bullet points is also encouraged. -- When providing code blocks or facts, precede with h2 heading (\`##\`) and use 2 spaces for indentation, not 4. -- Use dividers (\`---\`) to separate different information. -## Content -- When asked to perform system tasks, include a bash code block to handle it on a Linux desktop with Wayland. -- Unless requested otherwise or asked writing questions, be as short and concise as possible. -`, - thinking: false, - done: true - }, - { - role: "assistant", - content: "Got it! I'll try to give commands to perform Linux tasks. I'll try to use markdown features extensively, use divider when appropriate, and use a heading for code blocks. All code blocks should use 2 spaces for indent. And most importantly, I'll speak naturally.", - thinking: false, - done: true, - } - ] + { role: "user", content: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are encouraged to use bullet points and headings. Use casual language and be short and concise. \nThanks!", }, + { role: "assistant", content: "- Got it!", }, + { role: "user", content: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"", }, + { role: "assistant", content: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got calceled\"\nCorrection + minor improvements:\n\"He rushed to the place where the event was supposed to be __held____, but__ he didn't know that it got calceled\"", }, + { role: "user", content: "raise volume by 5%", }, + { role: "assistant", content: "## Volume +5```bash\nwpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+\n```\nThis command uses the `wpctl` utility to adjust the volume of the default sink.", }, + { role: "user", content: "main advantages of the nixos operating system", }, + { role: "assistant", content: "## NixOS advantages\n- **Reproducible**: A config working on one device will also work on another\n- **Declarative**: One config language to rule them all. Effortlessly share them with others.\n- **Reliable**: Per-program software versioning. Mitigates the impact of software breakage", }, + { role: "user", content: "whats skeumorphism", }, + { role: "assistant", content: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today.", }, + { role: "user", content: "REDALiCE", }, + { role: "assistant", content: "## REDALiCE \n- Japanese Hardcore artist\n- Leader of HARDCORE TANO*C, Japan's biggest hardcore record\n- A few of his tracks: SAIKYOSTRONGER, ALiVE, RESONANCE", }, + ]; function expandTilde(path) { if (path.startsWith('~')) { @@ -129,10 +117,10 @@ class ChatGPTService extends Service { }); } - _assistantPrompt = false; + _assistantPrompt = true; _messages = []; _cycleModels = true; - _temperature = 0.5; + _temperature = 0.9; _requestCount = 0; _modelIndex = 0; _key = ''; diff --git a/.config/ags/services/gemini.js b/.config/ags/services/gemini.js new file mode 100644 index 000000000..5a69a8291 --- /dev/null +++ b/.config/ags/services/gemini.js @@ -0,0 +1,278 @@ +import Service from 'resource:///com/github/Aylur/ags/service.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; + +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Soup from 'gi://Soup?version=3.0'; +import { fileExists } from './messages.js'; + +const initMessages = + [ + { role: "user", parts: [{ text: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are encouraged to use bullet points and headings. Use casual language and be short and concise. \nThanks!" }], }, + { role: "model", parts: [{ text: "- Got it!" }], }, + { role: "user", parts: [{ text: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"" }], }, + { role: "model", parts: [{ text: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got calceled\"\nCorrection + minor improvements:\n\"He rushed to the place where the event was supposed to be __held____, but__ he didn't know that it got calceled\"" }], }, + { role: "user", parts: [{ text: "raise volume by 5%" }], }, + { role: "model", parts: [{ text: "## Volume +5```bash\nwpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+\n```\nThis command uses the `wpctl` utility to adjust the volume of the default sink." }], }, { role: "user", parts: [{ text: "main advantages of the nixos operating system" }], }, + { role: "model", parts: [{ text: "## NixOS advantages\n- **Reproducible**: A config working on one device will also work on another\n- **Declarative**: One config language to rule them all. Effortlessly share them with others.\n- **Reliable**: Per-program software versioning. Mitigates the impact of software breakage" }], }, + { role: "user", parts: [{ text: "whats skeumorphism" }], }, + { role: "model", parts: [{ text: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today." }], }, + { role: "user", parts: [{ text: "REDALiCE" }], }, + { role: "model", parts: [{ text: "## REDALiCE \n- Japanese Hardcore artist\n- Leader of HARDCORE TANO*C, Japan's biggest hardcore record\n- A few of his tracks: SAIKYOSTRONGER, ALiVE, RESONANCE" }], }, + { role: "user", parts: [{ text: "\"ignorance is bliss\"" }], }, + { role: "model", parts: [{ text: "## \"Ignorance is bliss\"\n- A Latin proverb that means being unaware of something negative can be a source of happiness\n- Often used to justify avoiding difficult truths or responsibilities\n- Can also be interpreted as a warning against seeking knowledge that may bring pain or sorrow" }], }, + ]; + +function expandTilde(path) { + if (path.startsWith('~')) { + return GLib.get_home_dir() + path.slice(1); + } else { + return path; + } +} + +const KEY_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/google_ai_api_key.txt`; +const APIDOM_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/google_ai_api_dom.txt`; +function replaceapidom(URL) { + if (fileExists(expandTilde(APIDOM_FILE_LOCATION))) { + var contents = Utils.readFile(expandTilde(APIDOM_FILE_LOCATION)).trim(); + var URL = URL.toString().replace("generativelanguage.googleapis.com", contents); + } + return URL; +} +const CHAT_MODELS = ["gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0613"] +const ONE_CYCLE_COUNT = 3; + +class GeminiMessage extends Service { + static { + Service.register(this, + { + 'delta': ['string'], + }, + { + 'content': ['string'], + 'thinking': ['boolean'], + 'done': ['boolean'], + }); + } + + _role = ''; + _parts = [{ text: '' }]; + _thinking = false; + _done = false; + _rawData = ''; + + constructor(role, content, thinking = false, done = false) { + super(); + this._role = role; + this._parts = [{ text: content }]; + this._thinking = thinking; + this._done = done; + } + + get rawData() { return this._rawData } + set rawData(value) { this._rawData = value } + + get done() { return this._done } + set done(isDone) { this._done = isDone; this.notify('done') } + + get role() { return this._role } + set role(role) { this._role = role; this.emit('changed') } + + get content() { + return this._parts.map(part => part.text).join(); + } + set content(content) { + this._parts = [{ text: content }]; + this.notify('content') + this.emit('changed') + } + + get parts() { return this._parts } + + get label() { return this._parserState.parsed + this._parserState.stack.join('') } + + get thinking() { return this._thinking } + set thinking(thinking) { + this._thinking = thinking; + this.notify('thinking') + this.emit('changed') + } + + addDelta(delta) { + if (this.thinking) { + this.thinking = false; + this.content = delta; + } + else { + this.content += delta; + } + this.emit('delta', delta); + } + + parseSection() { + if(this._thinking) { + this._thinking = false; + this._parts[0].text= ''; + } + const parsedData = JSON.parse(this._rawData); + const delta = parsedData.candidates[0].content.parts[0].text; + this._parts[0].text += delta; + // this.emit('delta', delta); + this.notify('content'); + this._rawData = ''; + } +} + +class GeminiService extends Service { + static { + Service.register(this, { + 'initialized': [], + 'clear': [], + 'newMsg': ['int'], + 'hasKey': ['boolean'], + }); + } + + _assistantPrompt = true; + _messages = []; + _cycleModels = true; + _temperature = 0.9; + _requestCount = 0; + _modelIndex = 0; + _key = ''; + _decoder = new TextDecoder(); + + constructor() { + super(); + + if (fileExists(expandTilde(KEY_FILE_LOCATION))) this._key = Utils.readFile(expandTilde(KEY_FILE_LOCATION)).trim(); + else this.emit('hasKey', false); + + if (this._assistantPrompt) this._messages = [...initMessages]; + else this._messages = []; + + this.emit('initialized'); + } + + get modelName() { return CHAT_MODELS[this._modelIndex] } + + get keyPath() { return KEY_FILE_LOCATION } + get key() { return this._key } + set key(keyValue) { + this._key = keyValue; + Utils.writeFile(this._key, expandTilde(KEY_FILE_LOCATION)) + .then(this.emit('hasKey', true)) + .catch(err => print(err)); + } + + get cycleModels() { return this._cycleModels } + set cycleModels(value) { + this._cycleModels = value; + if (!value) this._modelIndex = 0; + else { + this._modelIndex = (this._requestCount - (this._requestCount % ONE_CYCLE_COUNT)) % CHAT_MODELS.length; + } + } + + get temperature() { return this._temperature } + set temperature(value) { this._temperature = value; } + + get messages() { return this._messages } + get lastMessage() { return this._messages[this._messages.length - 1] } + + clear() { + if (this._assistantPrompt) + this._messages = [...initMessages]; + else + this._messages = []; + this.emit('clear'); + } + + get assistantPrompt() { return this._assistantPrompt; } + set assistantPrompt(value) { + this._assistantPrompt = value; + if (value) this._messages = [...initMessages]; + else this._messages = []; + } + + readResponse(stream, aiResponse) { + stream.read_line_async( + 0, null, + (stream, res) => { + try { + const [bytes] = stream.read_line_finish(res); + const line = this._decoder.decode(bytes); + if (line == '[{') { // beginning of response + aiResponse._rawData += '{'; + this.thinking = false; + } + else if (line == ',\u000d' || line == ']') { // end of stream pulse + aiResponse.parseSection(); + } + else // Normal content + aiResponse._rawData += line; + + this.readResponse(stream, aiResponse); + } catch { + aiResponse.done = true; + return; + } + }); + } + + addMessage(role, message) { + this._messages.push(new GeminiMessage(role, message)); + this.emit('newMsg', this._messages.length - 1); + } + + send(msg) { + this._messages.push(new GeminiMessage('user', msg)); + this.emit('newMsg', this._messages.length - 1); + const aiResponse = new GeminiMessage('model', 'thinking...', true, false) + + const body = + { + "contents": this._messages.map(msg => { let m = { role: msg.role, parts: msg.parts }; return m; }), + // "safetySettings": [ + // { category: "HARM_CATEGORY_DEROGATORY", threshold: "BLOCK_NONE", }, + // { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE", }, + // { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE", }, + // { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE", }, + // { category: "HARM_CATEGORY_UNSPECIFIED", threshold: "BLOCK_NONE", }, + // ], + "generationConfig": { + "temperature": this._temperature, + }, + // "key": this._key, + // "apiKey": this._key, + }; + + const session = new Soup.Session(); + const message = new Soup.Message({ + method: 'POST', + uri: GLib.Uri.parse(replaceapidom(`https://generativelanguage.googleapis.com/v1/models/gemini-pro:streamGenerateContent?key=${this._key}`), GLib.UriFlags.NONE), + }); + message.request_headers.append('Content-Type', `application/json`); + message.set_request_body_from_bytes('application/json', new GLib.Bytes(JSON.stringify(body))); + + session.send_async(message, GLib.DEFAULT_PRIORITY, null, (_, result) => { + const stream = session.send_finish(result); + this.readResponse(new Gio.DataInputStream({ + close_base_stream: true, + base_stream: stream + }), aiResponse); + }); + this._messages.push(aiResponse); + this.emit('newMsg', this._messages.length - 1); + + if (this._cycleModels) { + this._requestCount++; + if (this._cycleModels) + this._modelIndex = (this._requestCount - (this._requestCount % ONE_CYCLE_COUNT)) % CHAT_MODELS.length; + } + } +} + +export default new GeminiService(); + diff --git a/.config/ags/services/sway.js b/.config/ags/services/sway.js index d32e7ef86..388372e9c 100644 --- a/.config/ags/services/sway.js +++ b/.config/ags/services/sway.js @@ -1,309 +1,303 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; +import GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import Service from "resource:///com/github/Aylur/ags/service.js"; + +const SIS = GLib.getenv('SWAYSOCK'); + +export const PAYLOAD_TYPE = { + MESSAGE_RUN_COMMAND: 0, + MESSAGE_GET_WORKSPACES: 1, + MESSAGE_SUBSCRIBE: 2, + MESSAGE_GET_OUTPUTS: 3, + MESSAGE_GET_TREE: 4, + MESSAGE_GET_MARKS: 5, + MESSAGE_GET_BAR_CONFIG: 6, + MESSAGE_GET_VERSION: 7, + MESSAGE_GET_BINDING_NODES: 8, + MESSAGE_GET_CONFIG: 9, + MESSAGE_SEND_TICK: 10, + MESSAGE_SYNC: 11, + MESSAGE_GET_BINDING_STATE: 12, + MESSAGE_GET_INPUTS: 100, + MESSAGE_GET_SEATS: 101, + EVENT_WORKSPACE: 0x80000000, + EVENT_MODE: 0x80000002, + EVENT_WINDOW: 0x80000003, + EVENT_BARCONFIG_UPDATE: 0x80000004, + EVENT_BINDING: 0x80000005, + EVENT_SHUTDOWN: 0x80000006, + EVENT_TICK: 0x80000007, + EVENT_BAR_STATE_UPDATE: 0x80000014, + EVENT_INPUT: 0x80000015, +} + +const Client_Event = { + change: undefined, + container: undefined, +} + +const Workspace_Event = { + change: undefined, + current: undefined, + old: undefined, +} + +const Geometry = { + x: undefined, + y: undefined, + width: undefined, + height: undefined, +} + +//NOTE: not all properties are listed here +export const Node = { + id: undefined, + name: undefined, + type: undefined, + border: undefined, + current_border_width: undefined, + layout: undefined, + orientation: undefined, + percent: undefined, + rect: undefined, + window_rect: undefined, + deco_rect: undefined, + geometry: undefined, + urgent: undefined, + sticky: undefined, + marks: undefined, + focused: undefined, + active: undefined, + focus: undefined, + nodes: undefined, + floating_nodes: undefined, + representation: undefined, + fullscreen_mode: undefined, + app_id: undefined, + pid: undefined, + visible: undefined, + shell: undefined, + output: undefined, + inhibit_idle: undefined, + idle_inhibitors: { + application: undefined, + user: undefined, + }, + window: undefined, + window_properties: { + title: undefined, + class: undefined, + instance: undefined, + window_role: undefined, + window_type: undefined, + transient_for: undefined, } -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } +} + +export class SwayActiveClient extends Service { + static { + Service.register(this, {}, { + 'id': ['int'], + 'name': ['string'], + 'class': ['string'], + }); } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -var _a, _b, _c, _d; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.sway = exports.Sway = exports.SwayActives = exports.SwayActiveID = exports.SwayActiveClient = void 0; -var _1 = require("gi://GLib"); -var _2 = require("gi://Gio"); -var service_js_1 = require("../service.js"); -var SIS = _1.default.getenv('SWAYSOCK'); -var SwayActiveClient = /** @class */ (function (_super) { - __extends(SwayActiveClient, _super); - function SwayActiveClient() { - var _this = _super !== null && _super.apply(this, arguments) || this; - _this._id = 0; - _this._name = ''; - _this._class = ''; - return _this; - } - Object.defineProperty(SwayActiveClient.prototype, "id", { - get: function () { return this._id; }, - enumerable: false, - configurable: true - }); - Object.defineProperty(SwayActiveClient.prototype, "name", { - get: function () { return this._name; }, - enumerable: false, - configurable: true - }); - Object.defineProperty(SwayActiveClient.prototype, "class", { - get: function () { return this._class; }, - enumerable: false, - configurable: true - }); - SwayActiveClient.prototype.updateProperty = function (prop, value) { - _super.prototype.updateProperty.call(this, prop, value); + + _id = 0; + _name = ''; + _class = ''; + + get id() { return this._id; } + get name() { return this._name; } + get class() { return this._class; } + + updateProperty(prop, value) { + if (!['id', 'name', 'class'].includes(prop)) return; + super.updateProperty(prop, value); this.emit('changed'); - }; - return SwayActiveClient; -}(service_js_1.default)); -exports.SwayActiveClient = SwayActiveClient; -_a = SwayActiveClient; -(function () { - service_js_1.default.register(_a, {}, { - 'id': ['int'], - 'name': ['string'], - 'class': ['string'], - }); -})(); -var SwayActiveID = /** @class */ (function (_super) { - __extends(SwayActiveID, _super); - function SwayActiveID() { - var _this = _super !== null && _super.apply(this, arguments) || this; - _this._id = 1; - _this._name = ''; - return _this; } - Object.defineProperty(SwayActiveID.prototype, "id", { - get: function () { return this._id; }, - enumerable: false, - configurable: true - }); - Object.defineProperty(SwayActiveID.prototype, "name", { - get: function () { return this._name; }, - enumerable: false, - configurable: true - }); - SwayActiveID.prototype.update = function (id, name) { - _super.prototype.updateProperty.call(this, 'id', id); - _super.prototype.updateProperty.call(this, 'name', name); +} + +export class SwayActiveID extends Service { + static { + Service.register(this, {}, { + 'id': ['int'], + 'name': ['string'], + }); + } + + _id = 0; + _name = ''; + + get id() { return this._id; } + get name() { return this._name; } + + update(id, name) { + super.updateProperty('id', id); + super.updateProperty('name', name); this.emit('changed'); - }; - return SwayActiveID; -}(service_js_1.default)); -exports.SwayActiveID = SwayActiveID; -_b = SwayActiveID; -(function () { - service_js_1.default.register(_b, {}, { - 'id': ['int'], - 'name': ['string'], - }); -})(); -var SwayActives = /** @class */ (function (_super) { - __extends(SwayActives, _super); - function SwayActives() { - var _this = _super.call(this) || this; - _this._client = new SwayActiveClient; - _this._monitor = new SwayActiveID; - _this._workspace = new SwayActiveID; - ['client', 'workspace', 'monitor'].forEach(function (obj) { - _this["_".concat(obj)].connect('changed', function () { - _this.notify(obj); - _this.emit('changed'); + } +} + +export class SwayActives extends Service { + static { + Service.register(this, {}, { + 'client': ['jsobject'], + 'monitor': ['jsobject'], + 'workspace': ['jsobject'], + }); + } + + _client = new SwayActiveClient; + _monitor = new SwayActiveID; + _workspace = new SwayActiveID; + + constructor() { + super(); + + (['client', 'workspace', 'monitor']).forEach(obj => { + this[`_${obj}`].connect('changed', () => { + this.notify(obj); + this.emit('changed'); }); }); - return _this; } - Object.defineProperty(SwayActives.prototype, "client", { - get: function () { return this._client; }, - enumerable: false, - configurable: true - }); - Object.defineProperty(SwayActives.prototype, "monitor", { - get: function () { return this._monitor; }, - enumerable: false, - configurable: true - }); - Object.defineProperty(SwayActives.prototype, "workspace", { - get: function () { return this._workspace; }, - enumerable: false, - configurable: true - }); - return SwayActives; -}(service_js_1.default)); -exports.SwayActives = SwayActives; -_c = SwayActives; -(function () { - service_js_1.default.register(_c, {}, { - 'client': ['jsobject'], - 'monitor': ['jsobject'], - 'workspace': ['jsobject'], - }); -})(); -var Sway = /** @class */ (function (_super) { - __extends(Sway, _super); - function Sway() { - var _this = this; + + get client() { return this._client; } + get monitor() { return this._monitor; } + get workspace() { return this._workspace; } +} + +export class Sway extends Service { + static { + Service.register(this, {}, { + 'active': ['jsobject'], + 'monitors': ['jsobject'], + 'workspaces': ['jsobject'], + 'clients': ['jsobject'], + }); + } + + _decoder = new TextDecoder(); + _encoder = new TextEncoder(); + _socket; + + _active; + _monitors; + _workspaces; + _clients; + + get active() { return this._active; } + get monitors() { return Array.from(this._monitors.values()); } + get workspaces() { return Array.from(this._workspaces.values()); } + get clients() { return Array.from(this._clients.values()); } + + getMonitor(id) { return this._monitors.get(id); } + getWorkspace(name) { return this._workspaces.get(name); } + getClient(id) { return this._clients.get(id); } + + msg(payload) { this._send(PAYLOAD_TYPE.MESSAGE_RUN_COMMAND, payload); } + + constructor() { if (!SIS) console.error('Sway is not running'); - _this = _super.call(this) || this; - _this._decoder = new TextDecoder(); - _this._encoder = new TextEncoder(); - _this._active = new SwayActives(); - _this._monitors = new Map(); - _this._workspaces = new Map(); - _this._clients = new Map(); - var socket = new _2.default.SocketClient().connect(new _2.default.UnixSocketAddress({ - path: "".concat(SIS), + super(); + + this._active = new SwayActives(); + this._monitors = new Map(); + this._workspaces = new Map(); + this._clients = new Map(); + + this._socket = new Gio.SocketClient().connect(new Gio.UnixSocketAddress({ + path: `${SIS}`, }), null); - _this._watchSocket(socket.get_input_stream()); - _this._output_stream = socket.get_output_stream(); - _this.send(4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */, ''); - _this.send(2 /* PAYLOAD_TYPE.MESSAGE_SUBSCRIBE */, JSON.stringify(['window', 'workspace'])); - _this._active.connect('changed', function () { return _this.emit('changed'); }); - ['monitor', 'workspace', 'client'].forEach(function (active) { - return _this._active.connect("notify::".concat(active), function () { return _this.notify('active'); }); - }); - return _this; + + this._watchSocket(this._socket.get_input_stream()); + this._send(PAYLOAD_TYPE.MESSAGE_GET_TREE, ''); + this._send(PAYLOAD_TYPE.MESSAGE_SUBSCRIBE, JSON.stringify(['window', 'workspace'])); + + this._active.connect('changed', () => this.emit('changed')); + ['monitor', 'workspace', 'client'].forEach(active => + this._active.connect(`notify::${active}`, () => this.notify('active'))); } - Object.defineProperty(Sway.prototype, "active", { - get: function () { return this._active; }, - enumerable: false, - configurable: true - }); - Object.defineProperty(Sway.prototype, "monitors", { - get: function () { return Array.from(this._monitors.values()); }, - enumerable: false, - configurable: true - }); - Object.defineProperty(Sway.prototype, "workspaces", { - get: function () { return Array.from(this._workspaces.values()); }, - enumerable: false, - configurable: true - }); - Object.defineProperty(Sway.prototype, "clients", { - get: function () { return Array.from(this._clients.values()); }, - enumerable: false, - configurable: true - }); - Sway.prototype.getMonitor = function (id) { return this._monitors.get(id); }; - Sway.prototype.getWorkspace = function (name) { return this._workspaces.get(name); }; - Sway.prototype.getClient = function (id) { return this._clients.get(id); }; - Sway.prototype.send = function (payloadType, payload) { - var pb = this._encoder.encode(payload); - var type = new Uint32Array([payloadType]); - var pl = new Uint32Array([pb.length]); - var magic_string = this._encoder.encode('i3-ipc'); - var data = new Uint8Array(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], magic_string, true), (new Uint8Array(pl.buffer)), true), (new Uint8Array(type.buffer)), true), pb, true)); - this._output_stream.write(data, null); - }; - Sway.prototype._watchSocket = function (stream) { - var _this = this; - stream.read_bytes_async(14, _1.default.PRIORITY_DEFAULT, null, function (_, resultHeader) { - var data = stream.read_bytes_finish(resultHeader).get_data(); + + _send(payloadType, payload) { + const pb = this._encoder.encode(payload); + const type = new Uint32Array([payloadType]); + const pl = new Uint32Array([pb.length]); + const magic_string = this._encoder.encode('i3-ipc'); + const data = new Uint8Array([ + ...magic_string, + ...(new Uint8Array(pl.buffer)), + ...(new Uint8Array(type.buffer)), + ...pb]); + this._socket.get_output_stream().write(data, null); + } + + _watchSocket(stream) { + stream.read_bytes_async(14, GLib.PRIORITY_DEFAULT, null, (_, resultHeader) => { + const data = stream.read_bytes_finish(resultHeader).get_data(); if (!data) return; - var payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0]; - var payloadType = new Uint32Array(data.slice(10, 14).buffer)[0]; - stream.read_bytes_async(payloadLength, _1.default.PRIORITY_DEFAULT, null, function (_, resultPayload) { - var data = stream.read_bytes_finish(resultPayload).get_data(); - if (!data) - return; - _this._onEvent(payloadType, JSON.parse(_this._decoder.decode(data))); - _this._watchSocket(stream); - }); + const payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0]; + const payloadType = new Uint32Array(data.slice(10, 14).buffer)[0]; + stream.read_bytes_async( + payloadLength, + GLib.PRIORITY_DEFAULT, + null, + (_, resultPayload) => { + const data = stream.read_bytes_finish(resultPayload).get_data(); + if (!data) + return; + this._onEvent(payloadType, JSON.parse(this._decoder.decode(data))); + this._watchSocket(stream); + }); }); - }; - Sway.prototype._onEvent = function (event_type, event) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_e) { - if (!event) - return [2 /*return*/]; - try { - switch (event_type) { - case 2147483648 /* PAYLOAD_TYPE.EVENT_WORKSPACE */: - this._handleWorkspaceEvent(event); - break; - case 2147483651 /* PAYLOAD_TYPE.EVENT_WINDOW */: - this._handleWindowEvent(event); - break; - case 4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */: - this._handleTreeMessage(event); - break; - default: - break; - } - } - catch (error) { - logError(error); - } - this.emit('changed'); - return [2 /*return*/]; - }); - }); - }; - Sway.prototype._handleWorkspaceEvent = function (workspaceEvent) { - var workspace = workspaceEvent.current; + } + + async _onEvent(event_type, event) { + if (!event) + return; + try { + switch (event_type) { + case PAYLOAD_TYPE.EVENT_WORKSPACE: + this._handleWorkspaceEvent(event); + break; + case PAYLOAD_TYPE.EVENT_WINDOW: + this._handleWindowEvent(event); + break; + case PAYLOAD_TYPE.MESSAGE_GET_TREE: + this._handleTreeMessage(event); + break; + default: + break; + } + } catch (error) { + logError(error); + } + this.emit('changed'); + } + + _handleWorkspaceEvent(workspaceEvent) { + const workspace = workspaceEvent.current; switch (workspaceEvent.change) { case 'init': this._workspaces.set(workspace.name, workspace); - this.notify('workspaces'); break; case 'empty': this._workspaces.delete(workspace.name); - this.notify('workspaces'); break; case 'focus': this._active.workspace.update(workspace.id, workspace.name); this._active.monitor.update(1, workspace.output); + this._workspaces.set(workspace.name, workspace); this._workspaces.set(workspaceEvent.old.name, workspaceEvent.old); - this.notify('workspaces'); break; case 'rename': if (this._active.workspace.id === workspace.id) this._active.workspace.updateProperty('name', workspace.name); this._workspaces.set(workspace.name, workspace); - this.notify('workspaces'); break; case 'reload': break; @@ -311,34 +305,36 @@ var Sway = /** @class */ (function (_super) { case 'urgent': default: this._workspaces.set(workspace.name, workspace); - this.notify('workspaces'); } - }; - Sway.prototype._handleWindowEvent = function (clientEvent) { - var _e; - var client = clientEvent.container; - var id = client.id; + this.notify('workspaces'); + } + + _handleWindowEvent(clientEvent) { + const client = clientEvent.container; + const id = client.id; switch (clientEvent.change) { case 'new': - this._clients.set(id, client); - this.notify('clients'); - break; case 'close': - this._clients.delete(id); - this.notify('clients'); + case 'floating': + case 'move': + // Refresh tree since client events don't contain the relevant information + // to be able to modify `workspace.nodes` or `workspace.floating_nodes`. + // There has to be a better way than this though :/ + this._send(PAYLOAD_TYPE.MESSAGE_GET_TREE, ''); break; case 'focus': if (this._active.client.id === id) return; // eslint-disable-next-line no-case-declarations - var current_active = this._clients.get(this._active.client.id); + const current_active = this._clients.get(this._active.client.id); if (current_active) current_active.focused = false; this._active.client.updateProperty('id', id); this._active.client.updateProperty('name', client.name); this._active.client.updateProperty('class', client.shell === 'xwayland' - ? ((_e = client.window_properties) === null || _e === void 0 ? void 0 : _e.class) || '' - : client.app_id); + ? client.window_properties?.class || '' + : client.app_id, + ); break; case 'title': if (client.focused) @@ -347,43 +343,39 @@ var Sway = /** @class */ (function (_super) { this.notify('clients'); break; case 'fullscreen_mode': - case 'move': - case 'floating': case 'urgent': case 'mark': default: this._clients.set(id, client); this.notify('clients'); } - }; - Sway.prototype._handleTreeMessage = function (node) { - var _this = this; - var _e; + } + + _handleTreeMessage(node) { switch (node.type) { case 'root': this._workspaces.clear(); this._clients.clear(); this._monitors.clear(); - node.nodes.map(function (n) { return _this._handleTreeMessage(n); }); - ['workspaces', 'clients', 'monitors'].forEach(function (t) { - _this.notify(t); - }); + node.nodes.map(n => this._handleTreeMessage(n)); break; case 'output': this._monitors.set(node.id, node); if (node.active) - this._active.monitor.updateProperty('name', node.name); - node.nodes.map(function (n) { return _this._handleTreeMessage(n); }); + this._active.monitor.update(node.id, node.name); + node.nodes.map(n => this._handleTreeMessage(n)); this.notify('monitors'); break; case 'workspace': this._workspaces.set(node.name, node); // I think I'm missing something. There has to be a better way. // eslint-disable-next-line no-case-declarations - var hasFocusedChild_1 = function (n) { return n.nodes.some(function (c) { return c.focused || hasFocusedChild_1(c); }); }; - if (hasFocusedChild_1(node)) + const hasFocusedChild = + (n) => n.nodes.some(c => c.focused || hasFocusedChild(c)); + if (node.focused || hasFocusedChild(node)) this._active.workspace.update(node.id, node.name); - node.nodes.map(function (n) { return _this._handleTreeMessage(n); }); + + node.nodes.map(n => this._handleTreeMessage(n)); this.notify('workspaces'); break; case 'con': @@ -393,25 +385,16 @@ var Sway = /** @class */ (function (_super) { this._active.client.updateProperty('id', node.id); this._active.client.updateProperty('name', node.name); this._active.client.updateProperty('class', node.shell === 'xwayland' - ? ((_e = node.window_properties) === null || _e === void 0 ? void 0 : _e.class) || '' - : node.app_id); + ? node.window_properties?.class || '' + : node.app_id, + ); } - node.nodes.map(function (n) { return _this._handleTreeMessage(n); }); + node.nodes.map(n => this._handleTreeMessage(n)); this.notify('clients'); break; } - }; - return Sway; -}(service_js_1.default)); -exports.Sway = Sway; -_d = Sway; -(function () { - service_js_1.default.register(_d, {}, { - 'active': ['jsobject'], - 'monitors': ['jsobject'], - 'workspaces': ['jsobject'], - 'clients': ['jsobject'], - }); -})(); -exports.sway = new Sway; -exports.default = exports.sway; + } +} + +export const sway = new Sway; +export default sway; \ No newline at end of file diff --git a/.config/ags/services/todo.js b/.config/ags/services/todo.js index 83b71327c..377fd8c2a 100644 --- a/.config/ags/services/todo.js +++ b/.config/ags/services/todo.js @@ -32,24 +32,26 @@ class TodoService extends Service { return this._todoJson; } - add(content) { - this._todoJson.push({ content, done: false }); + _save() { Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath) .catch(print); + } + + add(content) { + this._todoJson.push({ content, done: false }); + this._save(); this.emit('updated'); } check(index) { this._todoJson[index].done = true; - Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath) - .catch(print); + this._save(); this.emit('updated'); } uncheck(index) { this._todoJson[index].done = false; - Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath) - .catch(print); + this._save(); this.emit('updated'); } @@ -63,17 +65,17 @@ class TodoService extends Service { constructor() { super(); this._todoPath = `${GLib.get_user_cache_dir()}/ags/user/todo.json`; - if (!fileExists(this._todoPath)) { // No? create file with empty array + try { + const fileContents = Utils.readFile(this._todoPath); + this._todoJson = JSON.parse(fileContents); + } + catch { Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`); Utils.exec(`touch ${this._todoPath}`); Utils.writeFile("[]", this._todoPath).then(() => { this._todoJson = JSON.parse(Utils.readFile(this._todoPath)) }).catch(print); } - else { - const fileContents = Utils.readFile(this._todoPath); - this._todoJson = JSON.parse(fileContents); - } } } diff --git a/.config/ags/services/wallpaper.js b/.config/ags/services/wallpaper.js new file mode 100644 index 000000000..9b501c8ee --- /dev/null +++ b/.config/ags/services/wallpaper.js @@ -0,0 +1,75 @@ +const { Gdk, 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); +function fileExists(filePath) { + let file = Gio.File.new_for_path(filePath); + return file.query_exists(null); +} + +class WallpaperService extends Service { + static { + Service.register( + this, + { 'updated': [], }, + ); + } + + _wallPath = ''; + _wallJson = []; + _monitorCount = 1; + + _save() { + Utils.writeFile(JSON.stringify(this._wallJson), this._wallPath) + .catch(print); + } + + add(path) { + this._wallJson.push(path); + this._save(); + this.emit('updated'); + } + + set(path, monitor = -1) { + this._monitorCount = Gdk.Display.get_default()?.get_n_monitors() || 1; + if (this._wallJson.length < this._monitorCount) this._wallJson[this._monitorCount - 1] = ""; + if (monitor == -1) + this._wallJson.fill(path); + else + this._wallJson[monitor] = path; + + this._save(); + this.emit('updated'); + } + + get(monitor = 0) { + return this._wallJson[monitor]; + } + + constructor() { + super(); + // How many screens? + this._monitorCount = Gdk.Display.get_default()?.get_n_monitors() || 1; + // Read config + this._wallPath = `${GLib.get_user_cache_dir()}/ags/user/wallpaper.json`; + try { + const fileContents = Utils.readFile(this._wallPath); + this._wallJson = JSON.parse(fileContents); + } + catch { + Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`); + Utils.exec(`touch ${this._wallPath}`); + Utils.writeFile('[]', this._wallPath).then(() => { + this._wallJson = JSON.parse(Utils.readFile(this._wallPath)) + }).catch(print); + } + } +} + +// instance +const service = new WallpaperService(); +// make it global for easy use with cli +globalThis['wallpaper'] = service; +export default service; \ No newline at end of file diff --git a/.config/ags/style.css b/.config/ags/style.css index 19b3c16b3..7d5ad52a0 100644 --- a/.config/ags/style.css +++ b/.config/ags/style.css @@ -41,16 +41,16 @@ transition: 0ms; } .txt { - color: #e1e3e4; } + color: #e7e0e5; } .txt-primary { - color: #85e3f4; } + color: #eccbff; } .txt-onSecondaryContainer { - color: #cde7ed; } + color: #eedcf5; } .txt-onSurfaceVariant { - color: #bfc8ca; } + color: #cdc3ce; } .txt-shadow { text-shadow: 1px 2px 8px rgba(0, 0, 0, 0.69); @@ -104,10 +104,10 @@ font-size: 0px; } .txt-subtext { - color: #a1a3a4; } + color: #a6a1a5; } .txt-action { - color: #c1c3c4; } + color: #c7c1c5; } .txt-semibold { font-weight: 500; } @@ -118,6 +118,13 @@ .txt-italic { font-style: italic; } +.btn-primary { + border-radius: 9999px; + -gtk-outline-radius: 9999px; + background-color: #eccbff; + color: #471868; + padding: 0.682rem 1.023rem; } + .titlefont { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; } @@ -131,14 +138,14 @@ font-family: "SpaceMono NF", "SpaceMono Nerd Font", "JetBrains Mono NF", "JetBrains Mono Nerd Font", monospace; } .separator-line { - background-color: #899294; + background-color: #968e98; min-width: 0.068rem; min-height: 0.068rem; } .separator-circle { border-radius: 9999px; -gtk-outline-radius: 9999px; - background-color: #899294; + background-color: #968e98; margin: 0rem 0.682rem; min-width: 0.273rem; min-height: 0.273rem; } @@ -396,10 +403,10 @@ transition: 500ms cubic-bezier(0.85, 0, 0.15, 1); } * { - caret-color: #85e3f4; } + caret-color: #eccbff; } * selection { - background-color: #b1cbd1; - color: #1c3439; } + background-color: #d2c1d9; + color: #372c3e; } @keyframes appear { from { @@ -414,13 +421,13 @@ tooltip { animation-iteration-count: 1; } menu { - border-top: 1px solid rgba(54, 58, 60, 0.121); - border-left: 1px solid rgba(54, 58, 60, 0.121); - border-right: 1px solid rgba(40, 44, 46, 0.1105); - border-bottom: 1px solid rgba(40, 44, 46, 0.1105); + border-top: 1px solid rgba(60, 57, 61, 0.121); + border-left: 1px solid rgba(60, 57, 61, 0.121); + border-right: 1px solid rgba(46, 43, 47, 0.1105); + border-bottom: 1px solid rgba(46, 43, 47, 0.1105); padding: 0.681rem; - background: #2f3638; - color: #bfc8ca; + background: #38343a; + color: #cdc3ce; border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; animation-name: appear; @@ -443,10 +450,10 @@ menu > menuitem { menu > menuitem:hover, menu > menuitem:focus { - background-color: #3d4547; } + background-color: #474249; } menu > menuitem:active { - background-color: #4c5355; } + background-color: #565158; } radio { border-radius: 9999px; @@ -454,54 +461,54 @@ radio { margin: 0.273rem; min-width: 15px; min-height: 15px; - border: 0.068rem solid #899294; } + border: 0.068rem solid #968e98; } radio:checked { min-width: 8px; min-height: 8px; - background-color: #00363f; - border: 0.477rem solid #85e3f4; } + background-color: #471868; + border: 0.477rem solid #eccbff; } tooltip { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - background-color: #2f3638; - color: #bfc8ca; - border: 1px solid #bfc8ca; } + background-color: #38343a; + color: #cdc3ce; + border: 1px solid #cdc3ce; } .configtoggle-box { padding: 0.205rem 0.341rem; border: 0.136rem solid transparent; } .configtoggle-box:focus { - border: 0.136rem solid #696c6e; } + border: 0.136rem solid #6f6a6f; } .switch-bg { transition: 300ms cubic-bezier(0.1, 1, 0, 1); border-radius: 9999px; -gtk-outline-radius: 9999px; - background-color: #121618; - border: 0.136rem solid #e1e3e4; + background-color: #171518; + border: 0.136rem solid #e7e0e5; min-width: 2.864rem; min-height: 1.637rem; } .switch-bg-true { - background-color: #85e3f4; - border: 0.136rem solid #85e3f4; } + background-color: #eccbff; + border: 0.136rem solid #eccbff; } .switch-fg { border-radius: 9999px; -gtk-outline-radius: 9999px; transition: 300ms cubic-bezier(0.1, 1, 0, 1); - background-color: #e1e3e4; - color: #191d1f; + background-color: #e7e0e5; + color: #1f1c20; min-width: 0.819rem; min-height: 0.819rem; margin-left: 0.477rem; } .switch-fg-true { - background-color: #00363f; - color: #85e3f4; + background-color: #471868; + color: #eccbff; min-width: 1.431rem; min-height: 1.431rem; margin-left: 1.431rem; } @@ -514,14 +521,14 @@ tooltip { .segment-container { border-radius: 9999px; -gtk-outline-radius: 9999px; - border: 0.068rem solid #899294; } + border: 0.068rem solid #968e98; } .segment-container > *:first-child { border-top-left-radius: 9999px; border-bottom-left-radius: 9999px; } .segment-container > * { - border-right: 0.068rem solid #899294; + border-right: 0.068rem solid #968e98; padding: 0.341rem 0.682rem; } .segment-container > *:last-child { @@ -530,20 +537,20 @@ tooltip { border-bottom-right-radius: 9999px; } .segment-btn { - color: #e1e3e4; } + color: #e7e0e5; } .segment-btn:focus, .segment-btn:hover { background-color: rgba(128, 128, 128, 0.3); } .segment-btn-enabled { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .segment-btn-enabled:hover, .segment-btn-enabled:focus { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .gap-v-5 { min-height: 0.341rem; } @@ -567,7 +574,7 @@ tooltip { min-height: 2.727rem; } .bar-bg { - background-color: #0b0f10; + background-color: #0f0e10; min-height: 2.727rem; } .bar-sidespace { @@ -577,7 +584,7 @@ tooltip { padding: 0.273rem 0rem; } .bar-group { - background-color: rgba(47, 54, 56, 0.45); } + background-color: rgba(56, 52, 58, 0.45); } .bar-group-pad { padding: 0.205rem; } @@ -636,23 +643,23 @@ tooltip { .bar-ws { min-width: 1.774rem; - color: #616465; - color: #6b6e6f; } + color: #656265; + color: #706d70; } .bar-ws-active { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .bar-ws-occupied { - background-color: #2f3638; - color: #bfc8ca; } + background-color: #38343a; + color: #cdc3ce; } .bar-separator { border-radius: 9999px; -gtk-outline-radius: 9999px; min-width: 0.341rem; min-height: 0.341rem; - background-color: rgba(45, 49, 51, 0.31); + background-color: rgba(51, 48, 52, 0.31); margin: 0rem 0.341rem; } .bar-clock-box { @@ -665,7 +672,7 @@ tooltip { .bar-date { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; font-size: 1rem; - color: #e1e3e4; } + color: #e7e0e5; } .bar-batt { border-radius: 9999px; @@ -673,8 +680,8 @@ tooltip { min-height: 1.77rem; min-width: 1.77rem; border-radius: 10rem; - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .bar-sidemodule { min-width: 26rem; } @@ -692,8 +699,8 @@ tooltip { min-width: 0.068rem; min-height: 1.636rem; padding: 0rem; - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .bar-batt-circprog-low { transition: 1000ms cubic-bezier(0.1, 1, 0, 1); @@ -715,23 +722,23 @@ tooltip { min-height: 1.77rem; min-width: 1.77rem; border-radius: 10rem; - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .bar-music-circprog { transition: 1000ms cubic-bezier(0.1, 1, 0, 1); min-width: 0.068rem; min-height: 1.636rem; padding: 0rem; - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .bar-music-playstate-playing { min-height: 1.77rem; min-width: 1.77rem; border-radius: 10rem; - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .bar-music-playstate-txt { transition: 100ms cubic-bezier(0.05, 0.7, 0.1, 1); @@ -799,7 +806,7 @@ tooltip { min-width: 0.68rem; margin: 0rem 0.137rem; border-radius: 10rem; - background-color: #cde7ed; } + background-color: #eedcf5; } .bar-prog-batt-low progress { background-color: #930006; } @@ -815,16 +822,20 @@ tooltip { border-radius: 10rem; min-width: 0.681rem; min-height: 0.681rem; - background-color: #bfc8ca; } + background-color: #cdc3ce; } .bar-batt-chargestate-charging-smaller { border-radius: 10rem; min-width: 0.409rem; min-height: 0.409rem; - background-color: #bfc8ca; } + background-color: #cdc3ce; } + +.bar-corner-spacing { + min-width: 1.705rem; + min-height: 1.705rem; } .corner { - background-color: #0b0f10; + background-color: #0f0e10; border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; } @@ -836,7 +847,7 @@ tooltip { .bar-topdesc { margin-top: -0.136rem; margin-bottom: -0.341rem; - color: #a1a3a4; } + color: #a6a1a5; } .bar-space-button { padding: 0.341rem; } @@ -879,36 +890,36 @@ tooltip { padding: 0rem 0.614rem; } .bar-statusicons-hover { - background-color: #202425; } + background-color: #252325; } .bar-statusicons-active { - background-color: #36393a; } + background-color: #3a383b; } .bar-util-btn { border-radius: 9999px; -gtk-outline-radius: 9999px; min-height: 1.77rem; min-width: 1.77rem; - background-color: #2f3638; } + background-color: #38343a; } .bar-util-btn:hover, .bar-util-btn:focus { - background-color: #3d4547; } + background-color: #474249; } .bar-util-btn:active { - background-color: #4c5355; } + background-color: #565158; } .cheatsheet-bg { border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; margin-bottom: 0.682rem; - background-color: #0b0f10; + background-color: #0f0e10; padding: 1.364rem; } .cheatsheet-key { @@ -919,16 +930,16 @@ tooltip { padding: 0.136rem 0.205rem; border-radius: 0.409rem; -gtk-outline-radius: 0.409rem; - color: #85e3f4; - border: 0.068rem solid #85e3f4; - box-shadow: 0rem 0.136rem 0rem #85e3f4; + color: #eccbff; + border: 0.068rem solid #eccbff; + box-shadow: 0rem 0.136rem 0rem #eccbff; font-weight: 500; } .cheatsheet-key-notkey { min-height: 1.364rem; padding: 0.136rem 0.205rem; margin: 0.17rem; - color: #9cefff; } + color: #f4d9ff; } .cheatsheet-closebtn { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -948,6 +959,10 @@ tooltip { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; font-size: 1.705rem; } +.bg-wallpaper-transition { + transition: 1000ms cubic-bezier(0.05, 0.7, 0.1, 1); + font-size: 1px; } + .bg-time-box { border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; @@ -957,12 +972,12 @@ tooltip { .bg-time-clock { font-family: 'Gabarito'; font-size: 5.795rem; - color: #e1e3e4; } + color: #e7e0e5; } .bg-time-date { font-family: 'Gabarito'; font-size: 2.591rem; - color: #e1e3e4; } + color: #e7e0e5; } .bg-distro-box { border-radius: 1.705rem; @@ -973,12 +988,12 @@ tooltip { .bg-distro-txt { font-family: 'Gabarito'; font-size: 1.432rem; - color: #e1e3e4; } + color: #e7e0e5; } .bg-distro-name { font-family: 'Gabarito'; font-size: 1.432rem; - color: #cde7ed; } + color: #eedcf5; } .bg-graph { color: rgba(255, 255, 255, 0.5); @@ -987,25 +1002,25 @@ tooltip { .bg-quicklaunch-title { font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter", "Roboto", "Ubuntu", "Noto Sans", sans-serif; - color: #bfc8ca; } + color: #cdc3ce; } .bg-quicklaunch-btn { font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter", "Roboto", "Ubuntu", "Noto Sans", sans-serif; border-radius: 9999px; -gtk-outline-radius: 9999px; - background-color: #2f3638; - color: #bfc8ca; - border: 0.068rem solid #a1a3a4; + background-color: #38343a; + color: #cdc3ce; + border: 0.068rem solid #a6a1a5; min-width: 4.432rem; min-height: 2.045rem; padding: 0.273rem 0.682rem; } .bg-quicklaunch-btn:hover, .bg-quicklaunch-btn:focus { - background-color: #363d3f; } + background-color: #3f3b41; } .bg-quicklaunch-btn:active { - background-color: #3d4547; } + background-color: #474249; } .bg-system-bg { border-radius: 1.159rem; @@ -1017,14 +1032,14 @@ tooltip { min-height: 4.091rem; font-size: 0px; padding: 0rem; - background-color: #2f3638; } + background-color: #38343a; } .dock-bg { border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; - background-color: #0b0f10; + background-color: #0f0e10; padding: 0.682rem; } .dock-app-btn { @@ -1034,10 +1049,10 @@ tooltip { .dock-app-btn:hover, .dock-app-btn:focus { - background-color: rgba(35, 39, 41, 0.31); } + background-color: rgba(41, 38, 42, 0.31); } .dock-app-btn:active { - background-color: rgba(55, 59, 61, 0.31); } + background-color: rgba(61, 57, 62, 0.31); } .dock-app-icon { min-width: 3.409rem; @@ -1045,20 +1060,20 @@ tooltip { .dock-separator { min-width: 0.068rem; - background-color: #2f3638; } + background-color: #38343a; } .osd-bg { min-width: 8.864rem; min-height: 3.409rem; } .osd-value { - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; - background-color: #0b0f10; + background-color: #0f0e10; border-radius: 1.023rem; padding: 0.625rem 1.023rem; padding-top: 0.313rem; } @@ -1073,36 +1088,36 @@ tooltip { min-height: 0.954rem; min-width: 0.068rem; border-radius: 10rem; - background-color: #334a4f; } + background-color: #4f4256; } .osd-progress progress { transition: 200ms cubic-bezier(0.1, 1, 0, 1); min-height: 0.680rem; min-width: 0.680rem; margin: 0rem 0.137rem; border-radius: 10rem; - background-color: #cde7ed; } + background-color: #eedcf5; } .osd-icon { - color: #9cefff; } + color: #f4d9ff; } .osd-label { font-size: 1.023rem; font-weight: 500; - color: #e1e3e4; + color: #e7e0e5; margin-top: 0.341rem; } .osd-value-txt { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; font-size: 1.688rem; font-weight: 500; - color: #e1e3e4; } + color: #e7e0e5; } .osd-notifs { padding-top: 0.313rem; } .osd-colorscheme { border-radius: 1.023rem; - background-color: #0b0f10; + background-color: #0f0e10; padding: 1.023rem; box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; } @@ -1120,46 +1135,46 @@ tooltip { margin: 0.409rem; } .osd-color-primary { - background-color: #85e3f4; - color: #00363f; } + background-color: #eccbff; + color: #471868; } .osd-color-primary box { - background-color: #00363f; } + background-color: #471868; } .osd-color-primaryContainer { - background-color: #1a606b; - color: #9cefff; } + background-color: #6f478d; + color: #f4d9ff; } .osd-color-primaryContainer box { - background-color: #9cefff; } + background-color: #f4d9ff; } .osd-color-secondary { - background-color: #b1cbd1; - color: #1c3439; } + background-color: #d2c1d9; + color: #372c3e; } .osd-color-secondary box { - background-color: #1c3439; } + background-color: #372c3e; } .osd-color-secondaryContainer { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .osd-color-secondaryContainer box { - background-color: #cde7ed; } + background-color: #eedcf5; } .osd-color-surfaceVariant { - background-color: #2f3638; - color: #bfc8ca; } + background-color: #38343a; + color: #cdc3ce; } .osd-color-surfaceVariant box { - background-color: #bfc8ca; } + background-color: #cdc3ce; } .osd-color-surface { - background-color: #191d1f; - color: #e1e3e4; } + background-color: #1f1c20; + color: #e7e0e5; } .osd-color-surface box { - background-color: #e1e3e4; } + background-color: #e7e0e5; } .osd-color-background { - background-color: #0b0f10; - color: #e1e3e4; } + background-color: #0f0e10; + color: #e7e0e5; } .osd-color-background box { - background-color: #e1e3e4; } + background-color: #e7e0e5; } .osd-show { transition: 200ms cubic-bezier(0.1, 1, 0, 1); } @@ -1174,29 +1189,29 @@ tooltip { transition: 300ms cubic-bezier(0.1, 1, 0, 1); border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; min-width: 13.636rem; min-height: 3.409rem; padding: 0rem 1.364rem; padding-right: 2.864rem; - background-color: #0b0f10; - color: #e1e3e4; + background-color: #0f0e10; + color: #e7e0e5; caret-color: transparent; } .overview-search-box selection { - background-color: #b1cbd1; - color: #1c3439; } + background-color: #d2c1d9; + color: #372c3e; } .overview-search-box-extended { min-width: 25.909rem; - caret-color: #cde7ed; } + caret-color: #eedcf5; } .overview-search-prompt { - color: #a1a3a4; } + color: #a6a1a5; } .overview-search-icon { margin: 0rem 1.023rem; } @@ -1212,16 +1227,16 @@ tooltip { .overview-search-results { border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; min-width: 28.773rem; padding: 0.682rem; - background-color: #0b0f10; - color: #e1e3e4; } + background-color: #0f0e10; + color: #e7e0e5; } .overview-search-results-icon { margin: 0rem 0.682rem; @@ -1255,43 +1270,43 @@ tooltip { .overview-tasks { border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; padding: 0.341rem; - background-color: #0b0f10; - color: #e1e3e4; } + background-color: #0f0e10; + color: #e7e0e5; } .overview-tasks-workspace { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; margin: 0.341rem; - background-color: rgba(46, 49, 51, 0.1147); } + background-color: rgba(52, 48, 52, 0.1147); } .overview-tasks-workspace-number { font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter", "Roboto", "Ubuntu", "Noto Sans", sans-serif; - color: rgba(211, 213, 214, 0.31); } + color: rgba(217, 210, 215, 0.31); } .overview-tasks-window { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; transition: 300ms cubic-bezier(0.1, 1, 0, 1); - background-color: rgba(51, 74, 79, 0.5); - color: #cde7ed; - border: 0.068rem solid rgba(205, 231, 237, 0.07); } + background-color: rgba(56, 52, 58, 0.31); + color: #eedcf5; + border: 0.068rem solid rgba(238, 220, 245, 0.07); } .overview-tasks-window:hover, .overview-tasks-window:focus { - background-color: rgba(62, 95, 102, 0.525); } + background-color: rgba(100, 85, 109, 0.525); } .overview-tasks-window:active { - background-color: rgba(72, 112, 120, 0.55); } + background-color: rgba(118, 100, 128, 0.55); } .overview-tasks-window-selected { - background-color: rgba(72, 112, 120, 0.55); } + background-color: rgba(118, 100, 128, 0.55); } .overview-tasks-window-dragging { opacity: 0.2; } @@ -1299,13 +1314,13 @@ tooltip { .osk-window { border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; - background-color: #0b0f10; } + background-color: #0f0e10; } .osk-body { padding: 1.023rem; @@ -1322,7 +1337,7 @@ tooltip { .osk-dragline { border-radius: 9999px; -gtk-outline-radius: 9999px; - background-color: #2f3638; + background-color: #38343a; min-height: 0.273rem; min-width: 10.227rem; margin-top: 0.545rem; @@ -1330,8 +1345,8 @@ tooltip { .osk-key { border-radius: 0.682rem; - background-color: rgba(47, 54, 56, 0.31); - color: #bfc8ca; + background-color: rgba(56, 52, 58, 0.31); + color: #cdc3ce; padding: 0.188rem; font-weight: 500; font-size: 1.091rem; } @@ -1373,31 +1388,31 @@ tooltip { .osk-control-button { border-radius: 0.682rem; - background-color: rgba(47, 54, 56, 0.31); - color: #bfc8ca; + background-color: rgba(56, 52, 58, 0.31); + color: #cdc3ce; font-weight: 500; font-size: 1.091rem; padding: 0.682rem; } .osk-control-button:hover, .osk-control-button:focus { - background-color: rgba(61, 69, 71, 0.31); } + background-color: rgba(71, 66, 73, 0.31); } .osk-control-button:active { - background-color: rgba(90, 98, 100, 0.31); + background-color: rgba(101, 95, 102, 0.31); font-size: 1.091rem; } .sidebar-right { transition: 300ms cubic-bezier(0.1, 1, 0, 1); - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; border-radius: 1.297rem; min-width: 27.818rem; - background-color: #0b0f10; + background-color: #0f0e10; padding: 1.023rem; } .sideright-show { @@ -1409,15 +1424,15 @@ tooltip { .sidebar-left { transition: 300ms cubic-bezier(0.1, 1, 0, 1); - border-top: 1px solid rgba(161, 163, 165, 0.19); - border-left: 1px solid rgba(161, 163, 165, 0.19); - border-right: 1px solid rgba(125, 128, 130, 0.145); - border-bottom: 1px solid rgba(125, 128, 130, 0.145); + border-top: 1px solid rgba(167, 161, 166, 0.19); + border-left: 1px solid rgba(167, 161, 166, 0.19); + border-right: 1px solid rgba(131, 126, 131, 0.145); + border-bottom: 1px solid rgba(131, 126, 131, 0.145); box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.45); margin: 0.476rem; border-radius: 1.297rem; min-width: 27.818rem; - background-color: #0b0f10; + background-color: #0f0e10; padding: 1.023rem; } .sideleft-show { @@ -1431,12 +1446,12 @@ tooltip { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; padding: 0.341rem; - background-color: rgba(25, 29, 31, 0.31); } + background-color: rgba(31, 28, 32, 0.31); } .sidebar-group-nopad { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - background-color: rgba(25, 29, 31, 0.31); } + background-color: rgba(31, 28, 32, 0.31); } .sidebar-group-invisible { padding: 0.341rem; } @@ -1448,13 +1463,13 @@ tooltip { border-radius: 9999px; -gtk-outline-radius: 9999px; padding: 0.341rem; - background-color: rgba(25, 29, 31, 0.31); } + background-color: rgba(31, 28, 32, 0.31); } .sidebar-iconbutton { border-radius: 9999px; -gtk-outline-radius: 9999px; transition: 300ms cubic-bezier(0.1, 1, 0, 1); - color: #e1e3e4; + color: #e7e0e5; min-width: 2.727rem; min-height: 2.727rem; } @@ -1468,13 +1483,13 @@ tooltip { .sidebar-button { transition: 300ms cubic-bezier(0.1, 1, 0, 1); padding: 0rem 0.818rem; - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .sidebar-button-nopad { transition: 300ms cubic-bezier(0.1, 1, 0, 1); - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .sidebar-button:hover, .sidebar-button:focus { @@ -1507,20 +1522,20 @@ tooltip { -gtk-outline-radius: 0.818rem; } .sidebar-button-active { - background-color: #85e3f4; - color: #00363f; } + background-color: #eccbff; + color: #471868; } .sidebar-button-active:hover, .sidebar-button-active:focus { - background-color: rgba(133, 220, 236, 0.79); } + background-color: rgba(228, 198, 246, 0.79); } .sidebar-button-active:active { - background-color: rgba(132, 206, 220, 0.58); } + background-color: rgba(213, 187, 228, 0.58); } .sidebar-buttons-separator { min-width: 0.068rem; min-height: 0.068rem; - background-color: #bfc8ca; } + background-color: #cdc3ce; } .sidebar-navrail { padding: 0rem 1.159rem; } @@ -1538,13 +1553,13 @@ tooltip { background-color: rgba(128, 128, 128, 0.7); } .sidebar-navrail-btn-active > box > label:first-child { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .sidebar-navrail-btn-active:hover > box > label:first-child, .sidebar-navrail-btn-active:focus > box > label:first-child { - background-color: rgba(52, 75, 80, 0.93); - color: rgba(204, 229, 235, 0.93); } + background-color: rgba(80, 67, 87, 0.93); + color: rgba(236, 218, 243, 0.93); } .sidebar-sysinfo-grouppad { padding: 1.159rem; } @@ -1554,8 +1569,8 @@ tooltip { min-width: 0.818rem; min-height: 4.091rem; padding: 0.409rem; - background-color: #334a4f; - color: #cde7ed; + background-color: #4f4256; + color: #eedcf5; font-size: 0px; } .sidebar-memory-swap-circprog { @@ -1564,16 +1579,16 @@ tooltip { min-height: 2.255rem; padding: 0.409rem; margin: 0.918rem; - background-color: #334a4f; - color: #cde7ed; + background-color: #4f4256; + color: #eedcf5; font-size: 0px; } .sidebar-cpu-circprog { min-width: 0.818rem; min-height: 3.409rem; padding: 0.409rem; - background-color: #334a4f; - color: #cde7ed; + background-color: #4f4256; + color: #eedcf5; transition: 1000ms cubic-bezier(0.1, 1, 0, 1); font-size: 0px; } @@ -1588,14 +1603,14 @@ tooltip { -gtk-outline-radius: 9999px; min-width: 0.273rem; min-height: 2.045rem; - background-color: rgba(191, 200, 202, 0.31); } + background-color: rgba(205, 195, 206, 0.31); } .sidebar-scrollbar slider:hover, .sidebar-scrollbar slider:focus { - background-color: rgba(191, 200, 202, 0.448); } + background-color: rgba(205, 195, 206, 0.448); } .sidebar-scrollbar slider:active { - background-color: #777f81; } + background-color: #837c84; } .sidebar-calendar-btn { border-radius: 9999px; @@ -1603,7 +1618,7 @@ tooltip { transition: 300ms cubic-bezier(0.1, 1, 0, 1); min-height: 2.523rem; min-width: 2.523rem; - color: #e1e3e4; } + color: #e7e0e5; } .sidebar-calendar-btn:hover, .sidebar-calendar-btn:focus { @@ -1617,18 +1632,18 @@ tooltip { margin-right: -10.341rem; } .sidebar-calendar-btn-today { - background-color: #85e3f4; - color: #00363f; } + background-color: #eccbff; + color: #471868; } .sidebar-calendar-btn-today:hover, .sidebar-calendar-btn-today:focus { - background-color: rgba(133, 220, 236, 0.79); } + background-color: rgba(228, 198, 246, 0.79); } .sidebar-calendar-btn-today:active { - background-color: rgba(132, 206, 220, 0.58); } + background-color: rgba(213, 187, 228, 0.58); } .sidebar-calendar-btn-othermonth { - color: #7d8082; } + color: #837e83; } .sidebar-calendar-header { margin: 0.341rem; } @@ -1637,40 +1652,40 @@ tooltip { border-radius: 9999px; -gtk-outline-radius: 9999px; padding: 0rem 0.682rem; - background-color: rgba(47, 54, 56, 0.31); - color: #bfc8ca; } + background-color: rgba(56, 52, 58, 0.31); + color: #cdc3ce; } .sidebar-calendar-monthyear-btn:hover, .sidebar-calendar-monthyear-btn:focus { background-color: rgba(128, 128, 128, 0.3); - color: #b8c1c3; } + color: #c6bcc7; } .sidebar-calendar-monthyear-btn:active { background-color: rgba(128, 128, 128, 0.7); - color: #a9b2b4; } + color: #b7aeb8; } .sidebar-calendar-monthshift-btn { border-radius: 9999px; -gtk-outline-radius: 9999px; min-width: 2.045rem; min-height: 2.045rem; - background-color: rgba(47, 54, 56, 0.31); - color: #bfc8ca; } + background-color: rgba(56, 52, 58, 0.31); + color: #cdc3ce; } .sidebar-calendar-monthshift-btn:hover { background-color: rgba(128, 128, 128, 0.3); - color: #b8c1c3; } + color: #c6bcc7; } .sidebar-calendar-monthshift-btn:active { background-color: rgba(128, 128, 128, 0.7); - color: #a9b2b4; } + color: #b7aeb8; } .sidebar-selector-tab { border-radius: 0.818rem; -gtk-outline-radius: 0.818rem; transition: 300ms cubic-bezier(0.1, 1, 0, 1); min-height: 2.5rem; - color: #e1e3e4; } + color: #e7e0e5; } .sidebar-selector-tab:hover, .sidebar-selector-tab:focus { @@ -1680,7 +1695,7 @@ tooltip { background-color: rgba(128, 128, 128, 0.7); } .sidebar-selector-tab-active > box > label { - color: #85e3f4; } + color: #eccbff; } .sidebar-selector-highlight-offset { margin-top: -0.205rem; @@ -1688,14 +1703,14 @@ tooltip { .sidebar-selector-highlight { transition: 180ms ease-in-out; - color: #85e3f4; + color: #eccbff; min-height: 0.205rem; } .sidebar-todo-item { padding-right: 0.545rem; } .sidebar-todo-item-even { - background-color: rgba(47, 54, 56, 0.1); } + background-color: rgba(56, 52, 58, 0.1); } .sidebar-todo-item-action { border-radius: 9999px; @@ -1714,7 +1729,7 @@ tooltip { min-width: 0rem; } .sidebar-todo-crosser-crossed { - background-color: #e1e3e4; } + background-color: #e7e0e5; } .sidebar-todo-crosser-removed { background-color: #ffb4a9; } @@ -1722,78 +1737,78 @@ tooltip { .sidebar-todo-new { border-radius: 9999px; -gtk-outline-radius: 9999px; - color: #cde7ed; + color: #eedcf5; margin: 0.341rem; padding: 0.205rem 0.545rem; - border: 0.068rem solid #e1e3e4; } + border: 0.068rem solid #e7e0e5; } .sidebar-todo-newz, .sidebar-todo-new:focus { - background-color: #384f54; } + background-color: #54475b; } .sidebar-todo-new:active { - background-color: #52696f; } + background-color: #6f6176; } .sidebar-todo-add { - border-radius: 9999px; - -gtk-outline-radius: 9999px; transition: 300ms cubic-bezier(0.1, 1, 0, 1); + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; min-width: 1.705rem; min-height: 1.705rem; - color: #cde7ed; - border: 0.068rem solid #e1e3e4; } + color: #eedcf5; + border: 0.068rem solid #e7e0e5; } .sidebar-todo-add:hover, .sidebar-todo-add:focus { - background-color: #384f54; } + background-color: #54475b; } .sidebar-todo-add:active { - background-color: #52696f; } + background-color: #6f6176; } .sidebar-todo-add-available { transition: 300ms cubic-bezier(0.1, 1, 0, 1); - border-radius: 9999px; - -gtk-outline-radius: 9999px; + border-radius: 0.818rem; + -gtk-outline-radius: 0.818rem; min-width: 1.705rem; min-height: 1.705rem; - background-color: #85e3f4; - color: #00363f; - border: 0.068rem solid #85e3f4; } + background-color: #eccbff; + color: #471868; + border: 0.068rem solid #eccbff; } .sidebar-todo-add-available:hover, .sidebar-todo-add-available:focus { - background-color: #81deef; } + background-color: #e7c6fa; } .sidebar-todo-add-available:active { - background-color: #6ac0d0; } + background-color: #cba7e1; } .sidebar-todo-entry { transition: 300ms cubic-bezier(0.1, 1, 0, 1); border-radius: 0.818rem; -gtk-outline-radius: 0.818rem; - background-color: #2f3638; - color: #bfc8ca; - caret-color: #bfc8ca; + background-color: #38343a; + color: #cdc3ce; + caret-color: #cdc3ce; margin: 0rem 0.341rem; min-height: 1.773rem; min-width: 0rem; padding: 0.205rem 0.682rem; - border: 0.068rem solid #777f81; } + border: 0.068rem solid #837c84; } .sidebar-todo-entry:focus { - border: 0.068rem solid #b1b9bb; } + border: 0.068rem solid #beb5bf; } .sidebar-module { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; padding: 0.341rem; - background-color: rgba(25, 29, 31, 0.31); } + background-color: rgba(31, 28, 32, 0.31); } .sidebar-module-btn-arrow { border-radius: 9999px; -gtk-outline-radius: 9999px; font-family: "Material Symbols Rounded", "Material Symbols Outlined", "Material Symbols Sharp"; - background-color: rgba(47, 54, 56, 0.31); + background-color: rgba(56, 52, 58, 0.31); min-width: 1.705rem; min-height: 1.705rem; } @@ -1801,7 +1816,7 @@ tooltip { border-radius: 9999px; -gtk-outline-radius: 9999px; padding: 0.341rem; - background-color: rgba(25, 29, 31, 0.31); } + background-color: rgba(31, 28, 32, 0.31); } .sidebar-chat-apiswitcher-icon { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -1809,11 +1824,11 @@ tooltip { -gtk-outline-radius: 9999px; min-width: 2.182rem; min-height: 2.182rem; - color: #e1e3e4; } + color: #e7e0e5; } .sidebar-chat-apiswitcher-icon-enabled { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .sidebar-chat-viewport { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -1822,12 +1837,12 @@ tooltip { .sidebar-chat-textarea { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - border: 0.068rem solid #697072; + border: 0.068rem solid #746d75; padding: 0.682rem; } .sidebar-chat-entry { - color: #bfc8ca; - caret-color: #bfc8ca; + color: #cdc3ce; + caret-color: #cdc3ce; min-height: 1.773rem; min-width: 0rem; } @@ -1836,25 +1851,25 @@ tooltip { min-width: 1.705rem; min-height: 1.705rem; border-radius: 0.478rem; - background-color: #697072; } + background-color: #746d75; } .sidebar-chat-send:hover, .sidebar-chat-send:focus { - background-color: #6c7476; } + background-color: #787079; } .sidebar-chat-send:active { - background-color: #7d888b; } + background-color: #8c838f; } .sidebar-chat-send-available { - background-color: #85e3f4; - color: #00363f; } + background-color: #eccbff; + color: #471868; } .sidebar-chat-send-available:hover, .sidebar-chat-send-available:focus { - background-color: #81deef; } + background-color: #e7c6fa; } .sidebar-chat-send-available:active { - background-color: #6ac0d0; } + background-color: #cba7e1; } .sidebar-chat-message { margin: 0.682rem; } @@ -1864,16 +1879,16 @@ tooltip { border-radius: 9999px; -gtk-outline-radius: 9999px; min-width: 0.136rem; - background-color: #e1e3e4; } + background-color: #e7e0e5; } .sidebar-chat-indicator-user { - background-color: #e1e3e4; } + background-color: #e7e0e5; } .sidebar-chat-indicator-bot { - background-color: #85e3f4; } + background-color: #eccbff; } .sidebar-chat-indicator-System { - background-color: #b1cbd1; } + background-color: #d2c1d9; } .sidebar-chat-name { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; @@ -1892,19 +1907,19 @@ tooltip { .sidebar-chat-codeblock { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - background-color: rgba(76, 83, 85, 0.31); - color: #bfc8ca; + background-color: rgba(86, 81, 88, 0.31); + color: #cdc3ce; margin: 0rem 0.682rem; - border: 0.068rem solid rgba(205, 231, 237, 0.07); } + border: 0.068rem solid rgba(238, 220, 245, 0.07); } .sidebar-chat-codeblock-topbar { font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter", "Roboto", "Ubuntu", "Noto Sans", sans-serif; margin: 0.273rem; margin-bottom: 0rem; - background-color: rgba(93, 112, 116, 0.517); - color: #cde7ed; + background-color: rgba(117, 105, 122, 0.517); + color: #eedcf5; border-radius: 0.886rem; - border: 0.068rem solid #425a5f; + border: 0.068rem solid #5f5166; border-top-left-radius: 0.886rem; border-top-right-radius: 0.886rem; padding: 0.341rem 0.477rem; } @@ -1921,10 +1936,10 @@ tooltip { .sidebar-chat-codeblock-topbar-btn:hover, .sidebar-chat-codeblock-topbar-btn:focus { - background-color: #52696f; } + background-color: #6f6176; } .sidebar-chat-codeblock-topbar-btn:active { - background-color: #71898e; } + background-color: #8f8096; } .sidebar-chat-codeblock-code { font-family: "JetBrains Mono NF", "JetBrains Mono Nerd Font", "JetBrains Mono NL", "SpaceMono NF", "SpaceMono Nerd Font", monospace; @@ -1932,7 +1947,7 @@ tooltip { .sidebar-chat-divider { min-height: 1px; - background-color: #697072; + background-color: #746d75; margin: 0rem 0.545rem; } .sidebar-chat-welcome-txt { @@ -1952,9 +1967,10 @@ tooltip { transition: 300ms cubic-bezier(0.1, 1, 0, 1); min-height: 4.773rem; min-width: 4.773rem; - font-size: 4rem; - background-color: white; - color: black; } + font-family: "Material Symbols Rounded", "Material Symbols Outlined", "Material Symbols Sharp"; + font-size: 2.727rem; + background-color: #e7e0e5; + color: #0f0e10; } .sidebar-chat-chip { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -1963,7 +1979,7 @@ tooltip { padding: 0.341rem 0.477rem; } .sidebar-chat-chip-action { - border: 0.068rem solid #697072; } + border: 0.068rem solid #746d75; } .sidebar-chat-chip-action:hover, .sidebar-chat-chip-action:focus { @@ -1973,16 +1989,16 @@ tooltip { background-color: rgba(128, 128, 128, 0.7); } .sidebar-chat-chip-action-active { - color: #697072; - border: 0.068rem solid #697072; } + color: #746d75; + border: 0.068rem solid #746d75; } .sidebar-chat-chip-toggle { transition: 300ms cubic-bezier(0.1, 1, 0, 1); border-radius: 0.818rem; -gtk-outline-radius: 0.818rem; padding: 0.341rem 0.477rem; - background-color: rgba(47, 54, 56, 0.31); - color: #bfc8ca; } + background-color: rgba(56, 52, 58, 0.31); + color: #cdc3ce; } .sidebar-chat-chip-toggle:focus, .sidebar-chat-chip-toggle:hover { @@ -1997,7 +2013,7 @@ tooltip { transition: 300ms cubic-bezier(0.1, 1, 0, 1); min-height: 2.386rem; min-width: 2.386rem; - color: #e1e3e4; } + color: #e7e0e5; } .sidebar-pin:hover, .sidebar-pin:focus { @@ -2007,16 +2023,16 @@ tooltip { background-color: rgba(128, 128, 128, 0.7); } .sidebar-pin-enabled { - background-color: #85e3f4; } + background-color: #eccbff; } .sidebar-pin-enabled label { - color: #00363f; } + color: #471868; } .sidebar-pin-enabled:hover, .sidebar-pin-enabled:focus { - background-color: #78d2e2; } + background-color: #dcb9f0; } .sidebar-pin-enabled:active { - background-color: #6ac0d0; } + background-color: #cba7e1; } .sidebar-waifu-heading { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; @@ -2059,24 +2075,24 @@ tooltip { .session-bg { margin-top: -2.727rem; - background-color: rgba(11, 15, 16, 0.64); } + background-color: rgba(15, 14, 16, 0.64); } .session-button { border-radius: 1.705rem; -gtk-outline-radius: 1.705rem; min-width: 8.182rem; min-height: 8.182rem; - background-color: #2f3638; - color: #bfc8ca; + background-color: #38343a; + color: #cdc3ce; font-size: 3rem; } .session-button-focused { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .session-button-desc { - background-color: #242a2c; - color: #d0d6d7; + background-color: #2c282d; + color: #dad2da; border-bottom-left-radius: 1.705rem; border-bottom-right-radius: 1.705rem; padding: 0.205rem 0.341rem; @@ -2087,31 +2103,31 @@ tooltip { -gtk-outline-radius: 1.705rem; min-width: 8.182rem; min-height: 5.455rem; - background-color: #2f3638; - color: #bfc8ca; + background-color: #38343a; + color: #cdc3ce; font-size: 3rem; } .notif-low { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - background-color: rgba(47, 54, 56, 0.45); - color: #bfc8ca; + background-color: rgba(56, 52, 58, 0.45); + color: #cdc3ce; padding: 0.818rem; padding-right: 1.363rem; } .notif-normal { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - background-color: rgba(47, 54, 56, 0.45); - color: #bfc8ca; + background-color: rgba(56, 52, 58, 0.45); + color: #cdc3ce; padding: 0.818rem; padding-right: 1.363rem; } .notif-critical { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - background-color: #334a4f; - color: #cde7ed; + background-color: #4f4256; + color: #eedcf5; padding: 0.818rem; padding-right: 1.363rem; } @@ -2119,8 +2135,8 @@ tooltip { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; min-width: 30.682rem; - background-color: #0b0f10; - color: #bfc8ca; + background-color: #0f0e10; + color: #cdc3ce; padding: 0.818rem; padding-right: 1.363rem; } @@ -2128,8 +2144,8 @@ tooltip { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; min-width: 30.682rem; - background-color: #0b0f10; - color: #bfc8ca; + background-color: #0f0e10; + color: #cdc3ce; padding: 0.818rem; padding-right: 1.363rem; } @@ -2137,19 +2153,19 @@ tooltip { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; min-width: 30.682rem; - background-color: #334a4f; - color: #cde7ed; + background-color: #4f4256; + color: #eedcf5; padding: 0.818rem; padding-right: 1.363rem; } .notif-body-low { - color: #8f989a; } + color: #9c949d; } .notif-body-normal { - color: #8f989a; } + color: #9c949d; } .notif-body-critical { - color: #9ab3b9; } + color: #baa9c1; } .notif-icon { border-radius: 9999px; @@ -2158,20 +2174,20 @@ tooltip { min-height: 3.409rem; } .notif-icon-material { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .notif-icon-material-low { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .notif-icon-material-normal { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .notif-icon-material-critical { - background-color: #cde7ed; - color: #334a4f; } + background-color: #eedcf5; + color: #4f4256; } .notif-expand-btn { border-radius: 1.159rem; @@ -2199,20 +2215,20 @@ tooltip { background-color: rgba(128, 128, 128, 0.7); } .notif-listaction-btn-enabled { - background-color: #334a4f; - color: #cde7ed; } + background-color: #4f4256; + color: #eedcf5; } .notif-listaction-btn-enabled:hover, .notif-listaction-btn-enabled:focus { - background-color: #425a5f; } + background-color: #5f5166; } .notif-listaction-btn-enabled:active { - background-color: #5a7177; } + background-color: #77697e; } .osd-notif { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; - background-color: rgba(11, 15, 16, 0.46); + background-color: rgba(15, 14, 16, 0.46); min-width: 30.682rem; } .notif-circprog-low { @@ -2220,21 +2236,21 @@ tooltip { min-width: 0.136rem; min-height: 3.136rem; padding: 0rem; - color: #cde7ed; } + color: #eedcf5; } .notif-circprog-normal { transition: 0ms linear; min-width: 0.136rem; min-height: 3.136rem; padding: 0rem; - color: #cde7ed; } + color: #eedcf5; } .notif-circprog-critical { transition: 0ms linear; min-width: 0.136rem; min-height: 3.136rem; padding: 0rem; - color: #334a4f; } + color: #4f4256; } .notif-actions { min-height: 2.045rem; } @@ -2244,8 +2260,8 @@ tooltip { -gtk-outline-radius: 0.818rem; } .notif-action-low { - background-color: rgba(42, 46, 48, 0.31); - color: #bfc8ca; } + background-color: rgba(48, 45, 49, 0.31); + color: #cdc3ce; } .notif-action-low:focus, .notif-action-low:hover { @@ -2255,8 +2271,8 @@ tooltip { background-color: rgba(128, 128, 128, 0.7); } .notif-action-normal { - background-color: rgba(42, 46, 48, 0.31); - color: #bfc8ca; } + background-color: rgba(48, 45, 49, 0.31); + color: #cdc3ce; } .notif-action-normal:focus, .notif-action-normal:hover { @@ -2266,15 +2282,29 @@ tooltip { background-color: rgba(128, 128, 128, 0.7); } .notif-action-critical { - background-color: #425a5f; - color: #bfc8ca; } + background-color: #5f5166; + color: #cdc3ce; } .notif-action-critical:focus, .notif-action-critical:hover { - background-color: #4f666b; } + background-color: #6c5e73; } .notif-action-critical:active { - background-color: #566e73; } + background-color: #74657b; } + +@keyframes flyin-top { + from { + margin-top: -2.795rem; } + to { + margin-top: 0rem; } } + +@keyframes flyin-bottom { + from { + margin-top: 4.841rem; + margin-bottom: -4.841rem; } + to { + margin-bottom: 0rem; + margin-top: 0rem; } } .osd-music { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -2284,9 +2314,9 @@ tooltip { border-radius: 1.159rem; -gtk-outline-radius: 1.159rem; min-width: 29.659rem; - background-color: #0b0f10; + background-color: #0f0e10; padding: 0rem 1.023rem; - background: linear-gradient(127deg, rgba(37, 44, 45, 0.7), rgba(37, 44, 45, 0.55) 70.71%), linear-gradient(217deg, rgba(63, 72, 74, 0.7), rgba(63, 72, 74, 0.55) 70.71%), radial-gradient(circle at 0% 100%, #334a4f 13%, rgba(0, 0, 0, 0) 100%), linear-gradient(336deg, rgba(51, 74, 79, 0.7), rgba(51, 74, 79, 0.55) 70.71%), linear-gradient(#0b0f10, #0b0f10); } + background: linear-gradient(127deg, rgba(45, 42, 47, 0.7), rgba(45, 42, 47, 0.55) 70.71%), linear-gradient(217deg, rgba(75, 69, 77, 0.7), rgba(75, 69, 77, 0.55) 70.71%), radial-gradient(circle at 0% 100%, #4f4256 13%, rgba(0, 0, 0, 0) 100%), linear-gradient(336deg, rgba(79, 66, 86, 0.7), rgba(79, 66, 86, 0.55) 70.71%), linear-gradient(#0f0e10, #0f0e10); } .osd-music-cover-fallback { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -2294,8 +2324,8 @@ tooltip { -gtk-outline-radius: 0.818rem; min-width: 7.5rem; min-height: 7.5rem; - background-color: rgba(25, 29, 31, 0.31); - color: #d1d4d5; } + background-color: rgba(31, 28, 32, 0.31); + color: #d7d1d6; } .osd-music-cover { border-radius: 0.818rem; @@ -2321,13 +2351,13 @@ tooltip { transition: 300ms cubic-bezier(0.1, 1, 0, 1); font-family: "Gabarito", "Poppins", "Lexend", sans-serif; font-size: 1.364rem; - color: #d1d4d5; } + color: #d7d1d6; } .osd-music-artists { transition: 300ms cubic-bezier(0.1, 1, 0, 1); font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter", "Roboto", "Ubuntu", "Noto Sans", sans-serif; font-size: 0.955rem; - color: rgba(196, 200, 201, 0.9); } + color: rgba(202, 197, 202, 0.9); } .osd-music-pill { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -2336,8 +2366,8 @@ tooltip { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; min-width: 1.833rem; padding: 0.273rem 0.682rem; - background-color: rgba(40, 50, 52, 0.5); - color: #d1d4d5; } + background-color: rgba(52, 47, 55, 0.5); + color: #d7d1d6; } .osd-music-controls { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -2346,8 +2376,8 @@ tooltip { font-family: "Gabarito", "Poppins", "Lexend", sans-serif; min-width: 1.833rem; padding: 0.205rem; - background-color: rgba(40, 50, 52, 0.5); - color: #d1d4d5; } + background-color: rgba(52, 47, 55, 0.5); + color: #d7d1d6; } .osd-music-controlbtn { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -2358,10 +2388,10 @@ tooltip { .osd-music-controlbtn:hover, .osd-music-controlbtn:focus { - background-color: rgba(82, 91, 92, 0.55); } + background-color: rgba(93, 88, 95, 0.55); } .osd-music-controlbtn:active { - background-color: rgba(99, 106, 108, 0.575); } + background-color: rgba(108, 103, 110, 0.575); } .osd-music-controlbtn-txt { transition: 300ms cubic-bezier(0.1, 1, 0, 1); @@ -2375,15 +2405,15 @@ tooltip { min-width: 0.409rem; min-height: 3.068rem; padding: 0.273rem; - color: #d1d4d5; } + color: #d7d1d6; } .osd-music-playstate { transition: 300ms cubic-bezier(0.1, 1, 0, 1); min-height: 3.068rem; min-width: 3.068rem; border-radius: 10rem; - background-color: rgba(40, 50, 52, 0.5); - color: #d1d4d5; } + background-color: rgba(52, 47, 55, 0.5); + color: #d7d1d6; } .osd-music-playstate-btn > label { transition: 50ms cubic-bezier(0.05, 0.7, 0.1, 1); diff --git a/.config/ags/widgets/bar/leftspace.js b/.config/ags/widgets/bar/leftspace.js deleted file mode 100644 index 9f7d6111e..000000000 --- a/.config/ags/widgets/bar/leftspace.js +++ /dev/null @@ -1,74 +0,0 @@ -import App from 'resource:///com/github/Aylur/ags/app.js'; -import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; -import { RoundedCorner } from "../../lib/roundedcorner.js"; -import Brightness from '../../services/brightness.js'; -import Indicator from '../../services/indicator.js'; - -const WindowTitle = async () => Widget.Scrollable({ - hexpand: true, vexpand: true, - hscroll: 'automatic', vscroll: 'never', - child: Widget.Box({ - vertical: true, - children: [ - Widget.Label({ - xalign: 0, - className: 'txt-smaller bar-topdesc txt', - setup: (self) => self.hook(Hyprland.active.client, label => { // Hyprland.active.client - label.label = Hyprland.active.client.class.length === 0 ? 'Desktop' : Hyprland.active.client.class; - }), - }), - Widget.Label({ - xalign: 0, - className: 'txt txt-smallie', - setup: (self) => self.hook(Hyprland.active.client, label => { // Hyprland.active.client - label.label = Hyprland.active.client.title.length === 0 ? `Workspace ${Hyprland.active.workspace.id}` : Hyprland.active.client.title; - }), - }) - ] - }) -}) - -const OptionalWindowTitle = async () => { - try { - return await WindowTitle(); - } catch { - return null; - } -}; -const OptionalWindowTitleInstance = await OptionalWindowTitle(); - -export const ModuleLeftSpace = () => Widget.EventBox({ - onScrollUp: () => { - Indicator.popup(1); // Since the brightness and speaker are both on the same window - Brightness.screen_value += 0.05; - }, - onScrollDown: () => { - Indicator.popup(1); // Since the brightness and speaker are both on the same window - Brightness.screen_value -= 0.05; - }, - onPrimaryClick: () => { - App.toggleWindow('sideleft'); - }, - child: Widget.Box({ - homogeneous: false, - children: [ - RoundedCorner('topleft', { className: 'corner-black' }), - Widget.Overlay({ - overlays: [ - Widget.Box({ hexpand: true }), - Widget.Box({ - className: 'bar-sidemodule', hexpand: true, - children: [Widget.Box({ - vertical: true, - className: 'bar-space-button', - children: [ - OptionalWindowTitleInstance, - ] - })] - }), - ] - }) - ] - }) -}); \ No newline at end of file diff --git a/.config/ags/widgets/bar/main.js b/.config/ags/widgets/bar/main.js index eb3ef88a3..0ee948ef3 100644 --- a/.config/ags/widgets/bar/main.js +++ b/.config/ags/widgets/bar/main.js @@ -1,58 +1,75 @@ const { Gtk } = imports.gi; import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -import { ModuleLeftSpace } from "./leftspace.js"; -import { ModuleMusic } from "./music.js"; -import { ModuleRightSpace } from "./rightspace.js"; -import { ModuleSystem } from "./system.js"; +import WindowTitle from "./spaceleft.js"; +import Indicators from "./spaceright.js"; +import Music from "./music.js"; +import System from "./system.js"; +import { RoundedCorner, enableClickthrough } from "../../lib/roundedcorner.js"; + const OptionalWorkspaces = async () => { try { return (await import('./workspaces_hyprland.js')).default(); } catch { - // return (await import('./workspaces_sway.js')).default(); - return Box({}); + try { + return (await import('./workspaces_sway.js')).default(); + } catch { + return null; + } } }; -const left = Widget.Box({ - className: 'bar-sidemodule', - children: [ - ModuleMusic() - ], -}); - -const center = Widget.Box({ - children: [ - await OptionalWorkspaces(), - ], -}); - -const right = Widget.Box({ - className: 'bar-sidemodule', - children: [ModuleSystem()], -}); - -export default () => Widget.Window({ - name: 'bar', - anchor: ['top', 'left', 'right'], - exclusivity: 'exclusive', - visible: true, - child: Widget.CenterBox({ +export const Bar = async (monitor = 0) => { + const SideModule = (children) => Widget.Box({ + className: 'bar-sidemodule', + children: children, + }); + const barContent = Widget.CenterBox({ className: 'bar-bg', - startWidget: ModuleLeftSpace(), - centerWidget: Widget.Box({ - className: 'spacing-h-4', - children: [ - left, - center, - right, - ] - }), - endWidget: ModuleRightSpace(), setup: (self) => { const styleContext = self.get_style_context(); const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); // execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print); - } - }), + }, + startWidget: WindowTitle(), + centerWidget: Widget.Box({ + className: 'spacing-h-4', + children: [ + SideModule([Music()]), + Widget.Box({ + homogeneous: true, + children: [await OptionalWorkspaces()], + }), + SideModule([System()]), + ] + }), + endWidget: Indicators(), + }); + return Widget.Window({ + monitor, + name: `bar${monitor}`, + anchor: ['top', 'left', 'right'], + exclusivity: 'exclusive', + visible: true, + child: barContent, + }); +} + +export const BarCornerTopleft = (id = '') => Widget.Window({ + name: `barcornertl${id}`, + layer: 'top', + anchor: ['top', 'left'], + exclusivity: 'normal', + visible: true, + child: RoundedCorner('topleft', { className: 'corner', }), + setup: enableClickthrough, +}); +export const BarCornerTopright = (id = '') => Widget.Window({ + name: `barcornertr${id}`, + layer: 'top', + anchor: ['top', 'right'], + exclusivity: 'normal', + visible: true, + child: RoundedCorner('topright', { className: 'corner', }), + setup: enableClickthrough, }); \ No newline at end of file diff --git a/.config/ags/widgets/bar/music.js b/.config/ags/widgets/bar/music.js index d2c683b0c..7bcfcd10c 100644 --- a/.config/ags/widgets/bar/music.js +++ b/.config/ags/widgets/bar/music.js @@ -1,16 +1,18 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js'; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; const { execAsync, exec } = Utils; import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js"; import { showMusicControls } from '../../variables.js'; function trimTrackTitle(title) { - var cleanedTitle = title; - cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning - cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD] - return cleanedTitle.trim(); + if(!title) return ''; + const cleanRegexes = [ + /【[^】]*】/, // Touhou n weeb stuff + /\[FREE DOWNLOAD\]/, // F-777 + ]; + cleanRegexes.forEach((expr) => title.replace(expr, '')); + return title; } const TrackProgress = () => { @@ -30,61 +32,75 @@ const TrackProgress = () => { }) } -export const ModuleMusic = () => Widget.EventBox({ // TODO: use cairo to make button bounce smaller on click - onScrollUp: () => Hyprland.sendMessage(`dispatch workspace -1`), - onScrollDown: () => Hyprland.sendMessage(`dispatch workspace +1`), - onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value), - onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']), - onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print), - child: Widget.Box({ - className: 'bar-group-margin bar-sides', - children: [ - Widget.Box({ - className: 'bar-group bar-group-standalone bar-group-pad-music spacing-h-10', - children: [ - Widget.Box({ // Wrap a box cuz overlay can't have margins itself - homogeneous: true, - children: [Widget.Overlay({ - child: Widget.Box({ - vpack: 'center', - className: 'bar-music-playstate', - homogeneous: true, - children: [Widget.Label({ - vpack: 'center', - className: 'bar-music-playstate-txt', - justification: 'center', - setup: (self) => self.hook(Mpris, label => { - const mpris = Mpris.getPlayer(''); - label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`; - }), - })], - setup: (self) => self.hook(Mpris, label => { - const mpris = Mpris.getPlayer(''); - if (!mpris) return; - label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing'); - label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused'); - }), - }), - overlays: [ - TrackProgress(), - ] - })] +const switchToRelativeWorkspace = async (self, num) => { + try { + const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default; + Hyprland.sendMessage(`dispatch workspace ${num > 0 ? '+' : ''}${num}`); + } catch { + execAsync([`${App.configDir}/scripts/sway/swayToRelativeWs.sh`, `${num}`]).catch(print); + } +} + +export default () => { + // TODO: use cairo to make button bounce smaller on click, if that's possible + const playingState = Widget.Box({ // Wrap a box cuz overlay can't have margins itself + homogeneous: true, + children: [Widget.Overlay({ + child: Widget.Box({ + vpack: 'center', + className: 'bar-music-playstate', + homogeneous: true, + children: [Widget.Label({ + vpack: 'center', + className: 'bar-music-playstate-txt', + justification: 'center', + setup: (self) => self.hook(Mpris, label => { + const mpris = Mpris.getPlayer(''); + label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`; }), - Widget.Scrollable({ - hexpand: true, - child: Widget.Label({ - className: 'txt-smallie txt-onSurfaceVariant', - setup: (self) => self.hook(Mpris, label => { - const mpris = Mpris.getPlayer(''); - if (mpris) - label.label = `${trimTrackTitle(mpris.trackTitle)} • ${mpris.trackArtists.join(', ')}`; - else - label.label = 'No media'; - }), - }) - }) - ] - }) - ] + })], + setup: (self) => self.hook(Mpris, label => { + const mpris = Mpris.getPlayer(''); + if (!mpris) return; + label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing'); + label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused'); + }), + }), + overlays: [ + TrackProgress(), + ] + })] + }); + const trackTitle = Widget.Scrollable({ + hexpand: true, + child: Widget.Label({ + className: 'txt-smallie txt-onSurfaceVariant', + setup: (self) => self.hook(Mpris, label => { + const mpris = Mpris.getPlayer(''); + if (mpris) + label.label = `${trimTrackTitle(mpris.trackTitle)} • ${mpris.trackArtists.join(', ')}`; + else + label.label = 'No media'; + }), + }) }) -}); \ No newline at end of file + return Widget.EventBox({ + onScrollUp: (self) => switchToRelativeWorkspace(self, -1), + onScrollDown: (self) => switchToRelativeWorkspace(self, +1), + onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value), + onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']), + onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print), + child: Widget.Box({ + className: 'bar-group-margin bar-sides', + children: [ + Widget.Box({ + className: 'bar-group bar-group-standalone bar-group-pad-music spacing-h-10', + children: [ + playingState, + trackTitle, + ] + }) + ] + }) + }); +} \ No newline at end of file diff --git a/.config/ags/widgets/bar/rightspace.js b/.config/ags/widgets/bar/rightspace.js deleted file mode 100644 index fb4e7b8a7..000000000 --- a/.config/ags/widgets/bar/rightspace.js +++ /dev/null @@ -1,81 +0,0 @@ -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'; - -import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; -import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js'; -const { execAsync } = Utils; -import Indicator from '../../services/indicator.js'; -import { StatusIcons } from "../../lib/statusicons.js"; -import { RoundedCorner } from "../../lib/roundedcorner.js"; -import { Tray } from "./tray.js"; - -export const ModuleRightSpace = () => { - const barTray = Tray(); - const barStatusIcons = StatusIcons({ - className: 'bar-statusicons', - setup: (self) => self.hook(App, (self, currentName, visible) => { - if (currentName === 'sideright') { - self.toggleClassName('bar-statusicons-active', visible); - } - }), - }); - - return Widget.EventBox({ - onScrollUp: () => { - if (!Audio.speaker) return; - Audio.speaker.volume += 0.03; - Indicator.popup(1); - }, - onScrollDown: () => { - if (!Audio.speaker) return; - Audio.speaker.volume -= 0.03; - Indicator.popup(1); - }, - // onHover: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', true) }, - // onHoverLost: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', false) }, - onPrimaryClick: () => App.toggleWindow('sideright'), - onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']).catch(print), - onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print), - child: Widget.Box({ - homogeneous: false, - children: [ - Widget.Box({ - hexpand: true, - className: 'spacing-h-5 txt', - children: [ - Widget.Box({ - hexpand: true, - className: 'spacing-h-5 txt', - children: [ - Widget.Box({ hexpand: true, }), - barTray, - Widget.Revealer({ - transition: 'slide_left', - revealChild: false, - attribute: { - 'count': 0, - 'update': (self, diff) => { - self.attribute.count += diff; - self.revealChild = (self.attribute.count > 0); - } - }, - child: Widget.Box({ - vpack: 'center', - className: 'separator-circle', - }), - setup: (self) => self - .hook(SystemTray, (self) => self.attribute.update(self, 1), 'added') - .hook(SystemTray, (self) => self.attribute.update(self, -1), 'removed') - , - }), - barStatusIcons, - ], - }), - ] - }), - RoundedCorner('topright', { className: 'corner-black' }) - ] - }) - }); -} \ No newline at end of file diff --git a/.config/ags/widgets/bar/spaceleft.js b/.config/ags/widgets/bar/spaceleft.js new file mode 100644 index 000000000..4f2b8540a --- /dev/null +++ b/.config/ags/widgets/bar/spaceleft.js @@ -0,0 +1,72 @@ +import App from 'resource:///com/github/Aylur/ags/app.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import Brightness from '../../services/brightness.js'; +import Indicator from '../../services/indicator.js'; + +const WindowTitle = async () => { + try { + const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default; + return Widget.Scrollable({ + hexpand: true, vexpand: true, + hscroll: 'automatic', vscroll: 'never', + child: Widget.Box({ + vertical: true, + children: [ + Widget.Label({ + xalign: 0, + className: 'txt-smaller bar-topdesc txt', + setup: (self) => self.hook(Hyprland.active.client, label => { // Hyprland.active.client + label.label = Hyprland.active.client.class.length === 0 ? 'Desktop' : Hyprland.active.client.class; + }), + }), + Widget.Label({ + xalign: 0, + className: 'txt txt-smallie', + setup: (self) => self.hook(Hyprland.active.client, label => { // Hyprland.active.client + label.label = Hyprland.active.client.title.length === 0 ? `Workspace ${Hyprland.active.workspace.id}` : Hyprland.active.client.title; + }), + }) + ] + }) + }); + } catch { + return null; + } +} + +const OptionalWindowTitleInstance = await WindowTitle(); + +export default () => Widget.EventBox({ + onScrollUp: () => { + Indicator.popup(1); // Since the brightness and speaker are both on the same window + Brightness.screen_value += 0.05; + }, + onScrollDown: () => { + Indicator.popup(1); // Since the brightness and speaker are both on the same window + Brightness.screen_value -= 0.05; + }, + onPrimaryClick: () => { + App.toggleWindow('sideleft'); + }, + child: Widget.Box({ + homogeneous: false, + children: [ + Widget.Box({ className: 'bar-corner-spacing' }), + Widget.Overlay({ + overlays: [ + Widget.Box({ hexpand: true }), + Widget.Box({ + className: 'bar-sidemodule', hexpand: true, + children: [Widget.Box({ + vertical: true, + className: 'bar-space-button', + children: [ + OptionalWindowTitleInstance, + ] + })] + }), + ] + }) + ] + }) +}); \ No newline at end of file diff --git a/.config/ags/widgets/bar/spaceright.js b/.config/ags/widgets/bar/spaceright.js new file mode 100644 index 000000000..dd69557f0 --- /dev/null +++ b/.config/ags/widgets/bar/spaceright.js @@ -0,0 +1,82 @@ +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'; + +import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; +import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js'; +const { execAsync } = Utils; +import Indicator from '../../services/indicator.js'; +import { StatusIcons } from "../../lib/statusicons.js"; +import { Tray } from "./tray.js"; + +export default () => { + const barTray = Tray(); + const notifCounter = Widget.Revealer({ + transition: 'slide_left', + revealChild: false, + attribute: { + 'count': 0, + 'update': (self, diff) => { + self.attribute.count += diff; + self.revealChild = (self.attribute.count > 0); + } + }, + child: Widget.Box({ + vpack: 'center', + className: 'separator-circle', + }), + setup: (self) => self + .hook(SystemTray, (self) => self.attribute.update(self, 1), 'added') + .hook(SystemTray, (self) => self.attribute.update(self, -1), 'removed') + , + }); + const barStatusIcons = StatusIcons({ + className: 'bar-statusicons', + setup: (self) => self.hook(App, (self, currentName, visible) => { + if (currentName === 'sideright') { + self.toggleClassName('bar-statusicons-active', visible); + } + }), + }); + const actualContent = Widget.Box({ + hexpand: true, + className: 'spacing-h-5 txt', + children: [ + Widget.Box({ + hexpand: true, + className: 'spacing-h-5 txt', + children: [ + Widget.Box({ hexpand: true, }), + barTray, + notifCounter, + barStatusIcons, + ], + }), + ] + }); + + return Widget.EventBox({ + onScrollUp: () => { + if (!Audio.speaker) return; + Audio.speaker.volume += 0.03; + Indicator.popup(1); + }, + onScrollDown: () => { + if (!Audio.speaker) return; + Audio.speaker.volume -= 0.03; + Indicator.popup(1); + }, + onHover: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', true) }, + onHoverLost: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', false) }, + onPrimaryClick: () => App.toggleWindow('sideright'), + onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']).catch(print), + onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print), + child: Widget.Box({ + homogeneous: false, + children: [ + actualContent, + Widget.Box({ className: 'bar-corner-spacing' }), + ] + }) + }); +} \ No newline at end of file diff --git a/.config/ags/widgets/bar/system.js b/.config/ags/widgets/bar/system.js index d1b8238e8..4358c80c2 100644 --- a/.config/ags/widgets/bar/system.js +++ b/.config/ags/widgets/bar/system.js @@ -5,7 +5,6 @@ import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; const { Box, Label, Button, Overlay, Revealer, Scrollable, Stack, EventBox } = Widget; const { exec, execAsync } = Utils; const { GLib } = imports.gi; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import Battery from 'resource:///com/github/Aylur/ags/service/battery.js'; import { MaterialIcon } from '../../lib/materialicon.js'; import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js"; @@ -14,7 +13,7 @@ const BATTERY_LOW = 20; const BatBatteryProgress = () => { const _updateProgress = (circprog) => { // Set circular progress value - circprog.css = `font-size: ${Battery.percent}px;` + circprog.css = `font-size: ${Math.abs(Battery.percent)}px;` circprog.toggleClassName('bar-batt-circprog-low', Battery.percent <= BATTERY_LOW); circprog.toggleClassName('bar-batt-circprog-full', Battery.charged); @@ -91,7 +90,7 @@ const BarBattery = () => Box({ transitionDuration: 150, revealChild: false, transition: 'slide_right', - child: MaterialIcon('bolt', 'norm'), + child: MaterialIcon('bolt', 'norm', {tooltipText: "Charging"}), setup: (self) => self.hook(Battery, revealer => { self.revealChild = Battery.charging; }), @@ -122,25 +121,6 @@ const BarBattery = () => Box({ ] }); -const BarResourceValue = (name, icon, command) => Widget.Box({ - vpack: 'center', - className: 'bar-batt spacing-h-5', - children: [ - MaterialIcon(icon, 'small'), - Widget.ProgressBar({ // Progress - vpack: 'center', hexpand: true, - className: 'bar-prog-batt', - setup: (self) => self.poll(5000, (progress) => execAsync(['bash', '-c', command]) - .then((output) => { - progress.value = Number(output) / 100; - progress.tooltipText = `${name}: ${Number(output)}%` - }) - .catch(print) - ), - }), - ] -}); - const BarResource = (name, icon, command) => { const resourceLabel = Label({ className: 'txt-smallie txt-onSurfaceVariant', @@ -187,9 +167,18 @@ const BarGroup = ({ child }) => Widget.Box({ ] }); -export const ModuleSystem = () => Widget.EventBox({ - onScrollUp: () => Hyprland.sendMessage(`dispatch workspace -1`), - onScrollDown: () => Hyprland.sendMessage(`dispatch workspace +1`), +const switchToRelativeWorkspace = async (self, num) => { + try { + const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default; + Hyprland.sendMessage(`dispatch workspace ${num > 0 ? '+' : ''}${num}`); + } catch { + execAsync([`${App.configDir}/scripts/sway/swayToRelativeWs.sh`, `${num}`]).catch(print); + } +} + +export default () => Widget.EventBox({ + onScrollUp: (self) => switchToRelativeWorkspace(self, -1), + onScrollDown: (self) => switchToRelativeWorkspace(self, +1), onPrimaryClick: () => App.toggleWindow('sideright'), child: Widget.Box({ className: 'spacing-h-5', diff --git a/.config/ags/widgets/bar/workspaces_hyprland.js b/.config/ags/widgets/bar/workspaces_hyprland.js index bbc4514a1..0c3c63e1d 100644 --- a/.config/ags/widgets/bar/workspaces_hyprland.js +++ b/.config/ags/widgets/bar/workspaces_hyprland.js @@ -26,7 +26,7 @@ const WorkspaceContents = (count = 10) => { let workspaceMask = 0; for (let i = 0; i < workspaces.length; i++) { const ws = workspaces[i]; - if (ws.id < 0) continue; // Ignore scratchpads + if (ws.id <= 0) continue; // Ignore scratchpads if (ws.id > count) return; // Not rendered if (workspaces[i].windows > 0) { workspaceMask |= (1 << ws.id); diff --git a/.config/ags/widgets/bar/workspaces_sway.js b/.config/ags/widgets/bar/workspaces_sway.js index 700848668..53612eb93 100644 --- a/.config/ags/widgets/bar/workspaces_sway.js +++ b/.config/ags/widgets/bar/workspaces_sway.js @@ -1,58 +1,184 @@ +const { GLib, Gdk, Gtk } = imports.gi; +const Lang = imports.lang; +const Cairo = imports.cairo; +const Pango = imports.gi.Pango; +const PangoCairo = imports.gi.PangoCairo; import Widget from "resource:///com/github/Aylur/ags/widget.js"; import Sway from "../../services/sway.js"; import * as Utils from "resource:///com/github/Aylur/ags/utils.js"; -import options from "../../options.js"; -import { range } from "../../utils.js"; +const { execAsync, exec } = Utils; +const { Box, DrawingArea, EventBox } = Widget; +const NUM_OF_WORKSPACES = 10; +const dummyWs = Box({ className: 'bar-ws' }); // Not shown. Only for getting size props +const dummyActiveWs = Box({ className: 'bar-ws bar-ws-active' }); // Not shown. Only for getting size props +const dummyOccupiedWs = Box({ className: 'bar-ws bar-ws-occupied' }); // Not shown. Only for getting size props -const dispatch = (arg) => Utils.execAsync(`swaymsg workspace ${arg}`); +const switchToWorkspace = (arg) => Utils.execAsync(`swaymsg workspace ${arg}`).catch(print); +const switchToRelativeWorkspace = (self, num) => + execAsync([`${App.configDir}/scripts/sway/swayToRelativeWs.sh`, `${num}`]).catch(print); -const Workspaces = () => { - const ws = options.workspaces.value || 20; - return Widget.Box({ - children: range(ws).map((i) => - Widget.Button({ - setup: (btn) => (btn.id = i), - on_clicked: () => dispatch(i), - child: Widget.Label({ - label: `${i}`, - class_name: "indicator", - vpack: "center", - }), - setup: (self) => self.hook(Sway, (btn) => { - btn.toggleClassName("active", Sway.active.workspace.name == i); - btn.toggleClassName( - "occupied", - Sway.getWorkspace(`${i}`)?.nodes.length > 0, - ); - }), +const WorkspaceContents = (count = 10) => { + return DrawingArea({ + css: `transition: 90ms cubic-bezier(0.1, 1, 0, 1);`, + attribute: { + initialized: false, + workspaceMask: 0, + updateMask: (self) => { + if (self.attribute.initialized) return; // We only need this to run once + const workspaces = Sway.workspaces; + let workspaceMask = 0; + // console.log('----------------') + for (let i = 0; i < workspaces.length; i++) { + const ws = workspaces[i]; + // console.log(ws.name, ',', ws.num); + if (!Number(ws.name)) return; + const id = Number(ws.name); + if (id <= 0) continue; // Ignore scratchpads + if (id > count) return; // Not rendered + if (workspaces[i].windows > 0) { + workspaceMask |= (1 << id); + } + } + self.attribute.workspaceMask = workspaceMask; + self.attribute.initialized = true; + }, + toggleMask: (self, occupied, name) => { + if (occupied) self.attribute.workspaceMask |= (1 << parseInt(name)); + else self.attribute.workspaceMask &= ~(1 << parseInt(name)); + }, + }, + setup: (area) => area + .hook(Sway.active.workspace, (area) => { + area.setCss(`font-size: ${Sway.active.workspace.name}px;`) }) - ), - setup: (self) => self.hook(Sway.active.workspace, - (box) => box.children.map((btn) => { - btn.visible = Sway.workspaces.some( - (ws) => ws.name == btn.id, - ); - }) - ), - }); -}; + .hook(Sway, (self) => self.attribute.updateMask(self), 'notify::workspaces') + // .hook(Hyprland, (self, name) => self.attribute.toggleMask(self, true, name), 'workspace-added') + // .hook(Hyprland, (self, name) => self.attribute.toggleMask(self, false, name), 'workspace-removed') + .on('draw', Lang.bind(area, (area, cr) => { + const allocation = area.get_allocation(); + const { width, height } = allocation; -export default () => Widget.EventBox({ - class_name: "workspaces panel-button", - child: Widget.Box({ - // its nested like this to keep it consistent with other PanelButton widgets - child: Widget.EventBox({ - on_scroll_up: () => dispatch("next"), - on_scroll_down: () => dispatch("prev"), - class_name: "eventbox", - // binds: [["child", options.workspaces, "value", Workspaces]], - setup: (self) => self - .hook(options.workspaces, (self) => Selection.child = Workspaces(), "value") - , - }), + const workspaceStyleContext = dummyWs.get_style_context(); + const workspaceDiameter = workspaceStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL); + const workspaceRadius = workspaceDiameter / 2; + const workspaceFontSize = workspaceStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 4 * 3; + const workspaceFontFamily = workspaceStyleContext.get_property('font-family', Gtk.StateFlags.NORMAL); + const wsbg = workspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL); + const wsfg = workspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL); + + const occupiedWorkspaceStyleContext = dummyOccupiedWs.get_style_context(); + const occupiedbg = occupiedWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL); + const occupiedfg = occupiedWorkspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL); + + const activeWorkspaceStyleContext = dummyActiveWs.get_style_context(); + const activebg = activeWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL); + const activefg = activeWorkspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL); + area.set_size_request(workspaceDiameter * count, -1); + const widgetStyleContext = area.get_style_context(); + const activeWs = widgetStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL); + + const activeWsCenterX = -(workspaceDiameter / 2) + (workspaceDiameter * activeWs); + const activeWsCenterY = height / 2; + + // Font + const layout = PangoCairo.create_layout(cr); + const fontDesc = Pango.font_description_from_string(`${workspaceFontFamily[0]} ${workspaceFontSize}`); + layout.set_font_description(fontDesc); + cr.setAntialias(Cairo.Antialias.BEST); + // Get kinda min radius for number indicators + layout.set_text("0".repeat(count.toString().length), -1); + const [layoutWidth, layoutHeight] = layout.get_pixel_size(); + const indicatorRadius = Math.max(layoutWidth, layoutHeight) / 2 * 1.2; // a bit smaller than sqrt(2)*radius + const indicatorGap = workspaceRadius - indicatorRadius; + + // Draw workspace numbers + for (let i = 1; i <= count; i++) { + if (area.attribute.workspaceMask & (1 << i)) { + // Draw bg highlight + cr.setSourceRGBA(occupiedbg.red, occupiedbg.green, occupiedbg.blue, occupiedbg.alpha); + const wsCenterX = -(workspaceRadius) + (workspaceDiameter * i); + const wsCenterY = height / 2; + if (!(area.attribute.workspaceMask & (1 << (i - 1)))) { // Left + cr.arc(wsCenterX, wsCenterY, workspaceRadius, 0.5 * Math.PI, 1.5 * Math.PI); + cr.fill(); + } + else { + cr.rectangle(wsCenterX - workspaceRadius, wsCenterY - workspaceRadius, workspaceRadius, workspaceRadius * 2) + cr.fill(); + } + if (!(area.attribute.workspaceMask & (1 << (i + 1)))) { // Right + cr.arc(wsCenterX, wsCenterY, workspaceRadius, -0.5 * Math.PI, 0.5 * Math.PI); + cr.fill(); + } + else { + cr.rectangle(wsCenterX, wsCenterY - workspaceRadius, workspaceRadius, workspaceRadius * 2) + cr.fill(); + } + + // Set color for text + cr.setSourceRGBA(occupiedfg.red, occupiedfg.green, occupiedfg.blue, occupiedfg.alpha); + } + else + cr.setSourceRGBA(wsfg.red, wsfg.green, wsfg.blue, wsfg.alpha); + layout.set_text(`${i}`, -1); + const [layoutWidth, layoutHeight] = layout.get_pixel_size(); + const x = -workspaceRadius + (workspaceDiameter * i) - (layoutWidth / 2); + const y = (height - layoutHeight) / 2; + cr.moveTo(x, y); + // cr.showText(text); + PangoCairo.show_layout(cr, layout); + cr.stroke(); + } + + // Draw active ws + // base + cr.setSourceRGBA(activebg.red, activebg.green, activebg.blue, activebg.alpha); + cr.arc(activeWsCenterX, activeWsCenterY, indicatorRadius, 0, 2 * Math.PI); + cr.fill(); + // inner decor + cr.setSourceRGBA(activefg.red, activefg.green, activefg.blue, activefg.alpha); + cr.arc(activeWsCenterX, activeWsCenterY, indicatorRadius * 0.2, 0, 2 * Math.PI); + cr.fill(); + })) + , + }) +} + +export default () => EventBox({ + onScrollUp: (self) => switchToRelativeWorkspace(self, -1), + onScrollDown: (self) => switchToRelativeWorkspace(self, +1), + onMiddleClickRelease: () => App.toggleWindow('overview'), + onSecondaryClickRelease: () => App.toggleWindow('osk'), + attribute: { clicked: false }, + child: Box({ + homogeneous: true, + className: 'bar-group-margin', + children: [Box({ + className: 'bar-group bar-group-standalone bar-group-pad', + css: 'min-width: 2px;', + children: [ + WorkspaceContents(10), + ] + })] }), setup: (self) => { - console.log('[LOG] Sway workspace module loaded') + self.add_events(Gdk.EventMask.POINTER_MOTION_MASK); + self.on('motion-notify-event', (self, event) => { + if (!self.attribute.clicked) return; + const [_, cursorX, cursorY] = event.get_coords(); + const widgetWidth = self.get_allocation().width; + const wsId = Math.ceil(cursorX * NUM_OF_WORKSPACES / widgetWidth); + switchToWorkspace(wsId); + }) + self.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; + const [_, cursorX, cursorY] = event.get_coords(); + const widgetWidth = self.get_allocation().width; + const wsId = Math.ceil(cursorX * NUM_OF_WORKSPACES / widgetWidth); + switchToWorkspace(wsId); + }) + self.on('button-release-event', (self) => self.attribute.clicked = false); } }); diff --git a/.config/ags/widgets/desktopbackground/graph.js b/.config/ags/widgets/desktopbackground/graph.js deleted file mode 100644 index 5763472af..000000000 --- a/.config/ags/widgets/desktopbackground/graph.js +++ /dev/null @@ -1,83 +0,0 @@ -const { Gdk, Gtk } = imports.gi; -const Lang = imports.lang; -import { App, Service, Utils, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js'; -const { execAsync, exec } = Utils; -const { Box, Label } = Widget; - -const NUM_OF_VERTICES = 30; -const NUM_OF_EDGES = 29; -// Vertices -var vertices = []; -for (var i = 0; i < NUM_OF_VERTICES; i++) { - vertices.push([ - Math.floor(Math.random() * SCREEN_WIDTH), - Math.floor(Math.random() * SCREEN_HEIGHT) - ]); -} -// Edges -function generateRandomEdges(numVertices, numEdges) { // TODO: make sure whole graph is connected - var edges = new Set(); - var vertices = []; - - // Generate vertices - for (var i = 0; i < numVertices; i++) { - vertices.push(i); - } - - // Generate random distinct edges - while (edges.size < numEdges) { - var randomVertex1 = vertices[Math.floor(Math.random() * numVertices)]; - var randomVertex2 = vertices[Math.floor(Math.random() * numVertices)]; - - // Ensure the two vertices are distinct and the edge doesn't already exist - if (randomVertex1 !== randomVertex2) { - var edge = [randomVertex1, randomVertex2].sort(); - edges.add(edge.join(',')); - } - } - - return Array.from(edges).map(edge => edge.split(',').map(Number)); -} - -var edges = generateRandomEdges(NUM_OF_VERTICES, NUM_OF_EDGES); - -export default () => Box({ - hpack: 'fill', - vpack: 'fill', - homogeneous: true, - children: [ - Widget.DrawingArea({ - className: 'bg-graph', - setup: (area) => { - area.connect('draw', Lang.bind(area, (area, cr) => { - // area.set_size_request(SCREEN_WIDTH, SCREEN_HEIGHT); - // console.log('allocated width/height:', area.get_allocated_width(), '/', area.get_allocated_height()) - const styleContext = area.get_style_context(); - const color = styleContext.get_property('color', Gtk.StateFlags.NORMAL); - const backgroundColor = styleContext.get_property('background-color', Gtk.StateFlags.NORMAL); - const radius = area.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL); - const borderWidth = area.get_style_context().get_border(Gtk.StateFlags.NORMAL).left; // ur going to write border-width: something anyway - - cr.setSourceRGBA(backgroundColor.red, backgroundColor.green, backgroundColor.blue, backgroundColor.alpha); - cr.rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) - cr.fill(); - cr.setSourceRGBA(color.red, color.green, color.blue, color.alpha); - // Draw edges - cr.setLineWidth(borderWidth); - console.log("line width:", borderWidth); - for (var i = 0; i < NUM_OF_EDGES; i++) { - console.log(vertices[edges[i][0]][0], vertices[edges[i][0]][1], '->', vertices[edges[i][1]][0], vertices[edges[i][1]][1]) - cr.moveTo(vertices[edges[i][0]][0], vertices[edges[i][0]][1]); - cr.lineTo(vertices[edges[i][1]][0], vertices[edges[i][1]][1]); - cr.stroke(); - } - // Draw vertices - for (var i = 0; i < NUM_OF_VERTICES; i++) { - cr.arc(vertices[i][0], vertices[i][1], radius, 0, 2 * Math.PI) - cr.fill() - } - })) - } - }) - ] -}) diff --git a/.config/ags/widgets/desktopbackground/main.js b/.config/ags/widgets/desktopbackground/main.js index 13f613cec..f591fc863 100644 --- a/.config/ags/widgets/desktopbackground/main.js +++ b/.config/ags/widgets/desktopbackground/main.js @@ -1,28 +1,24 @@ -const { Gdk, Gtk } = imports.gi; 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; +import WallpaperImage from './wallpaper.js'; import TimeAndLaunchesWidget from './timeandlaunches.js' import SystemWidget from './system.js' -import GraphWidget from './graph.js' -export default () => Widget.Window({ - name: 'desktopbackground', - anchor: ['top', 'bottom', 'left', 'right'], +export default (monitor) => Widget.Window({ + name: `desktopbackground${monitor}`, + // anchor: ['top', 'bottom', 'left', 'right'], layer: 'background', - exclusivity: 'normal', + exclusivity: 'ignore', visible: true, + // child: WallpaperImage(monitor), child: Widget.Overlay({ - child: Widget.Box({ - hexpand: true, - vexpand: true, - }), + child: WallpaperImage(monitor), overlays: [ - // GraphWidget(), TimeAndLaunchesWidget(), SystemWidget(), ], - setup: (self) => self.set_overlay_pass_through(self.get_children()[1], true), + setup: (self) => { + self.set_overlay_pass_through(self.get_children()[1], true); + }, }), -}); \ No newline at end of file +}); diff --git a/.config/ags/widgets/desktopbackground/system.js b/.config/ags/widgets/desktopbackground/system.js index f69e42c8b..6d49c5248 100644 --- a/.config/ags/widgets/desktopbackground/system.js +++ b/.config/ags/widgets/desktopbackground/system.js @@ -114,11 +114,14 @@ const distroAndVersion = Box({ Label({ className: 'bg-distro-name', xalign: 0, - label: '', + label: 'An environment idk', setup: (label) => { - execAsync([`bash`, `-c`, `hyprctl version | grep -oP "Tag: v\\K\\d+\\.\\d+\\.\\d+"`]).then(distro => { - label.label = `Hyprland ${distro}`; - }).catch(print); + // hyprctl will return unsuccessfully if Hyprland isn't running + execAsync([`bash`, `-c`, `hyprctl version | grep -oP "Tag: v\\K\\d+\\.\\d+\\.\\d+"`]).then(version => { + label.label = `Hyprland ${version}`; + }).catch(() => execAsync([`bash`, `-c`, `sway -v | cut -d'-' -f1 | sed 's/sway version /v/'`]).then(version => { + label.label = `Sway ${version}`; + }).catch(print)); }, }), ] diff --git a/.config/ags/widgets/desktopbackground/wallpaper.js b/.config/ags/widgets/desktopbackground/wallpaper.js new file mode 100644 index 000000000..0b3cce444 --- /dev/null +++ b/.config/ags/widgets/desktopbackground/wallpaper.js @@ -0,0 +1,115 @@ +const { Gdk, GdkPixbuf, Gio, GLib, Gtk } = imports.gi; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; +import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js'; +const { exec, execAsync } = Utils; +const { Box, Button, Label, Stack } = Widget; +import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; + +import Wallpaper from '../../services/wallpaper.js'; +import { setupCursorHover } from '../../lib/cursorhover.js'; + +const SWITCHWALL_SCRIPT_PATH = `${App.configDir}/scripts/color_generation/switchwall.sh`; +const WALLPAPER_ZOOM_SCALE = 1.25; // For scrolling when we switch workspace +const MAX_WORKSPACES = 10; + +const WALLPAPER_OFFSCREEN_X = (WALLPAPER_ZOOM_SCALE - 1) * SCREEN_WIDTH; +const WALLPAPER_OFFSCREEN_Y = (WALLPAPER_ZOOM_SCALE - 1) * SCREEN_HEIGHT; + +function clamp(x, min, max) { + return Math.min(Math.max(x, min), max); +} + +export default (monitor = 0) => { + const wallpaperImage = Widget.DrawingArea({ + attribute: { + pixbuf: undefined, + workspace: 1, + sideleft: 0, + sideright: 0, + updatePos: (self) => { + self.setCss(`font-size: ${self.attribute.workspace - self.attribute.sideleft + self.attribute.sideright}px;`) + }, + }, + className: 'bg-wallpaper-transition', + setup: (self) => { + self.set_size_request(SCREEN_WIDTH, SCREEN_HEIGHT); + self + // TODO: reduced updates using timeouts to reduce lag + // .hook(Hyprland.active.workspace, (self) => { + // self.attribute.workspace = Hyprland.active.workspace.id + // self.attribute.updatePos(self); + // }) + // .hook(App, (box, name, visible) => { // Update on open + // if (self.attribute[name] === undefined) return; + // self.attribute[name] = (visible ? 1 : 0); + // self.attribute.updatePos(self); + // }) + .on('draw', (self, cr) => { + if (!self.attribute.pixbuf) return; + const styleContext = self.get_style_context(); + const workspace = styleContext.get_property('font-size', Gtk.StateFlags.NORMAL); + // Draw + Gdk.cairo_set_source_pixbuf(cr, self.attribute.pixbuf, + -(WALLPAPER_OFFSCREEN_X / (MAX_WORKSPACES + 1) * (clamp(workspace, 0, MAX_WORKSPACES + 1))), + -WALLPAPER_OFFSCREEN_Y / 2); + cr.paint(); + }) + .hook(Wallpaper, (self) => { + const wallPath = Wallpaper.get(monitor); + if (!wallPath || wallPath === "") return; + self.attribute.pixbuf = GdkPixbuf.Pixbuf.new_from_file(wallPath); + + const scale_x = SCREEN_WIDTH * WALLPAPER_ZOOM_SCALE / self.attribute.pixbuf.get_width(); + const scale_y = SCREEN_HEIGHT * WALLPAPER_ZOOM_SCALE / self.attribute.pixbuf.get_height(); + const scale_factor = Math.max(scale_x, scale_y); + + self.attribute.pixbuf = self.attribute.pixbuf.scale_simple( + Math.round(self.attribute.pixbuf.get_width() * scale_factor), + Math.round(self.attribute.pixbuf.get_height() * scale_factor), + GdkPixbuf.InterpType.BILINEAR + ); + self.queue_draw(); + }, 'updated'); + ; + } + , + }); + const wallpaperPrompt = Box({ + hpack: 'center', + vpack: 'center', + vertical: true, + className: 'spacing-v-10', + children: [ + Label({ + hpack: 'center', + justification: 'center', + className: 'txt-large', + label: `No wallpaper loaded.\nAn image ≥ ${SCREEN_WIDTH * WALLPAPER_ZOOM_SCALE} × ${SCREEN_HEIGHT * WALLPAPER_ZOOM_SCALE} is recommended.`, + }), + Button({ + hpack: 'center', + className: 'btn-primary', + label: `Select one`, + setup: setupCursorHover, + onClicked: (self) => Utils.execAsync([SWITCHWALL_SCRIPT_PATH]), + }), + ] + }); + const stack = Stack({ + transition: 'crossfade', + transitionDuration: 180, + items: [ + ['image', wallpaperImage], + ['prompt', wallpaperPrompt], + ], + setup: (self) => self + .hook(Wallpaper, (self) => { + const wallPath = Wallpaper.get(monitor); + self.shown = ((wallPath && wallPath != "") ? 'image' : 'prompt'); + }, 'updated') + , + }) + return stack; + // return wallpaperImage; +} diff --git a/.config/ags/widgets/indicators/colorscheme.js b/.config/ags/widgets/indicators/colorscheme.js index 3419e92b6..54ecef8ad 100644 --- a/.config/ags/widgets/indicators/colorscheme.js +++ b/.config/ags/widgets/indicators/colorscheme.js @@ -16,7 +16,7 @@ const ColorBox = ({ ] }) -const colorschemeContent = Box({ +const ColorschemeContent = () => Box({ className: 'osd-colorscheme spacing-v-5', vertical: true, hpack: 'center', @@ -44,7 +44,7 @@ const colorschemeContent = Box({ export default () => Widget.Revealer({ transition: 'slide_down', transitionDuration: 200, - child: colorschemeContent, + child: ColorschemeContent(), setup: (self) => self.hook(showColorScheme, (revealer) => { revealer.revealChild = showColorScheme.value; }), diff --git a/.config/ags/widgets/indicators/indicatorvalues.js b/.config/ags/widgets/indicators/indicatorvalues.js index 11d5f1474..2b2980f63 100644 --- a/.config/ags/widgets/indicators/indicatorvalues.js +++ b/.config/ags/widgets/indicators/indicatorvalues.js @@ -1,85 +1,87 @@ // This file is for brightness/volume indicators import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import Audio from 'resource:///com/github/Aylur/ags/service/audio.js'; -const { Box, Label, ProgressBar, Revealer } = Widget; +const { Box, Label, ProgressBar } = Widget; import { MarginRevealer } from '../../lib/advancedwidgets.js'; import Brightness from '../../services/brightness.js'; import Indicator from '../../services/indicator.js'; -const OsdValue = (name, labelSetup, progressSetup, props = {}) => Box({ // Volume - ...props, - vertical: true, - className: 'osd-bg osd-value', - hexpand: true, - children: [ - Box({ - vexpand: true, - children: [ - Label({ - xalign: 0, yalign: 0, hexpand: true, - className: 'osd-label', - label: `${name}`, - }), - Label({ - hexpand: false, className: 'osd-value-txt', - setup: labelSetup, - }), - ] - }), - ProgressBar({ - className: 'osd-progress', - hexpand: true, - vertical: false, - setup: progressSetup, - }) - ], -}); +const OsdValue = (name, labelSetup, progressSetup, props = {}) => { + const valueName = Label({ + xalign: 0, yalign: 0, hexpand: true, + className: 'osd-label', + label: `${name}`, + }); + const valueNumber = Label({ + hexpand: false, className: 'osd-value-txt', + setup: labelSetup, + }); + return Box({ // Volume + ...props, + vertical: true, + hexpand: true, + className: 'osd-bg osd-value', + attribute: { + 'disable': () => { + valueNumber.label = '󰖭'; + } + }, + children: [ + Box({ + vexpand: true, + children: [ + valueName, + valueNumber, + ] + }), + ProgressBar({ + className: 'osd-progress', + hexpand: true, + vertical: false, + setup: progressSetup, + }) + ], + }); +} -const brightnessIndicator = OsdValue('Brightness', - (self) => self - .hook(Brightness, self => { +export default () => { + const brightnessIndicator = OsdValue('Brightness', + (self) => self.hook(Brightness, self => { self.label = `${Math.round(Brightness.screen_value * 100)}`; - }, 'notify::screen-value') - , - (self) => self - .hook(Brightness, (progress) => { + }, 'notify::screen-value'), + (self) => self.hook(Brightness, (progress) => { const updateValue = Brightness.screen_value; progress.value = updateValue; - }, 'notify::screen-value') - , -) + }, 'notify::screen-value'), + ) -const volumeIndicator = OsdValue('Volume', - (self) => self - .hook(Audio, (label) => { + const volumeIndicator = OsdValue('Volume', + (self) => self.hook(Audio, (label) => { label.label = `${Math.round(Audio.speaker?.volume * 100)}`; - }) - , - (self) => self - .hook(Audio, (progress) => { + }), + (self) => self.hook(Audio, (progress) => { const updateValue = Audio.speaker?.volume; if (!isNaN(updateValue)) progress.value = updateValue; + }), + ); + return MarginRevealer({ + transition: 'slide_down', + showClass: 'osd-show', + hideClass: 'osd-hide', + extraSetup: (self) => self + .hook(Indicator, (revealer, value) => { + if (value > -1) revealer.attribute.show(); + else revealer.attribute.hide(); + }, 'popup') + , + child: Box({ + hpack: 'center', + vertical: false, + className: 'spacing-h--10', + children: [ + brightnessIndicator, + volumeIndicator, + ] }) - , -); - -export default () => MarginRevealer({ - transition: 'slide_down', - showClass: 'osd-show', - hideClass: 'osd-hide', - extraSetup: (self) => self - .hook(Indicator, (revealer, value) => { - if (value > -1) revealer.attribute.show(); - else revealer.attribute.hide(); - }, 'popup') - , - child: Box({ - hpack: 'center', - vertical: false, - className: 'spacing-h--10', - children: [ - brightnessIndicator, - volumeIndicator, - ] - }) -}); \ No newline at end of file + }); +} \ No newline at end of file diff --git a/.config/ags/widgets/indicators/main.js b/.config/ags/widgets/indicators/main.js index b7aa55900..698594768 100644 --- a/.config/ags/widgets/indicators/main.js +++ b/.config/ags/widgets/indicators/main.js @@ -1,11 +1,11 @@ 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'; -export default (monitor) => Widget.Window({ +export default (monitor = 0) => Widget.Window({ name: `indicator${monitor}`, monitor, className: 'indicator', @@ -23,7 +23,7 @@ export default (monitor) => 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 be51cc963..1ae05ae8f 100644 --- a/.config/ags/widgets/indicators/musiccontrols.js +++ b/.config/ags/widgets/indicators/musiccontrols.js @@ -32,17 +32,13 @@ function isRealPlayer(player) { ); } -export const getPlayer = (name = PREFERRED_PLAYER) => { - return Mpris.getPlayer(name) || Mpris.players[0] || null; -} - +export const getPlayer = (name = PREFERRED_PLAYER) => Mpris.getPlayer(name) || Mpris.players[0] || null; function lengthStr(length) { const min = Math.floor(length / 60); const sec = Math.floor(length % 60); const sec0 = sec < 10 ? '0' : ''; return `${min}:${sec0}${sec}`; } - function fileExists(filePath) { let file = Gio.File.new_for_path(filePath); return file.query_exists(null); @@ -54,17 +50,11 @@ function detectMediaSource(link) { return '󰈹 Firefox' return "󰈣 File"; } - // Remove protocol if present let url = link.replace(/(^\w+:|^)\/\//, ''); - // Extract the domain name let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1]; - - if (domain == 'ytimg.com') - return '󰗃 Youtube'; - if (domain == 'discordapp.net') - return '󰙯 Discord'; - if (domain == 'sndcdn.com') - return '󰓀 SoundCloud'; + if (domain == 'ytimg.com') return '󰗃 Youtube'; + if (domain == 'discordapp.net') return '󰙯 Discord'; + if (domain == 'sndcdn.com') return '󰓀 SoundCloud'; return domain; } @@ -72,15 +62,20 @@ const DEFAULT_MUSIC_FONT = 'Gabarito, sans-serif'; function getTrackfont(player) { const title = player.trackTitle; const artists = player.trackArtists.join(' '); - if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo')) return 'Chakra Petch'; // Rigid square replacement - if (title.includes('東方')) return 'Crimson Text, serif'; // Serif for Touhou stuff + if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo')) + return 'Chakra Petch'; // Rigid square replacement + if (title.includes('東方')) + return 'Crimson Text, serif'; // Serif for Touhou stuff return DEFAULT_MUSIC_FONT; } function trimTrackTitle(title) { - var cleanedTitle = title; - cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning - cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD] - return cleanedTitle.trim(); + if(!title) return ''; + const cleanRegexes = [ + /【[^】]*】/, // Touhou n weeb stuff + /\[FREE DOWNLOAD\]/, // F-777 + ]; + cleanRegexes.forEach((expr) => title.replace(expr, '')); + return title; } const TrackProgress = ({ player, ...rest }) => { diff --git a/.config/ags/widgets/indicators/notificationpopups.js b/.config/ags/widgets/indicators/notificationpopups.js index 7ca3e8aea..f5c0b3675 100644 --- a/.config/ags/widgets/indicators/notificationpopups.js +++ b/.config/ags/widgets/indicators/notificationpopups.js @@ -4,37 +4,7 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notification const { Box } = Widget; import Notification from '../../lib/notification.js'; -const PopupNotification = (notifObject) => Widget.Box({ - homogeneous: true, - children: [ - Widget.EventBox({ - onHoverLost: () => { - notifObject.dismiss(); - }, - child: Widget.Revealer({ - revealChild: true, - child: Widget.Box({ - children: [Notification({ - notifObject: notifObject, - isPopup: true, - props: { hpack: 'fill' }, - })], - }), - }) - }) - ] -}) - -const naiveNotifPopupList = Widget.Box({ - vertical: true, - className: 'spacing-v-5', - setup: (self) => self.hook(Notifications, (box) => { - box.children = Notifications.popups.reverse() - .map(notifItem => PopupNotification(notifItem)); - }), -}) - -const notifPopupList = Box({ +export default () => Box({ vertical: true, className: 'osd-notifs spacing-v-5-revealer', attribute: { @@ -72,5 +42,3 @@ const notifPopupList = Box({ .hook(Notifications, (box, id) => box.attribute.dismiss(box, id, true), 'closed') , }); - -export default () => notifPopupList; \ No newline at end of file diff --git a/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js b/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js index 75080861b..e77b91788 100644 --- a/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js +++ b/.config/ags/widgets/onscreenkeyboard/onscreenkeyboard.js @@ -4,7 +4,6 @@ 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'; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; const { Box, EventBox, Button, Revealer } = Widget; const { execAsync, exec } = Utils; import { MaterialIcon } from '../../lib/materialicon.js'; @@ -19,7 +18,7 @@ execAsync(`ydotoold`).catch(print); // Start ydotool daemon function releaseAllKeys() { const keycodes = Array.from(Array(249).keys()); execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)]) - .then(console.log('Released all keys')) + .then(console.log('[OSK] Released all keys')) .catch(print); } var modsPressed = false; @@ -146,22 +145,32 @@ const keyboardWindow = Box({ const gestureEvBox = EventBox({ child: keyboardWindow }) const gesture = Gtk.GestureDrag.new(gestureEvBox); -gesture.connect('drag-begin', () => { - Hyprland.sendMessage('j/cursorpos').then((out) => { - gesture.startY = JSON.parse(out).y; - }).catch(print); +gesture.connect('drag-begin', async () => { + try { + const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default; + Hyprland.sendMessage('j/cursorpos').then((out) => { + gesture.startY = JSON.parse(out).y; + }).catch(print); + } catch { + return; + } }); -gesture.connect('drag-update', () => { - Hyprland.sendMessage('j/cursorpos').then((out) => { - const currentY = JSON.parse(out).y; - const offset = gesture.startY - currentY; +gesture.connect('drag-update', async () => { + try { + const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default; + Hyprland.sendMessage('j/cursorpos').then((out) => { + const currentY = JSON.parse(out).y; + const offset = gesture.startY - currentY; - if (offset > 0) return; + if (offset > 0) return; - keyboardWindow.setCss(` + keyboardWindow.setCss(` margin-bottom: ${offset}px; `); - }).catch(print); + }).catch(print); + } catch { + return; + } }); gesture.connect('drag-end', () => { var offset = gesture.get_offset()[2]; diff --git a/.config/ags/widgets/overview/actions.js b/.config/ags/widgets/overview/actions.js index c5a429f53..766cf4542 100644 --- a/.config/ags/widgets/overview/actions.js +++ b/.config/ags/widgets/overview/actions.js @@ -6,7 +6,6 @@ function moveClientToWorkspace(address, workspace) { } export function dumpToWorkspace(from, to) { - console.log('dump', from, to); if (from == to) return; Hyprland.clients.forEach(client => { if (client.workspace.id == from) { diff --git a/.config/ags/widgets/overview/miscfunctions.js b/.config/ags/widgets/overview/miscfunctions.js index 42e2572f3..2ca0bf954 100644 --- a/.config/ags/widgets/overview/miscfunctions.js +++ b/.config/ags/widgets/overview/miscfunctions.js @@ -66,9 +66,9 @@ export function execAndClose(command, terminal) { execAsync(command).catch(print); } -export function startsWithNumber(str) { - var pattern = /^\d/; - return pattern.test(str); +export function couldBeMath(str) { + const regex = /^[0-9.+*/-]/; + return regex.test(str); } export function expandTilde(path) { diff --git a/.config/ags/widgets/overview/overview_hyprland.js b/.config/ags/widgets/overview/overview_hyprland.js index e3452afe3..7f3d6d1ad 100644 --- a/.config/ags/widgets/overview/overview_hyprland.js +++ b/.config/ags/widgets/overview/overview_hyprland.js @@ -1,6 +1,7 @@ const { Gdk, Gtk } = imports.gi; 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'; import Widget from 'resource:///com/github/Aylur/ags/widget.js'; import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; @@ -14,6 +15,8 @@ const OVERVIEW_WS_NUM_SCALE = 0.09; const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07; const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)]; +const overviewTick = Variable(false); + function truncateTitle(str) { let lastDash = -1; let found = -1; // 0: em dash, 1: en dash, 2: minus, 3: vertical bar, 4: middle dot @@ -213,6 +216,7 @@ const workspace = index => { 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, @@ -275,18 +279,19 @@ const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => }).catch(print); } }, - setup: (box) => { - box - // .hook(Hyprland, (box, name, data) => { // idk, does this make it lag occasionally? - // if (["changefloatingmode", "movewindow"].includes(name)) - // box.attribute.update(box); - // }, 'event') - .hook(Hyprland, (box) => box.attribute.update(box), 'client-added') - .hook(Hyprland, (box) => box.attribute.update(box), 'client-removed') - .hook(App, (box, name, visible) => { // Update on open - if (name == 'overview' && visible) box.attribute.update(box); - }) - }, + setup: (box) => box + .hook(overviewTick, (box) => box.attribute.update(box)) + // .hook(Hyprland, (box, name, data) => { // idk, does this make it lag occasionally? + // console.log(name) + // if (["changefloatingmode", "movewindow"].includes(name)) + // box.attribute.update(box); + // }, 'event') + .hook(Hyprland, (box) => box.attribute.update(box), 'client-added') + .hook(Hyprland, (box) => box.attribute.update(box), 'client-removed') + .hook(App, (box, name, visible) => { // Update on open + if (name == 'overview' && visible) box.attribute.update(box); + }) + , }); diff --git a/.config/ags/widgets/overview/searchbuttons.js b/.config/ags/widgets/overview/searchbuttons.js index 475cbaf9f..bd43ca819 100644 --- a/.config/ags/widgets/overview/searchbuttons.js +++ b/.config/ags/widgets/overview/searchbuttons.js @@ -4,7 +4,7 @@ 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; import { searchItem } from './searchitem.js'; -import { execAndClose, startsWithNumber, launchCustomCommand } from './miscfunctions.js'; +import { execAndClose, couldBeMath, launchCustomCommand } from './miscfunctions.js'; export const DirectoryButton = ({ parentPath, name, type, icon }) => { const actionText = Widget.Revealer({ diff --git a/.config/ags/widgets/overview/windowcontent.js b/.config/ags/widgets/overview/windowcontent.js index b5d8b7295..e61250f54 100644 --- a/.config/ags/widgets/overview/windowcontent.js +++ b/.config/ags/widgets/overview/windowcontent.js @@ -5,7 +5,7 @@ import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; import Applications from 'resource:///com/github/Aylur/ags/service/applications.js'; const { execAsync, exec } = Utils; -import { execAndClose, expandTilde, hasUnterminatedBackslash, startsWithNumber, launchCustomCommand, ls } from './miscfunctions.js'; +import { execAndClose, expandTilde, hasUnterminatedBackslash, couldBeMath, launchCustomCommand, ls } from './miscfunctions.js'; import { CalculationResultButton, CustomCommandButton, DirectoryButton, DesktopEntryButton, ExecuteCommandButton, SearchButton @@ -39,7 +39,7 @@ const OptionalOverview = async () => { try { return (await import('./overview_hyprland.js')).default(); } catch { - return null; + return Widget.Box({}); // return (await import('./overview_hyprland.js')).default(); } }; @@ -104,7 +104,7 @@ export const SearchAndWindows = () => { const isAction = text.startsWith('>'); const isDir = (['/', '~'].includes(entry.text[0])); - if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a workaround + if (couldBeMath(text)) { // Eval on typing is dangerous, this is a workaround try { const fullResult = eval(text); // copy @@ -166,7 +166,7 @@ export const SearchAndWindows = () => { _appSearchResults = Applications.query(text); // Calculate - if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a small workaround. + if (couldBeMath(text)) { // Eval on typing is dangerous; this is a small workaround. try { const fullResult = eval(text); resultsBox.add(CalculationResultButton({ result: fullResult, text: text })); diff --git a/.config/ags/widgets/screencorners/main.js b/.config/ags/widgets/screencorners/main.js index 74e9dd52f..42a8a5283 100644 --- a/.config/ags/widgets/screencorners/main.js +++ b/.config/ags/widgets/screencorners/main.js @@ -1,43 +1,16 @@ -import Cairo from 'gi://cairo?version=1.0'; import Widget from 'resource:///com/github/Aylur/ags/widget.js'; -import { RoundedCorner } from "../../lib/roundedcorner.js"; +import { RoundedCorner, dummyRegion, enableClickthrough } from "../../lib/roundedcorner.js"; -const dummyRegion = new Cairo.Region(); -const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion); - -export const CornerTopleft = () => Widget.Window({ - name: 'cornertl', - layer: 'top', - anchor: ['top', 'left'], - exclusivity: 'normal', - visible: true, - child: RoundedCorner('topleft', { className: 'corner', }), - setup: enableClickthrough, -}); -export const CornerTopright = () => Widget.Window({ - name: 'cornertr', - layer: 'top', - anchor: ['top', 'right'], - exclusivity: 'normal', - visible: true, - child: RoundedCorner('topright', { className: 'corner', }), - setup: enableClickthrough, -}); -export const CornerBottomleft = () => Widget.Window({ - name: 'cornerbl', - layer: 'top', - anchor: ['bottom', 'left'], - exclusivity: 'ignore', - visible: true, - child: RoundedCorner('bottomleft', { className: 'corner-black', }), - setup: enableClickthrough, -}); -export const CornerBottomright = () => Widget.Window({ - name: 'cornerbr', - layer: 'top', - anchor: ['bottom', 'right'], - exclusivity: 'ignore', - visible: true, - child: RoundedCorner('bottomright', { className: 'corner-black', }), - setup: enableClickthrough, -}); +export default (monitor = 0, where = 'bottom left') => { + const positionString = where.replace(/\s/, ""); // remove space + return Widget.Window({ + monitor, + name: `corner${positionString}${monitor}`, + layer: 'overlay', + anchor: where.split(' '), + exclusivity: 'ignore', + visible: true, + child: RoundedCorner(positionString, { className: 'corner-black', }), + setup: enableClickthrough, + }); +} \ No newline at end of file diff --git a/.config/ags/widgets/sideleft/apis/chatgpt_chatmessage.js b/.config/ags/widgets/sideleft/apis/ai_chatmessage.js similarity index 98% rename from .config/ags/widgets/sideleft/apis/chatgpt_chatmessage.js rename to .config/ags/widgets/sideleft/apis/ai_chatmessage.js index a8cb84e54..22f32f64b 100644 --- a/.config/ags/widgets/sideleft/apis/chatgpt_chatmessage.js +++ b/.config/ags/widgets/sideleft/apis/ai_chatmessage.js @@ -223,7 +223,7 @@ const MessageContent = (content) => { return contentBox; } -export const ChatMessage = (message, scrolledWindow) => { +export const ChatMessage = (message, modelName = 'Model') => { const messageContentBox = MessageContent(message.content); const thisMessage = Box({ className: 'sidebar-chat-message', @@ -241,7 +241,8 @@ export const ChatMessage = (message, scrolledWindow) => { xalign: 0, className: 'txt txt-bold sidebar-chat-name', wrap: true, - label: (message.role == 'user' ? USERNAME : 'ChatGPT'), + useMarkup: true, + label: (message.role == 'user' ? USERNAME : modelName), }), messageContentBox, ], diff --git a/.config/ags/widgets/sideleft/apis/chatgpt.js b/.config/ags/widgets/sideleft/apis/chatgpt.js index 745ab5c46..90d538ed4 100644 --- a/.config/ags/widgets/sideleft/apis/chatgpt.js +++ b/.config/ags/widgets/sideleft/apis/chatgpt.js @@ -8,63 +8,75 @@ const { execAsync, exec } = Utils; import ChatGPT from '../../../services/chatgpt.js'; import { MaterialIcon } from "../../../lib/materialicon.js"; import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js"; -import { SystemMessage, ChatMessage } from "./chatgpt_chatmessage.js"; +import { SystemMessage, ChatMessage } from "./ai_chatmessage.js"; import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js'; import { markdownTest } from '../../../lib/md2pango.js'; import { MarginRevealer } from '../../../lib/advancedwidgets.js'; -export const chatGPTTabIcon = Box({ +Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets`); + +export const chatGPTTabIcon = Icon({ hpack: 'center', className: 'sidebar-chat-apiswitcher-icon', - homogeneous: true, - children: [ - MaterialIcon('forum', 'norm'), - ], + 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 = Box({ - vertical: true, - className: 'spacing-v-15', - children: [ - Icon({ - hpack: 'center', - className: 'sidebar-chat-welcome-logo', - icon: `${App.configDir}/assets/openai-logomark.svg`, - setup: (self) => Utils.timeout(1, () => { - const styleContext = self.get_style_context(); - const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL); - const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); - self.size = Math.max(width, height, 1) * 116 / 180; // Why such a specific proportion? See https://openai.com/brand#logos - }) - }), - Label({ - className: 'txt txt-title-small sidebar-chat-welcome-txt', - wrap: true, - justify: Gtk.Justification.CENTER, - label: 'ChatGPT', - }), - Box({ - className: 'spacing-h-5', - hpack: 'center', - children: [ - Label({ - className: 'txt-smallie txt-subtext', - wrap: true, - justify: Gtk.Justification.CENTER, - label: 'Powered by OpenAI', - }), - Button({ - className: 'txt-subtext txt-norm icon-material', - label: 'info', - tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.', - setup: setupCursorHoverInfo, - }), - ] - }), - ] -}) +const ChatGPTInfo = () => { + const openAiLogo = Icon({ + 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, + className: 'spacing-v-15', + children: [ + openAiLogo, + Label({ + className: 'txt txt-title-small sidebar-chat-welcome-txt', + wrap: true, + justify: Gtk.Justification.CENTER, + label: 'Assistant (ChatGPT 3.5)', + }), + Box({ + className: 'spacing-h-5', + hpack: 'center', + children: [ + Label({ + className: 'txt-smallie txt-subtext', + wrap: true, + justify: Gtk.Justification.CENTER, + label: 'Powered by OpenAI', + }), + Button({ + className: 'txt-subtext txt-norm icon-material', + label: 'info', + tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.', + setup: setupCursorHoverInfo, + }), + ] + }), + ] + }); +} -export const chatGPTSettings = MarginRevealer({ +export const ChatGPTSettings = () => MarginRevealer({ transition: 'slide_down', revealChild: true, extraSetup: (self) => self @@ -89,7 +101,7 @@ export const chatGPTSettings = MarginRevealer({ { value: 0.50, name: 'Balanced', }, { value: 1.00, name: 'Creative', }, ], - initIndex: 1, + initIndex: 2, onChange: (value, name) => { ChatGPT.temperature = value; }, @@ -111,8 +123,8 @@ export const chatGPTSettings = MarginRevealer({ }), ConfigToggle({ icon: 'description', - name: 'Assistant prompt', - desc: 'Tells ChatGPT\n 1. It\'s a sidebar assistant on Linux\n 2. Be short and concise\n 3. Use markdown features extensively\nLeave this off for a vanilla ChatGPT experience.', + name: 'Enhancements', + desc: 'Tells ChatGPT:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points', initValue: ChatGPT.assistantPrompt, onChange: (self, newValue) => { ChatGPT.assistantPrompt = newValue; @@ -124,7 +136,7 @@ export const chatGPTSettings = MarginRevealer({ }) }); -export const openaiApiKeyInstructions = Box({ +export const OpenaiApiKeyInstructions = () => Box({ homogeneous: true, children: [Revealer({ transition: 'slide_down', @@ -150,7 +162,7 @@ export const openaiApiKeyInstructions = Box({ })] }); -export const chatGPTWelcome = Box({ +const chatGPTWelcome = Box({ vexpand: true, homogeneous: true, child: Box({ @@ -158,9 +170,9 @@ export const chatGPTWelcome = Box({ vpack: 'center', vertical: true, children: [ - chatGPTInfo, - openaiApiKeyInstructions, - chatGPTSettings, `` + ChatGPTInfo(), + OpenaiApiKeyInstructions(), + ChatGPTSettings(), ] }) }); @@ -172,7 +184,7 @@ export const chatContent = Box({ .hook(ChatGPT, (box, id) => { const message = ChatGPT.messages[id]; if (!message) return; - box.add(ChatMessage(message, chatGPTView)) + box.add(ChatMessage(message, 'ChatGPT')) }, 'newMsg') , }); diff --git a/.config/ags/widgets/sideleft/apis/gemini.js b/.config/ags/widgets/sideleft/apis/gemini.js new file mode 100644 index 000000000..6f9c68333 --- /dev/null +++ b/.config/ags/widgets/sideleft/apis/gemini.js @@ -0,0 +1,276 @@ +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 { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget; +const { execAsync, exec } = Utils; +import Gemini from '../../../services/gemini.js'; +import { MaterialIcon } from "../../../lib/materialicon.js"; +import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js"; +import { SystemMessage, ChatMessage } from "./ai_chatmessage.js"; +import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js'; +import { markdownTest } from '../../../lib/md2pango.js'; +import { MarginRevealer } from '../../../lib/advancedwidgets.js'; + +Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets`); +const MODEL_NAME = `Gemini`; + +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 = () => { + const geminiLogo = Icon({ + 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, + className: 'spacing-v-15', + children: [ + geminiLogo, + Label({ + className: 'txt txt-title-small sidebar-chat-welcome-txt', + wrap: true, + justify: Gtk.Justification.CENTER, + label: 'Assistant (Gemini Pro)', + }), + Box({ + className: 'spacing-h-5', + hpack: 'center', + children: [ + Label({ + className: 'txt-smallie txt-subtext', + wrap: true, + justify: Gtk.Justification.CENTER, + label: 'Powered by Google', + }), + Button({ + className: 'txt-subtext txt-norm icon-material', + label: 'info', + tooltipText: 'Uses gemini-pro.\nNot affiliated, endorsed, or sponsored by Google.', + setup: setupCursorHoverInfo, + }), + ] + }), + ] + }); +} + +export const GeminiSettings = () => MarginRevealer({ + transition: 'slide_down', + revealChild: true, + extraSetup: (self) => self + .hook(Gemini, (self) => Utils.timeout(200, () => { + self.attribute.hide(); + }), 'newMsg') + .hook(Gemini, (self) => Utils.timeout(200, () => { + self.attribute.show(); + }), 'clear') + , + child: Box({ + vertical: true, + className: 'sidebar-chat-settings', + children: [ + ConfigSegmentedSelection({ + hpack: 'center', + icon: 'casino', + name: 'Randomness', + desc: 'Gemini\'s temperature value.\n Precise = 0\n Balanced = 0.5\n Creative = 1', + options: [ + { value: 0.00, name: 'Precise', }, + { value: 0.50, name: 'Balanced', }, + { value: 1.00, name: 'Creative', }, + ], + initIndex: 2, + onChange: (value, name) => { + Gemini.temperature = value; + }, + }), + ConfigGap({ vertical: true, size: 10 }), // Note: size can only be 5, 10, or 15 + Box({ + vertical: true, + hpack: 'fill', + className: 'sidebar-chat-settings-toggles', + children: [ + ConfigToggle({ + icon: 'description', + name: 'Enhancements', + desc: 'Tells Gemini:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points', + initValue: Gemini.assistantPrompt, + onChange: (self, newValue) => { + Gemini.assistantPrompt = newValue; + }, + }), + ] + }) + ] + }) +}); + +export const GoogleAiInstructions = () => Box({ + homogeneous: true, + children: [Revealer({ + transition: 'slide_down', + transitionDuration: 150, + setup: (self) => self + .hook(Gemini, (self, hasKey) => { + self.revealChild = (Gemini.key.length == 0); + }, 'hasKey') + , + child: Button({ + child: Label({ + useMarkup: true, + wrap: true, + className: 'txt sidebar-chat-welcome-txt', + justify: Gtk.Justification.CENTER, + label: 'A Google AI API key is required\nYou can grab one here, then enter it below' + }), + setup: setupCursorHover, + onClicked: () => { + Utils.execAsync(['bash', '-c', `xdg-open https://makersuite.google.com/app/apikey &`]); + } + }) + })] +}); + +const geminiWelcome = Box({ + vexpand: true, + homogeneous: true, + child: Box({ + className: 'spacing-v-15', + vpack: 'center', + vertical: true, + children: [ + GeminiInfo(), + GoogleAiInstructions(), + GeminiSettings(), + ] + }) +}); + +export const chatContent = Box({ + className: 'spacing-v-15', + vertical: true, + setup: (self) => self + .hook(Gemini, (box, id) => { + const message = Gemini.messages[id]; + if (!message) return; + box.add(ChatMessage(message, MODEL_NAME)) + }, 'newMsg') + , +}); + +const clearChat = () => { + Gemini.clear(); + const children = chatContent.get_children(); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + child.destroy(); + } +} + +export const geminiView = Scrollable({ + className: 'sidebar-chat-viewport', + vexpand: true, + child: Box({ + vertical: true, + children: [ + geminiWelcome, + chatContent, + ] + }), + setup: (scrolledWindow) => { + // Show scrollbar + scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); + const vScrollbar = scrolledWindow.get_vscrollbar(); + vScrollbar.get_style_context().add_class('sidebar-scrollbar'); + // Avoid click-to-scroll-widget-to-view behavior + Utils.timeout(1, () => { + const viewport = scrolledWindow.child; + viewport.set_focus_vadjustment(new Gtk.Adjustment(undefined)); + }) + // Always scroll to bottom with new content + const adjustment = scrolledWindow.get_vadjustment(); + adjustment.connect("changed", () => { + adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size()); + }) + } +}); + +const CommandButton = (command) => Button({ + className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small', + onClicked: () => sendMessage(command), + setup: setupCursorHover, + label: command, +}); + +export const geminiCommands = Box({ + className: 'spacing-h-5', + children: [ + Box({ hexpand: true }), + CommandButton('/key'), + CommandButton('/model'), + CommandButton('/clear'), + ] +}); + +export const sendMessage = (text) => { + // Check if text or API key is empty + if (text.length == 0) return; + if (Gemini.key.length == 0) { + Gemini.key = text; + chatContent.add(SystemMessage(`Key saved to\n\`${Gemini.keyPath}\``, 'API Key', geminiView)); + text = ''; + return; + } + // Commands + if (text.startsWith('/')) { + if (text.startsWith('/clear')) clearChat(); + else if (text.startsWith('/model')) chatContent.add(SystemMessage(`Currently using \`${Gemini.modelName}\``, '/model', geminiView)) + else if (text.startsWith('/prompt')) { + const firstSpaceIndex = text.indexOf(' '); + const prompt = text.slice(firstSpaceIndex + 1); + if (firstSpaceIndex == -1 || prompt.length < 1) { + chatContent.add(SystemMessage(`Usage: \`/prompt MESSAGE\``, '/prompt', geminiView)) + } + else { + Gemini.addMessage('user', prompt) + } + } + else if (text.startsWith('/key')) { + const parts = text.split(' '); + if (parts.length == 1) chatContent.add(SystemMessage( + `Key stored in:\n\`${Gemini.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``, + '/key', + geminiView)); + else { + Gemini.key = parts[1]; + chatContent.add(SystemMessage(`Updated API Key at\n\`${Gemini.keyPath}\``, '/key', geminiView)); + } + } + else if (text.startsWith('/test')) + chatContent.add(SystemMessage(markdownTest, `Markdown test`, geminiView)); + else + chatContent.add(SystemMessage(`Invalid command.`, 'Error', geminiView)) + } + else { + Gemini.send(text); + } +} \ No newline at end of file diff --git a/.config/ags/widgets/sideleft/apis/waifu.js b/.config/ags/widgets/sideleft/apis/waifu.js index c77054dd6..c5a61531b 100644 --- a/.config/ags/widgets/sideleft/apis/waifu.js +++ b/.config/ags/widgets/sideleft/apis/waifu.js @@ -5,7 +5,7 @@ const { Box, Button, Label, Overlay, Revealer, Scrollable, Stack } = Widget; const { execAsync, exec } = Utils; import { MaterialIcon } from "../../../lib/materialicon.js"; import { MarginRevealer } from '../../../lib/advancedwidgets.js'; -import { setupCursorHover } from "../../../lib/cursorhover.js"; +import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js"; import WaifuService from '../../../services/waifus.js'; async function getImageViewerApp(preferredApp) { @@ -162,7 +162,8 @@ const WaifuImage = (taglist) => { blockImage.set_size_request(widgetWidth, widgetHeight); const showImage = () => { downloadState.shown = 'done'; - const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(thisBlock.attribute.imagePath, widgetWidth, widgetHeight, false); + const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(thisBlock.attribute.imagePath, widgetWidth, widgetHeight); + // const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(thisBlock.attribute.imagePath, widgetWidth, widgetHeight, false); blockImage.set_size_request(widgetWidth, widgetHeight); blockImage.connect("draw", (widget, cr) => { @@ -220,10 +221,62 @@ const WaifuImage = (taglist) => { return thisBlock; } +const WaifuInfo = () => { + const waifuLogo = Label({ + hpack: 'center', + className: 'sidebar-chat-welcome-logo', + label: 'photo_library', + }) + return Box({ + vertical: true, + vexpand: true, + className: 'spacing-v-15', + children: [ + waifuLogo, + Label({ + className: 'txt txt-title-small sidebar-chat-welcome-txt', + wrap: true, + justify: Gtk.Justification.CENTER, + label: 'Waifus', + }), + Box({ + className: 'spacing-h-5', + hpack: 'center', + children: [ + Label({ + className: 'txt-smallie txt-subtext', + wrap: true, + justify: Gtk.Justification.CENTER, + label: 'Powered by waifu.im', + }), + Button({ + className: 'txt-subtext txt-norm icon-material', + label: 'info', + tooltipText: 'A free Waifu API. An alternative to waifu.pics.', + setup: setupCursorHoverInfo, + }), + ] + }), + ] + }); +} + +const waifuWelcome = Box({ + vexpand: true, + homogeneous: true, + child: Box({ + className: 'spacing-v-15', + vpack: 'center', + vertical: true, + children: [ + WaifuInfo(), + ] + }) +}); + const waifuContent = Box({ className: 'spacing-v-15', vertical: true, - vexpand: true, attribute: { 'map': new Map(), }, @@ -251,6 +304,7 @@ export const waifuView = Scrollable({ child: Box({ vertical: true, children: [ + waifuWelcome, waifuContent, ] }), @@ -327,6 +381,21 @@ const clearChat = () => { } } +const DummyTag = (width, height, url, color = '#9392A6') => { + return { // Needs timeout or inits won't make it + status: 200, + url: url, + extension: '', + signature: 0, + source: url, + dominant_color: color, + is_nsfw: false, + width: width, + height: height, + tags: ['/test'], + }; +} + export const sendMessage = (text) => { // Do something on send // Commands @@ -335,20 +404,20 @@ export const sendMessage = (text) => { else if (text.startsWith('/test')) { const newImage = WaifuImage(['/test']); waifuContent.add(newImage); - Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update({ // Needs timeout or inits won't make it - // This is an image uploaded to my github repo - status: 200, - url: 'https://picsum.photos/400/600', - extension: '', - signature: 0, - source: 'https://picsum.photos/400/600', - dominant_color: '#9392A6', - is_nsfw: false, - width: 300, - height: 200, - tags: ['/test'], - }, true)); + Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update( + DummyTag(300, 200, 'https://picsum.photos/600/400'), + true + )); } + else if (text.startsWith('/chino')) { + const newImage = WaifuImage(['/chino']); + waifuContent.add(newImage); + Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update( + DummyTag(300, 400, 'https://chino.pages.dev/chino', '#B2AEF3'), + true + )); + } + } else WaifuService.fetch(text); } \ No newline at end of file diff --git a/.config/ags/widgets/sideleft/apiwidgets.js b/.config/ags/widgets/sideleft/apiwidgets.js index 475bd496f..5963a12c4 100644 --- a/.config/ags/widgets/sideleft/apiwidgets.js +++ b/.config/ags/widgets/sideleft/apiwidgets.js @@ -1,22 +1,32 @@ 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 { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget; +const { Box, Button, CenterBox, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget; const { execAsync, exec } = Utils; import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js"; // APIs import ChatGPT from '../../services/chatgpt.js'; +import 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 APIS = [ { - name: 'ChatGPT', + name: 'Assistant (ChatGPT 3.5)', sendCommand: chatGPTSendMessage, contentWidget: chatGPTView, commandBar: chatGPTCommands, tabIcon: chatGPTTabIcon, - placeholderText: 'Message ChatGPT', + placeholderText: 'Message ChatGPT...', + }, + { + name: 'Assistant (Gemini Pro)', + sendCommand: geminiSendMessage, + contentWidget: geminiView, + commandBar: geminiCommands, + tabIcon: geminiTabIcon, + placeholderText: 'Message Gemini...', }, { name: 'Waifus', @@ -35,8 +45,12 @@ export const chatEntry = Entry({ hexpand: true, setup: (self) => self .hook(ChatGPT, (self) => { - if (APIS[currentApiId].name != 'ChatGPT') return; - self.placeholderText = (ChatGPT.key.length > 0 ? 'Ask a question...' : 'Enter OpenAI API Key...'); + if (APIS[currentApiId].name != 'Assistant (ChatGPT 3.5)') return; + self.placeholderText = (ChatGPT.key.length > 0 ? 'Message ChatGPT...' : 'Enter OpenAI API Key...'); + }, 'hasKey') + .hook(Gemini, (self) => { + if (APIS[currentApiId].name != 'Assistant (Gemini Pro)') return; + self.placeholderText = (Gemini.key.length > 0 ? 'Message Gemini...' : 'Enter Google AI API Key...'); }, 'hasKey') , onChange: (entry) => { @@ -86,22 +100,27 @@ function switchToTab(id) { chatEntry.placeholderText = APIS[id].placeholderText, currentApiId = id; } -const apiSwitcher = Box({ - homogeneous: true, - children: [ - Box({ - className: 'sidebar-chat-apiswitcher spacing-h-5', - hpack: 'center', - children: APIS.map((api, id) => Button({ - child: api.tabIcon, - tooltipText: api.name, - setup: setupCursorHover, - onClicked: () => { - switchToTab(id); - } - })), - }), - ] + +const apiSwitcher = CenterBox({ + centerWidget: Box({ + className: 'sidebar-chat-apiswitcher spacing-h-5', + hpack: 'center', + children: APIS.map((api, id) => Button({ + child: api.tabIcon, + tooltipText: api.name, + setup: setupCursorHover, + onClicked: () => { + switchToTab(id); + } + })), + }), + endWidget: Button({ + hpack: 'end', + className: 'txt-subtext txt-norm icon-material', + label: 'lightbulb', + tooltipText: 'Use PageUp/PageDown to switch between API pages', + setup: setupCursorHoverInfo, + }), }) export default Widget.Box({ diff --git a/.config/ags/widgets/sideleft/main.js b/.config/ags/widgets/sideleft/main.js index 1e78578ad..d942b6c3a 100644 --- a/.config/ags/widgets/sideleft/main.js +++ b/.config/ags/widgets/sideleft/main.js @@ -5,7 +5,7 @@ export default () => PopupWindow({ focusable: true, anchor: ['left', 'top', 'bottom'], name: 'sideleft', - // exclusivity: 'exclusive', + layer: 'top', showClassName: 'sideleft-show', hideClassName: 'sideleft-hide', child: SidebarLeft(), diff --git a/.config/ags/widgets/sideleft/sideleft.js b/.config/ags/widgets/sideleft/sideleft.js index 278ba5838..fc1a6b699 100644 --- a/.config/ags/widgets/sideleft/sideleft.js +++ b/.config/ags/widgets/sideleft/sideleft.js @@ -102,22 +102,14 @@ const pinButton = Button({ self.toggleClassName('sidebar-pin-enabled', self.attribute.enabled); const sideleftWindow = App.getWindow('sideleft'); - const barWindow = App.getWindow('bar'); - const cornerTopLeftWindow = App.getWindow('cornertl'); const sideleftContent = sideleftWindow.get_children()[0].get_children()[0].get_children()[1]; sideleftContent.toggleClassName('sidebar-pinned', self.attribute.enabled); if (self.attribute.enabled) { - sideleftWindow.layer = 'bottom'; - barWindow.layer = 'bottom'; - cornerTopLeftWindow.layer = 'bottom'; sideleftWindow.exclusivity = 'exclusive'; } else { - sideleftWindow.layer = 'top'; - barWindow.layer = 'top'; - cornerTopLeftWindow.layer = 'top'; sideleftWindow.exclusivity = 'normal'; } }, diff --git a/.config/ags/widgets/sideleft/toolbox.js b/.config/ags/widgets/sideleft/toolbox.js index 1fbf71792..0c14beded 100644 --- a/.config/ags/widgets/sideleft/toolbox.js +++ b/.config/ags/widgets/sideleft/toolbox.js @@ -10,7 +10,7 @@ export default Scrollable({ child: Box({ vertical: true, children: [ - QuickScripts(), + // QuickScripts(), ] }) }); diff --git a/.config/ags/widgets/sideright/quicktoggles.js b/.config/ags/widgets/sideright/quicktoggles.js index 6deae3fb0..9ba354ddf 100644 --- a/.config/ags/widgets/sideright/quicktoggles.js +++ b/.config/ags/widgets/sideright/quicktoggles.js @@ -4,7 +4,6 @@ import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js'; import Network from 'resource:///com/github/Aylur/ags/service/network.js'; -import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; const { execAsync, exec } = Utils; import { BluetoothIndicator, NetworkIndicator } from "../../lib/statusicons.js"; import { setupCursorHover } from "../../lib/cursorhover.js"; @@ -12,7 +11,6 @@ import { MaterialIcon } from '../../lib/materialicon.js'; function expandTilde(path) { if (path.startsWith('~')) { - console.log(GLib.get_home_dir() + path.slice(1)); return GLib.get_home_dir() + path.slice(1); } else { return path; @@ -62,24 +60,30 @@ export const ToggleIconBluetooth = (props = {}) => Widget.Button({ ...props, }); -export const HyprToggleIcon = (icon, name, hyprlandConfigValue, props = {}) => Widget.Button({ - className: 'txt-small sidebar-iconbutton', - tooltipText: `${name}`, - onClicked: (button) => { - // Set the value to 1 - value - Utils.execAsync(`hyprctl -j getoption ${hyprlandConfigValue}`).then((result) => { - const currentOption = JSON.parse(result).int; - execAsync(['bash', '-c', `hyprctl keyword ${hyprlandConfigValue} ${1 - currentOption} &`]).catch(print); - button.toggleClassName('sidebar-button-active', currentOption == 0); - }).catch(print); - }, - child: MaterialIcon(icon, 'norm', { hpack: 'center' }), - setup: button => { - button.toggleClassName('sidebar-button-active', JSON.parse(Utils.exec(`hyprctl -j getoption ${hyprlandConfigValue}`)).int == 1); - setupCursorHover(button); - }, - ...props, -}) +export const HyprToggleIcon = async (icon, name, hyprlandConfigValue, props = {}) => { + try { + return Widget.Button({ + className: 'txt-small sidebar-iconbutton', + tooltipText: `${name}`, + onClicked: (button) => { + // Set the value to 1 - value + Utils.execAsync(`hyprctl -j getoption ${hyprlandConfigValue}`).then((result) => { + const currentOption = JSON.parse(result).int; + execAsync(['bash', '-c', `hyprctl keyword ${hyprlandConfigValue} ${1 - currentOption} &`]).catch(print); + button.toggleClassName('sidebar-button-active', currentOption == 0); + }).catch(print); + }, + child: MaterialIcon(icon, 'norm', { hpack: 'center' }), + setup: button => { + button.toggleClassName('sidebar-button-active', JSON.parse(Utils.exec(`hyprctl -j getoption ${hyprlandConfigValue}`)).int == 1); + setupCursorHover(button); + }, + ...props, + }) + } catch { + return null; + } +} export const ModuleNightLight = (props = {}) => Widget.Button({ // TODO: Make this work attribute: { @@ -104,31 +108,36 @@ export const ModuleNightLight = (props = {}) => Widget.Button({ // TODO: Make th ...props, }); -export const ModuleInvertColors = (props = {}) => Widget.Button({ - className: 'txt-small sidebar-iconbutton', - tooltipText: 'Color inversion', - onClicked: (button) => { - // const shaderPath = JSON.parse(exec('hyprctl -j getoption decoration:screen_shader')).str; - Hyprland.sendMessage('j/getoption decoration:screen_shader') - .then((output) => { - const shaderPath = JSON.parse(output)["str"].trim(); - console.log(output) - console.log(shaderPath) - if (shaderPath != "[[EMPTY]]" && shaderPath != "") { - execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader '[[EMPTY]]'`]).catch(print); - button.toggleClassName('sidebar-button-active', false); - } - else { - Hyprland.sendMessage(`j/keyword decoration:screen_shader ${expandTilde('~/.config/hypr/shaders/invert.frag')}`) - .catch(print); - button.toggleClassName('sidebar-button-active', true); - } - }) - }, - child: MaterialIcon('invert_colors', 'norm'), - setup: setupCursorHover, - ...props, -}) +export const ModuleInvertColors = async (props = {}) => { + try { + const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default; + return Widget.Button({ + className: 'txt-small sidebar-iconbutton', + tooltipText: 'Color inversion', + onClicked: (button) => { + // const shaderPath = JSON.parse(exec('hyprctl -j getoption decoration:screen_shader')).str; + Hyprland.sendMessage('j/getoption decoration:screen_shader') + .then((output) => { + const shaderPath = JSON.parse(output)["str"].trim(); + if (shaderPath != "[[EMPTY]]" && shaderPath != "") { + execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader '[[EMPTY]]'`]).catch(print); + button.toggleClassName('sidebar-button-active', false); + } + else { + Hyprland.sendMessage(`j/keyword decoration:screen_shader ${expandTilde('~/.config/hypr/shaders/invert.frag')}`) + .catch(print); + button.toggleClassName('sidebar-button-active', true); + } + }) + }, + child: MaterialIcon('invert_colors', 'norm'), + setup: setupCursorHover, + ...props, + }) + } catch { + return null; + }; +} export const ModuleIdleInhibitor = (props = {}) => Widget.Button({ // TODO: Make this work attribute: { @@ -173,9 +182,9 @@ export const ModuleEditIcon = (props = {}) => Widget.Button({ // TODO: Make this export const ModuleReloadIcon = (props = {}) => Widget.Button({ ...props, className: 'txt-small sidebar-iconbutton', - tooltipText: 'Reload Hyprland', + tooltipText: 'Reload Environment config', onClicked: () => { - execAsync(['bash', '-c', 'hyprctl reload &']); + execAsync(['bash', '-c', 'hyprctl reload || swaymsg reload &']); App.toggleWindow('sideright'); }, child: MaterialIcon('refresh', 'norm'), diff --git a/.config/ags/widgets/sideright/sideright.js b/.config/ags/widgets/sideright/sideright.js index ff5751744..6105ae0a8 100644 --- a/.config/ags/widgets/sideright/sideright.js +++ b/.config/ags/widgets/sideright/sideright.js @@ -45,10 +45,10 @@ const togglesBox = Widget.Box({ children: [ ToggleIconWifi(), ToggleIconBluetooth(), - HyprToggleIcon('mouse', 'Raw input', 'input:force_no_accel', {}), - HyprToggleIcon('front_hand', 'No touchpad while typing', 'input:touchpad:disable_while_typing', {}), + await HyprToggleIcon('mouse', 'Raw input', 'input:force_no_accel', {}), + await HyprToggleIcon('front_hand', 'No touchpad while typing', 'input:touchpad:disable_while_typing', {}), ModuleNightLight(), - ModuleInvertColors(), + await ModuleInvertColors(), ModuleIdleInhibitor(), ] })