forked from Shinonome/dots-hyprland
ags: sync
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="google-gemini-symbolic.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.77339804"
|
||||
inkscape:cx="391.13107"
|
||||
inkscape:cy="332.94628"
|
||||
inkscape:window-width="1908"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<title
|
||||
id="title1">ionicons-v5_logos</title>
|
||||
<path
|
||||
d="m 377.94638,279.52117 -1.75376,-7.44181 H 214.44368 v 68.45852 h 96.64274 c -10.03366,47.64625 -56.59351,72.7265 -94.62516,72.7265 -27.67207,0 -56.84182,-11.63997 -76.14863,-30.34928 a 108.70175,108.70175 0 0 1 -32.43671,-76.73064 c 0,-28.83607 12.95917,-57.67991 31.81591,-76.65304 18.85674,-18.97314 47.33585,-29.5888 75.652,-29.5888 32.42894,0 55.67007,17.21939 64.36125,25.07248 l 48.64728,-48.3912 C 314.08178,164.08378 274.87838,132.48516 213.77632,132.48516 v 0 c -47.14186,0 -92.34372,18.05745 -125.385701,50.9908 -32.607419,32.42893 -49.485367,79.32247 -49.485367,122.83266 0,43.51018 15.970029,88.06021 47.56865,120.74522 33.763658,34.85782 81.580638,53.07825 130.817688,53.07825 44.79835,0 87.26093,-17.55307 117.52483,-49.40001 29.75175,-31.3503 45.13979,-74.72857 45.13979,-120.20204 0,-19.14386 -1.92447,-30.51221 -2.00983,-31.00887 z"
|
||||
id="path1"
|
||||
style="stroke-width:0.775997" />
|
||||
<path
|
||||
d="m 396.33661,224.32725 v 0 0 c -8.3763,-55.27516 -51.76093,-98.65984 -107.03609,-107.0361 v 0 0 c 55.27516,-8.3763 98.65979,-51.760947 107.03609,-107.036116 v 0 0 c 8.37627,55.275169 51.76088,98.659816 107.0361,107.036116 v 0 0 c -55.27522,8.37626 -98.65983,51.76094 -107.0361,107.0361 z"
|
||||
fill="#076eff"
|
||||
id="path19"
|
||||
style="fill:#000000;stroke-width:2.9422" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
+35
-22
@@ -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);
|
||||
+9
-10
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
|
||||
@@ -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 = `
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+23
@@ -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
|
||||
@@ -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=<bold variant of regular font>
|
||||
# font-italic=<italic variant of regular font>
|
||||
# font-bold-italic=<bold+italic variant of regular font>
|
||||
# line-height=<font metrics>
|
||||
letter-spacing=0
|
||||
# horizontal-letter-offset=0
|
||||
# vertical-letter-offset=0
|
||||
@@ -34,17 +24,8 @@ bold-text-in-bright=no
|
||||
# selection-target=primary
|
||||
# workers=<number of logical CPUs>
|
||||
|
||||
[bell]
|
||||
# urgent=no
|
||||
# notify=no
|
||||
# command=
|
||||
# command-focused=no
|
||||
|
||||
[scrollback]
|
||||
lines=10000
|
||||
# multiplier=3.0
|
||||
# indicator-position=relative
|
||||
# indicator-format=
|
||||
|
||||
[url]
|
||||
# launch=xdg-open ${url}
|
||||
@@ -55,15 +36,11 @@ lines=10000
|
||||
|
||||
[cursor]
|
||||
style=beam
|
||||
# color=111111 dcdccc
|
||||
color=$background # $onBackground #
|
||||
# blink=no
|
||||
beam-thickness=1.5
|
||||
# underline-thickness=<font underline thickness>
|
||||
|
||||
[mouse]
|
||||
# hide-when-typing=no
|
||||
# alternate-scroll-mode=yes
|
||||
|
||||
[colors]
|
||||
alpha=1
|
||||
@@ -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
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 = '';
|
||||
|
||||
@@ -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();
|
||||
|
||||
+304
-321
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
+381
-351
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
]
|
||||
})]
|
||||
}),
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
@@ -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';
|
||||
}),
|
||||
})
|
||||
})
|
||||
});
|
||||
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,
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -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' })
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
]
|
||||
})]
|
||||
}),
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
||||
@@ -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' }),
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}))
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
@@ -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);
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,11 +114,14 @@ const distroAndVersion = Box({
|
||||
Label({
|
||||
className: 'bg-distro-name',
|
||||
xalign: 0,
|
||||
label: '<version>',
|
||||
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));
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
]
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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(),
|
||||
]
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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;
|
||||
@@ -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];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
,
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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 }));
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
+3
-2
@@ -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,
|
||||
],
|
||||
@@ -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')
|
||||
,
|
||||
});
|
||||
|
||||
@@ -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 <u>here</u>, 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ export default Scrollable({
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
QuickScripts(),
|
||||
// QuickScripts(),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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(),
|
||||
]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user