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";
|
"use strict";
|
||||||
// Import
|
// Import
|
||||||
const { GLib } = imports.gi;
|
import Gdk from 'gi://Gdk';
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.js'
|
import App from 'resource:///com/github/Aylur/ags/app.js'
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
||||||
// Widgets
|
// 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 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 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 Indicator from './widgets/indicators/main.js';
|
||||||
import Osk from './widgets/onscreenkeyboard/main.js';
|
import Osk from './widgets/onscreenkeyboard/main.js';
|
||||||
import Overview from './widgets/overview/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 SideLeft from './widgets/sideleft/main.js';
|
||||||
import SideRight from './widgets/sideright/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
|
// SCSS compilation
|
||||||
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles
|
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles
|
||||||
@@ -29,7 +33,25 @@ function applyStyle() {
|
|||||||
}
|
}
|
||||||
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 {
|
export default {
|
||||||
css: `${App.configDir}/style.css`,
|
css: `${App.configDir}/style.css`,
|
||||||
stackTraceOnError: true,
|
stackTraceOnError: true,
|
||||||
@@ -38,20 +60,11 @@ export default {
|
|||||||
'sideleft': CLOSE_ANIM_TIME,
|
'sideleft': CLOSE_ANIM_TIME,
|
||||||
'osk': CLOSE_ANIM_TIME,
|
'osk': CLOSE_ANIM_TIME,
|
||||||
},
|
},
|
||||||
windows: [
|
windows: Windows().flat(1),
|
||||||
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(),
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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}`));
|
const service = async file => (await require(`service/${file}`));
|
||||||
|
|
||||||
export const App = await require('app');
|
export const App = await require('app');
|
||||||
export const Widget = await require('widget');
|
// export const Widget = await require('widget');
|
||||||
export const Service = await require('service');
|
// export const Service = await require('service');
|
||||||
export const Variable = await require('variable');
|
// export const Variable = await require('variable');
|
||||||
export const Utils = await import(resource('utils'));
|
export const Utils = await import(resource('utils'));
|
||||||
|
// export const Applications = await service('applications');
|
||||||
export const Applications = await service('applications');
|
// export const Audio = await service('audio');
|
||||||
export const Audio = await service('audio');
|
// export const Battery = await service('battery');
|
||||||
export const Battery = await service('battery');
|
// export const Bluetooth = await service('bluetooth');
|
||||||
export const Bluetooth = await service('bluetooth');
|
// export const Hyprland = await service('hyprland');
|
||||||
export const Hyprland = await service('hyprland');
|
|
||||||
export const Mpris = await service('mpris');
|
export const Mpris = await service('mpris');
|
||||||
export const Network = await service('network');
|
export const Network = await service('network');
|
||||||
export const Notifications = await service('notifications');
|
export const Notifications = await service('notifications');
|
||||||
export const SystemTray = await service('systemtray');
|
// export const SystemTray = await service('systemtray');
|
||||||
|
|
||||||
globalThis['App'] = App; //////////////////////////////
|
globalThis['App'] = App; //////////////////////////////
|
||||||
// globalThis['Widget'] = Widget;
|
// globalThis['Widget'] = Widget;
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ export const MarginRevealer = ({
|
|||||||
child.css = `margin-top: -${child.get_allocated_height()}px;`;
|
child.css = `margin-top: -${child.get_allocated_height()}px;`;
|
||||||
},
|
},
|
||||||
'toggle': () => {
|
'toggle': () => {
|
||||||
console.log('toggle');
|
|
||||||
if (widget.attribute.revealChild) widget.attribute.hide();
|
if (widget.attribute.revealChild) widget.attribute.hide();
|
||||||
else widget.attribute.show();
|
else widget.attribute.show();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export const AnimatedCircProg = ({
|
|||||||
// area.css = `font-size: ${initFrom}px; transition: ${initAnimTime}ms linear;`;
|
// area.css = `font-size: ${initFrom}px; transition: ${initAnimTime}ms linear;`;
|
||||||
Utils.timeout(20, () => {
|
Utils.timeout(20, () => {
|
||||||
area.css = `font-size: ${initTo}px;`;
|
area.css = `font-size: ${initTo}px;`;
|
||||||
})
|
}, area)
|
||||||
// const transitionDistance = initTo - initFrom;
|
// const transitionDistance = initTo - initFrom;
|
||||||
// const oneStep = initAnimTime / initAnimPoints;
|
// const oneStep = initAnimTime / initAnimPoints;
|
||||||
// area.css = `
|
// area.css = `
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
const { GLib, Gdk, Gtk } = imports.gi;
|
const { GLib, Gdk, Gtk } = imports.gi;
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.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;
|
const { Box, EventBox, Icon, Overlay, Label, Button, Revealer } = Widget;
|
||||||
import { MaterialIcon } from "./materialicon.js";
|
import { MaterialIcon } from "./materialicon.js";
|
||||||
import { setupCursorHover } from "./cursorhover.js";
|
import { setupCursorHover } from "./cursorhover.js";
|
||||||
@@ -38,9 +37,9 @@ const NotificationIcon = (notifObject) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let icon = 'NO_ICON';
|
let icon = 'NO_ICON';
|
||||||
if (lookUpIcon(notifObject.appIcon))
|
if (Utils.lookUpIcon(notifObject.appIcon))
|
||||||
icon = notifObject.appIcon;
|
icon = notifObject.appIcon;
|
||||||
if (lookUpIcon(notifObject.appEntry))
|
if (Utils.lookUpIcon(notifObject.appEntry))
|
||||||
icon = notifObject.appEntry;
|
icon = notifObject.appEntry;
|
||||||
|
|
||||||
return Box({
|
return Box({
|
||||||
@@ -58,7 +57,7 @@ const NotificationIcon = (notifObject) => {
|
|||||||
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||||
const height = styleContext.get_property('min-height', 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.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', {
|
MaterialIcon(`${notifObject.urgency == 'critical' ? 'release_alert' : guessMessageType(notifObject.summary.toLowerCase())}`, 'hugerass', {
|
||||||
@@ -84,11 +83,11 @@ export default ({
|
|||||||
notificationBox.setCss(middleClickClose);
|
notificationBox.setCss(middleClickClose);
|
||||||
Utils.timeout(200, () => {
|
Utils.timeout(200, () => {
|
||||||
wholeThing.revealChild = false;
|
wholeThing.revealChild = false;
|
||||||
});
|
}, wholeThing);
|
||||||
Utils.timeout(400, () => {
|
Utils.timeout(400, () => {
|
||||||
command();
|
command();
|
||||||
wholeThing.destroy();
|
wholeThing.destroy();
|
||||||
});
|
}, wholeThing);
|
||||||
}
|
}
|
||||||
const widget = EventBox({
|
const widget = EventBox({
|
||||||
onHover: (self) => {
|
onHover: (self) => {
|
||||||
@@ -382,11 +381,11 @@ export default ({
|
|||||||
}
|
}
|
||||||
Utils.timeout(200, () => {
|
Utils.timeout(200, () => {
|
||||||
wholeThing.revealChild = false
|
wholeThing.revealChild = false
|
||||||
});
|
}, wholeThing);
|
||||||
Utils.timeout(400, () => {
|
Utils.timeout(400, () => {
|
||||||
command();
|
command();
|
||||||
wholeThing.destroy();
|
wholeThing.destroy();
|
||||||
});
|
}, wholeThing);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
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';
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
const { Gtk } = imports.gi;
|
const { Gtk } = imports.gi;
|
||||||
const Lang = imports.lang;
|
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({
|
export const RoundedCorner = (place, props) => Widget.DrawingArea({
|
||||||
...props,
|
...props,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { MaterialIcon } from './materialicon.js';
|
|||||||
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
||||||
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
||||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.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';
|
import { languages } from '../data/languages.js';
|
||||||
|
|
||||||
// A guessing func to try to support langs not listed in 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 } = {}) => {
|
const HyprlandXkbKeyboardLayout = async ({ useFlag } = {}) => {
|
||||||
var initLangs = [];
|
try {
|
||||||
var languageStackArray = [];
|
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
||||||
var currentKeyboard;
|
var initLangs = [];
|
||||||
|
var languageStackArray = [];
|
||||||
|
var currentKeyboard;
|
||||||
|
|
||||||
const updateCurrentKeyboards = () => {
|
const updateCurrentKeyboards = () => {
|
||||||
currentKeyboard = JSON.parse(Utils.exec('hyprctl -j devices')).keyboards
|
currentKeyboard = JSON.parse(Utils.exec('hyprctl -j devices')).keyboards
|
||||||
.find(device => device.name === 'at-translated-set-2-keyboard');
|
.find(device => device.name === 'at-translated-set-2-keyboard');
|
||||||
if (currentKeyboard) {
|
if (currentKeyboard) {
|
||||||
initLangs = currentKeyboard.layout.split(',').map(lang => lang.trim());
|
initLangs = currentKeyboard.layout.split(',').map(lang => lang.trim());
|
||||||
}
|
}
|
||||||
languageStackArray = Array.from({ length: initLangs.length }, (_, i) => {
|
languageStackArray = Array.from({ length: initLangs.length }, (_, i) => {
|
||||||
const lang = languages.find(lang => lang.layout == initLangs[i]);
|
const lang = languages.find(lang => lang.layout == initLangs[i]);
|
||||||
if (!lang) return [
|
if (!lang) return [
|
||||||
initLangs[i],
|
initLangs[i],
|
||||||
Widget.Label({ label: initLangs[i] })
|
Widget.Label({ label: initLangs[i] })
|
||||||
];
|
];
|
||||||
return [
|
return [
|
||||||
lang.layout,
|
lang.layout,
|
||||||
Widget.Label({ label: (useFlag ? lang.flag : lang.layout) })
|
Widget.Label({ label: (useFlag ? lang.flag : lang.layout) })
|
||||||
];
|
];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
updateCurrentKeyboards();
|
||||||
|
const widgetRevealer = Widget.Revealer({
|
||||||
|
transition: 150,
|
||||||
|
transition: 'slide_left',
|
||||||
|
revealChild: languageStackArray.length > 1,
|
||||||
});
|
});
|
||||||
};
|
const widgetContent = Widget.Stack({
|
||||||
updateCurrentKeyboards();
|
transition: 'slide_up_down',
|
||||||
const widgetRevealer = Widget.Revealer({
|
items: [
|
||||||
transition: 150,
|
...languageStackArray,
|
||||||
transition: 'slide_left',
|
['undef', Widget.Label({ label: '?' })]
|
||||||
revealChild: languageStackArray.length > 1,
|
],
|
||||||
});
|
setup: (self) => self.hook(Hyprland, (stack, kbName, layoutName) => {
|
||||||
const widgetContent = Widget.Stack({
|
if (!kbName) {
|
||||||
transition: 'slide_up_down',
|
return;
|
||||||
items: [
|
}
|
||||||
...languageStackArray,
|
var lang = languages.find(lang => layoutName.includes(lang.name));
|
||||||
['undef', Widget.Label({ label: '?' })]
|
if (lang) {
|
||||||
],
|
widgetContent.shown = lang.layout;
|
||||||
setup: (self) => self.hook(Hyprland, (stack, kbName, layoutName) => {
|
}
|
||||||
if (!kbName) {
|
else { // Attempt to support langs not listed
|
||||||
return;
|
lang = languageStackArray.find(lang => isLanguageMatch(lang[0], layoutName));
|
||||||
}
|
if (!lang) stack.shown = 'undef';
|
||||||
var lang = languages.find(lang => layoutName.includes(lang.name));
|
else stack.shown = lang[0];
|
||||||
if (lang) {
|
}
|
||||||
widgetContent.shown = lang.layout;
|
}, 'keyboard-layout'),
|
||||||
}
|
});
|
||||||
else { // Attempt to support langs not listed
|
widgetRevealer.child = widgetContent;
|
||||||
lang = languageStackArray.find(lang => isLanguageMatch(lang[0], layoutName));
|
return widgetRevealer;
|
||||||
if (!lang) stack.shown = 'undef';
|
} catch {
|
||||||
else stack.shown = lang[0];
|
return null;
|
||||||
}
|
}
|
||||||
}, 'keyboard-layout'),
|
|
||||||
});
|
|
||||||
widgetRevealer.child = widgetContent;
|
|
||||||
return widgetRevealer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const OptionalKeyboardLayout = async () => {
|
const OptionalKeyboardLayout = async () => {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ apply_gtk() { # Using gradience-cli
|
|||||||
# (which is unreadable when broken when you use dark mode)
|
# (which is unreadable when broken when you use dark mode)
|
||||||
if [ "$lightdark" = "-l" ]; then
|
if [ "$lightdark" = "-l" ]; then
|
||||||
gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3'
|
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
|
else
|
||||||
gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3-dark
|
gsettings set org.gnome.desktop.interface gtk-theme adw-gtk3-dark
|
||||||
gsettings set org.gnome.desktop.interface color-scheme 'prefer-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))
|
newtheme = themeFromSourceColor(argbFromHex(colorstr))
|
||||||
else:
|
else:
|
||||||
# try:
|
# 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 = subprocess.check_output("swww query | awk -F 'image: ' '{print $2}'", shell=True)
|
||||||
imagePath = imagePath[:-1].decode("utf-8")
|
imagePath = imagePath[:-1].decode("utf-8")
|
||||||
img = Image.open(imagePath)
|
img = Image.open(imagePath)
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
# Switches swww wallpaper
|
|
||||||
# Requires: coreutils, xrandr, hyprland
|
|
||||||
|
|
||||||
if [ "$1" == "--noswitch" ]; then
|
if [ "$1" == "--noswitch" ]; then
|
||||||
imgpath=$(swww query | awk -F 'image: ' '{print $2}')
|
imgpath=$(swww query | awk -F 'image: ' '{print $2}')
|
||||||
|
# imgpath=$(ags run-js 'wallpaper.get(0)')
|
||||||
else
|
else
|
||||||
# Select and set image (hyprland)
|
# Select and set image (hyprland)
|
||||||
cd "$HOME/Pictures"
|
cd "$HOME/Pictures"
|
||||||
imgpath=$(yad --width 1200 --height 800 --file --title='Choose wallpaper')
|
imgpath=$(yad --width 1200 --height 800 --file --title='Choose wallpaper')
|
||||||
screensizey=$(xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2 | head -1)
|
screensizey=$(xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2 | head -1)
|
||||||
cursorposx=$(hyprctl cursorpos -j | gojq '.x')
|
cursorposx=$(hyprctl cursorpos -j | gojq '.x' 2>/dev/null) || cursorposx=960
|
||||||
cursorposy=$(hyprctl cursorpos -j | gojq '.y')
|
cursorposy=$(hyprctl cursorpos -j | gojq '.y' 2>/dev/null) || cursorposy=540
|
||||||
cursorposy_inverted=$(( screensizey - cursorposy ))
|
cursorposy_inverted=$(( screensizey - cursorposy ))
|
||||||
|
|
||||||
if [ "$imgpath" == '' ]; then
|
if [ "$imgpath" == '' ]; then
|
||||||
@@ -18,8 +17,9 @@ else
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
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 \
|
swww img "$imgpath" --transition-step 100 --transition-fps 60 \
|
||||||
--transition-type grow --transition-angle 30 --transition-duration 1 \
|
--transition-type grow --transition-angle 30 --transition-duration 1 \
|
||||||
--transition-pos "$cursorposx, $cursorposy_inverted"
|
--transition-pos "$cursorposx, $cursorposy_inverted"
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ getdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cd ~/Videos || exit
|
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'
|
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'record-script.sh'
|
||||||
if [[ "$1" == "--sound" ]]; then
|
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
|
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
|
else
|
||||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" & disown
|
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" & disown
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
kill --signal SIGINT wf-recorder
|
|
||||||
notify-send "Recording Stopped" "Stopped" -a 'record-script.sh'
|
|
||||||
fi
|
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
|
shell=fish
|
||||||
# term=foot (or xterm-256color if built with -Dterminfo=disabled)
|
|
||||||
term=xterm-256color
|
term=xterm-256color
|
||||||
# login-shell=no
|
|
||||||
|
|
||||||
# app-id=foot
|
|
||||||
title=foot
|
title=foot
|
||||||
# locked-title=no
|
|
||||||
|
|
||||||
font=SpaceMono Nerd Font:size=11
|
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
|
letter-spacing=0
|
||||||
# horizontal-letter-offset=0
|
# horizontal-letter-offset=0
|
||||||
# vertical-letter-offset=0
|
# vertical-letter-offset=0
|
||||||
@@ -34,17 +24,8 @@ bold-text-in-bright=no
|
|||||||
# selection-target=primary
|
# selection-target=primary
|
||||||
# workers=<number of logical CPUs>
|
# workers=<number of logical CPUs>
|
||||||
|
|
||||||
[bell]
|
|
||||||
# urgent=no
|
|
||||||
# notify=no
|
|
||||||
# command=
|
|
||||||
# command-focused=no
|
|
||||||
|
|
||||||
[scrollback]
|
[scrollback]
|
||||||
lines=10000
|
lines=10000
|
||||||
# multiplier=3.0
|
|
||||||
# indicator-position=relative
|
|
||||||
# indicator-format=
|
|
||||||
|
|
||||||
[url]
|
[url]
|
||||||
# launch=xdg-open ${url}
|
# launch=xdg-open ${url}
|
||||||
@@ -55,15 +36,11 @@ lines=10000
|
|||||||
|
|
||||||
[cursor]
|
[cursor]
|
||||||
style=beam
|
style=beam
|
||||||
# color=111111 dcdccc
|
|
||||||
color=$background # $onBackground #
|
color=$background # $onBackground #
|
||||||
# blink=no
|
# blink=no
|
||||||
beam-thickness=1.5
|
beam-thickness=1.5
|
||||||
# underline-thickness=<font underline thickness>
|
# underline-thickness=<font underline thickness>
|
||||||
|
|
||||||
[mouse]
|
|
||||||
# hide-when-typing=no
|
|
||||||
# alternate-scroll-mode=yes
|
|
||||||
|
|
||||||
[colors]
|
[colors]
|
||||||
alpha=1
|
alpha=1
|
||||||
@@ -122,10 +99,10 @@ search-start=Control+f
|
|||||||
# show-urls-copy=none
|
# show-urls-copy=none
|
||||||
|
|
||||||
[search-bindings]
|
[search-bindings]
|
||||||
# cancel=Control+g Control+c Escape
|
cancel=Escape
|
||||||
|
find-prev=Shift+F3
|
||||||
|
find-next=F3 Control+G
|
||||||
# commit=Return
|
# commit=Return
|
||||||
# find-prev=Control+r
|
|
||||||
# find-next=Control+s
|
|
||||||
# cursor-left=Left Control+b
|
# cursor-left=Left Control+b
|
||||||
# cursor-left-word=Control+Left Mod1+b
|
# cursor-left-word=Control+Left Mod1+b
|
||||||
# cursor-right=Right Control+f
|
# cursor-right=Right Control+f
|
||||||
|
|||||||
@@ -87,9 +87,9 @@
|
|||||||
"BLACK_500": "#393634",
|
"BLACK_500": "#393634",
|
||||||
"BLACK_700": "#33302F",
|
"BLACK_700": "#33302F",
|
||||||
"BLACK_900": "#2B2928",
|
"BLACK_900": "#2B2928",
|
||||||
"accent_bg_color": "#51d7ef",
|
"accent_bg_color": "#e4b5ff",
|
||||||
"accent_fg_color": "#00363f",
|
"accent_fg_color": "#471868",
|
||||||
"accent_color": "#51d7ef",
|
"accent_color": "#e4b5ff",
|
||||||
"destructive_bg_color": "#ffb4a9",
|
"destructive_bg_color": "#ffb4a9",
|
||||||
"destructive_fg_color": "#680003",
|
"destructive_fg_color": "#680003",
|
||||||
"destructive_color": "#ffb4a9",
|
"destructive_color": "#ffb4a9",
|
||||||
@@ -99,22 +99,22 @@
|
|||||||
"warning_fg_color": "rgba(0, 0, 0, 0.87)",
|
"warning_fg_color": "rgba(0, 0, 0, 0.87)",
|
||||||
"error_bg_color": "#ffb4a9",
|
"error_bg_color": "#ffb4a9",
|
||||||
"error_fg_color": "#680003",
|
"error_fg_color": "#680003",
|
||||||
"window_bg_color": "#0F1011",
|
"window_bg_color": "#111012",
|
||||||
"window_fg_color": "#e1e3e4",
|
"window_fg_color": "#e7e0e5",
|
||||||
"view_bg_color": "#191c1d",
|
"view_bg_color": "#1d1b1e",
|
||||||
"view_fg_color": "#e1e3e4",
|
"view_fg_color": "#e7e0e5",
|
||||||
"headerbar_bg_color": "mix(@dialog_bg_color, @window_bg_color, 0.5)",
|
"headerbar_bg_color": "mix(@dialog_bg_color, @window_bg_color, 0.5)",
|
||||||
"headerbar_fg_color": "#cde7ed",
|
"headerbar_fg_color": "#eedcf5",
|
||||||
"headerbar_border_color": "#334a4f",
|
"headerbar_border_color": "#4f4256",
|
||||||
"headerbar_backdrop_color": "@headerbar_bg_color",
|
"headerbar_backdrop_color": "@headerbar_bg_color",
|
||||||
"headerbar_shade_color": "rgba(0, 0, 0, 0.09)",
|
"headerbar_shade_color": "rgba(0, 0, 0, 0.09)",
|
||||||
"card_bg_color": "#0F1011",
|
"card_bg_color": "#111012",
|
||||||
"card_fg_color": "#cde7ed",
|
"card_fg_color": "#eedcf5",
|
||||||
"card_shade_color": "rgba(0, 0, 0, 0.09)",
|
"card_shade_color": "rgba(0, 0, 0, 0.09)",
|
||||||
"dialog_bg_color": "#334a4f",
|
"dialog_bg_color": "#4f4256",
|
||||||
"dialog_fg_color": "#cde7ed",
|
"dialog_fg_color": "#eedcf5",
|
||||||
"popover_bg_color": "#334a4f",
|
"popover_bg_color": "#4f4256",
|
||||||
"popover_fg_color": "#cde7ed",
|
"popover_fg_color": "#eedcf5",
|
||||||
"thumbnail_bg_color": "#1a1b26",
|
"thumbnail_bg_color": "#1a1b26",
|
||||||
"thumbnail_fg_color": "#AEE5FA",
|
"thumbnail_fg_color": "#AEE5FA",
|
||||||
"shade_color": "rgba(0, 0, 0, 0.36)",
|
"shade_color": "rgba(0, 0, 0, 0.36)",
|
||||||
|
|||||||
@@ -32,9 +32,7 @@ $bar_subgroup_bg: $surfaceVariant;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bar-group-pad {
|
.bar-group-pad {
|
||||||
// padding: 0rem 1.023rem;
|
|
||||||
padding: 0.205rem;
|
padding: 0.205rem;
|
||||||
// padding-left: 0.341rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar-group-pad-less {
|
.bar-group-pad-less {
|
||||||
@@ -333,6 +331,11 @@ $bar_subgroup_bg: $surfaceVariant;
|
|||||||
background-color: $onSurfaceVariant;
|
background-color: $onSurfaceVariant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bar-corner-spacing {
|
||||||
|
min-width: $rounding_large;
|
||||||
|
min-height: $rounding_large;
|
||||||
|
}
|
||||||
|
|
||||||
.corner {
|
.corner {
|
||||||
background-color: $t_background;
|
background-color: $t_background;
|
||||||
@include large-rounding;
|
@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 {
|
@mixin bg-textshadow {
|
||||||
// text-shadow: mix($shadow, $secondaryContainer, 50%) 1px 0px 3px;
|
// 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 {
|
.test {
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
45deg, #f4d609 0%, #f4d609 10%, #212121 10%, #212121 20%, #f4d609 20%, #f4d609 30%, #212121 30%,
|
45deg, #f4d609 0%, #f4d609 10%, #212121 10%, #212121 20%, #f4d609 20%, #f4d609 30%, #212121 30%,
|
||||||
@@ -161,6 +139,13 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
@include full-rounding;
|
||||||
|
background-color: $primary;
|
||||||
|
color: $onPrimary;
|
||||||
|
padding: 0.682rem 1.023rem;
|
||||||
|
}
|
||||||
|
|
||||||
.titlefont {
|
.titlefont {
|
||||||
@include titlefont;
|
@include titlefont;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,28 @@ $elevation_margin: 0.476rem;
|
|||||||
margin: $elevation_margin;
|
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 {
|
@mixin menu_decel {
|
||||||
transition: 300ms cubic-bezier(0.1, 1, 0, 1);
|
transition: 300ms cubic-bezier(0.1, 1, 0, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
$darkmode: true;
|
$darkmode: true;
|
||||||
$primary: #51d7ef;
|
$primary: #e4b5ff;
|
||||||
$onPrimary: #00363f;
|
$onPrimary: #471868;
|
||||||
$primaryContainer: #004e5a;
|
$primaryContainer: #5f3280;
|
||||||
$onPrimaryContainer: #9cefff;
|
$onPrimaryContainer: #f4d9ff;
|
||||||
$secondary: #b1cbd1;
|
$secondary: #d2c1d9;
|
||||||
$onSecondary: #1c3439;
|
$onSecondary: #372c3e;
|
||||||
$secondaryContainer: #334a4f;
|
$secondaryContainer: #4f4256;
|
||||||
$onSecondaryContainer: #cde7ed;
|
$onSecondaryContainer: #eedcf5;
|
||||||
$tertiary: #bcc5ea;
|
$tertiary: #f4b7ba;
|
||||||
$onTertiary: #262f4d;
|
$onTertiary: #4b2528;
|
||||||
$tertiaryContainer: #3d4665;
|
$tertiaryContainer: #663b3e;
|
||||||
$onTertiaryContainer: #dae1ff;
|
$onTertiaryContainer: #ffdadc;
|
||||||
$error: #ffb4a9;
|
$error: #ffb4a9;
|
||||||
$onError: #680003;
|
$onError: #680003;
|
||||||
$errorContainer: #930006;
|
$errorContainer: #930006;
|
||||||
$onErrorContainer: #ffb4a9;
|
$onErrorContainer: #ffb4a9;
|
||||||
$colorbarbg: #0F1011;
|
$colorbarbg: #111012;
|
||||||
$background: #0F1011;
|
$background: #111012;
|
||||||
$onBackground: #e1e3e4;
|
$onBackground: #e7e0e5;
|
||||||
$surface: #191c1d;
|
$surface: #1d1b1e;
|
||||||
$onSurface: #e1e3e4;
|
$onSurface: #e7e0e5;
|
||||||
$surfaceVariant: #3f484a;
|
$surfaceVariant: #4b454d;
|
||||||
$onSurfaceVariant: #bfc8ca;
|
$onSurfaceVariant: #cdc3ce;
|
||||||
$outline: #899294;
|
$outline: #968e98;
|
||||||
$shadow: #000000;
|
$shadow: #000000;
|
||||||
$inverseSurface: #e1e3e4;
|
$inverseSurface: #e7e0e5;
|
||||||
$inverseOnSurface: #2d3132;
|
$inverseOnSurface: #322f33;
|
||||||
$inversePrimary: #006877;
|
$inversePrimary: #784a9a;
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
.overview-tasks-window {
|
.overview-tasks-window {
|
||||||
@include normal-rounding;
|
@include normal-rounding;
|
||||||
@include menu_decel;
|
@include menu_decel;
|
||||||
background-color: $l_t_secondaryContainer;
|
background-color: $t_surfaceVariant;
|
||||||
color: $onSecondaryContainer;
|
color: $onSecondaryContainer;
|
||||||
border: 0.068rem solid $t_t_t_onSecondaryContainer;
|
border: 0.068rem solid $t_t_t_onSecondaryContainer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,27 +166,27 @@ $onChatgpt: $onPrimary;
|
|||||||
padding: 0rem $rounding_medium;
|
padding: 0rem $rounding_medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-navrail-btn > box > label {
|
.sidebar-navrail-btn>box>label {
|
||||||
@include full-rounding;
|
@include full-rounding;
|
||||||
@include menu_decel;
|
@include menu_decel;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-navrail-btn:hover > box > label:first-child,
|
.sidebar-navrail-btn:hover>box>label:first-child,
|
||||||
.sidebar-navrail-btn:focus > box > label:first-child {
|
.sidebar-navrail-btn:focus>box>label:first-child {
|
||||||
background-color: $hovercolor;
|
background-color: $hovercolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-navrail-btn:active > box > label:first-child {
|
.sidebar-navrail-btn:active>box>label:first-child {
|
||||||
background-color: $activecolor;
|
background-color: $activecolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-navrail-btn-active > box > label:first-child {
|
.sidebar-navrail-btn-active>box>label:first-child {
|
||||||
background-color: $secondaryContainer;
|
background-color: $secondaryContainer;
|
||||||
color: $onSecondaryContainer;
|
color: $onSecondaryContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-navrail-btn-active:hover > box > label:first-child,
|
.sidebar-navrail-btn-active:hover>box>label:first-child,
|
||||||
.sidebar-navrail-btn-active:focus > box > label:first-child {
|
.sidebar-navrail-btn-active:focus>box>label:first-child {
|
||||||
background-color: mix($secondaryContainer, $hovercolor, 90%);
|
background-color: mix($secondaryContainer, $hovercolor, 90%);
|
||||||
color: mix($onSecondaryContainer, $hovercolor, 90%);
|
color: mix($onSecondaryContainer, $hovercolor, 90%);
|
||||||
}
|
}
|
||||||
@@ -346,7 +346,7 @@ $onChatgpt: $onPrimary;
|
|||||||
background-color: $activecolor;
|
background-color: $activecolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-selector-tab-active > box > label {
|
.sidebar-selector-tab-active>box>label {
|
||||||
color: $primary;
|
color: $primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,8 +416,8 @@ $onChatgpt: $onPrimary;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-todo-add {
|
.sidebar-todo-add {
|
||||||
@include full-rounding;
|
|
||||||
@include menu_decel;
|
@include menu_decel;
|
||||||
|
@include small-rounding;
|
||||||
min-width: 1.705rem;
|
min-width: 1.705rem;
|
||||||
min-height: 1.705rem;
|
min-height: 1.705rem;
|
||||||
color: $onSecondaryContainer;
|
color: $onSecondaryContainer;
|
||||||
@@ -435,7 +435,7 @@ $onChatgpt: $onPrimary;
|
|||||||
|
|
||||||
.sidebar-todo-add-available {
|
.sidebar-todo-add-available {
|
||||||
@include menu_decel;
|
@include menu_decel;
|
||||||
@include full-rounding;
|
@include small-rounding;
|
||||||
min-width: 1.705rem;
|
min-width: 1.705rem;
|
||||||
min-height: 1.705rem;
|
min-height: 1.705rem;
|
||||||
background-color: $primary;
|
background-color: $primary;
|
||||||
@@ -531,19 +531,15 @@ $onChatgpt: $onPrimary;
|
|||||||
|
|
||||||
.sidebar-chat-send:hover,
|
.sidebar-chat-send:hover,
|
||||||
.sidebar-chat-send:focus {
|
.sidebar-chat-send:focus {
|
||||||
background-color: mix(
|
background-color: mix($sidebar_chat_textboxareaColor,
|
||||||
$sidebar_chat_textboxareaColor,
|
$t_onSecondaryContainer,
|
||||||
$t_onSecondaryContainer,
|
97%);
|
||||||
97%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-chat-send:active {
|
.sidebar-chat-send:active {
|
||||||
background-color: mix(
|
background-color: mix($sidebar_chat_textboxareaColor,
|
||||||
$sidebar_chat_textboxareaColor,
|
$t_onSecondaryContainer,
|
||||||
$t_onSecondaryContainer,
|
80%);
|
||||||
80%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-chat-send-available {
|
.sidebar-chat-send-available {
|
||||||
@@ -670,9 +666,10 @@ $onChatgpt: $onPrimary;
|
|||||||
@include menu_decel;
|
@include menu_decel;
|
||||||
min-height: 4.773rem;
|
min-height: 4.773rem;
|
||||||
min-width: 4.773rem;
|
min-width: 4.773rem;
|
||||||
font-size: 4rem;
|
@include icon-material;
|
||||||
background-color: white;
|
font-size: 2.727rem;
|
||||||
color: black;
|
background-color: $onBackground;
|
||||||
|
color: $background;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-chat-chip {
|
.sidebar-chat-chip {
|
||||||
@@ -706,10 +703,12 @@ $onChatgpt: $onPrimary;
|
|||||||
background-color: $t_surfaceVariant;
|
background-color: $t_surfaceVariant;
|
||||||
color: $onSurfaceVariant;
|
color: $onSurfaceVariant;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-chat-chip-toggle:focus,
|
.sidebar-chat-chip-toggle:focus,
|
||||||
.sidebar-chat-chip-toggle:hover {
|
.sidebar-chat-chip-toggle:hover {
|
||||||
background-color: $hovercolor;
|
background-color: $hovercolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-chat-chip-toggle:active {
|
.sidebar-chat-chip-toggle:active {
|
||||||
background-color: $activecolor;
|
background-color: $activecolor;
|
||||||
}
|
}
|
||||||
@@ -733,6 +732,7 @@ $onChatgpt: $onPrimary;
|
|||||||
|
|
||||||
.sidebar-pin-enabled {
|
.sidebar-pin-enabled {
|
||||||
background-color: $primary;
|
background-color: $primary;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
color: $onPrimary;
|
color: $onPrimary;
|
||||||
}
|
}
|
||||||
@@ -753,13 +753,16 @@ $onChatgpt: $onPrimary;
|
|||||||
margin-left: -0.136rem;
|
margin-left: -0.136rem;
|
||||||
padding-left: 0.818rem;
|
padding-left: 0.818rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-waifu-content {
|
.sidebar-waifu-content {
|
||||||
margin-left: 0.682rem;
|
margin-left: 0.682rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-waifu-txt {
|
.sidebar-waifu-txt {
|
||||||
@include readingfont;
|
@include readingfont;
|
||||||
margin-left: 0.682rem;
|
margin-left: 0.682rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-waifu-image {
|
.sidebar-waifu-image {
|
||||||
margin-left: 0.682rem;
|
margin-left: 0.682rem;
|
||||||
@include normal-rounding;
|
@include normal-rounding;
|
||||||
@@ -767,26 +770,29 @@ $onChatgpt: $onPrimary;
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-waifu-image-actions {
|
.sidebar-waifu-image-actions {
|
||||||
padding: 0.313rem;
|
padding: 0.313rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
$waifu_image_overlay_transparency: 0.7;
|
$waifu_image_overlay_transparency: 0.7;
|
||||||
|
|
||||||
.sidebar-waifu-image-action {
|
.sidebar-waifu-image-action {
|
||||||
@include full-rounding;
|
@include full-rounding;
|
||||||
min-width: 1.875rem;
|
min-width: 1.875rem;
|
||||||
min-height: 1.875rem;
|
min-height: 1.875rem;
|
||||||
background-color: rgba(
|
background-color: rgba(0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
$waifu_image_overlay_transparency ); // Fixed cuz on image
|
||||||
$waifu_image_overlay_transparency
|
|
||||||
); // Fixed cuz on image
|
|
||||||
color: rgba(255, 255, 255, $waifu_image_overlay_transparency);
|
color: rgba(255, 255, 255, $waifu_image_overlay_transparency);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-waifu-image-action:hover,
|
.sidebar-waifu-image-action:hover,
|
||||||
.sidebar-waifu-image-action:focus {
|
.sidebar-waifu-image-action:focus {
|
||||||
background-color: rgba(30, 30, 30, $waifu_image_overlay_transparency);
|
background-color: rgba(30, 30, 30, $waifu_image_overlay_transparency);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-waifu-image-action:active {
|
.sidebar-waifu-image-action:active {
|
||||||
background-color: rgba(60, 60, 60, $waifu_image_overlay_transparency);
|
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 Soup from 'gi://Soup?version=3.0';
|
||||||
import { fileExists } from './messages.js';
|
import { fileExists } from './messages.js';
|
||||||
|
|
||||||
// This is for custom prompt
|
// Custom prompt
|
||||||
// It's hard to make gpt-3.5 listen to all these, I know
|
|
||||||
// Disabled by default
|
|
||||||
const initMessages =
|
const initMessages =
|
||||||
[
|
[
|
||||||
{
|
{ 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: "user",
|
{ role: "assistant", content: "- Got it!", },
|
||||||
content: `
|
{ role: "user", content: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"", },
|
||||||
## Style
|
{ 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\"", },
|
||||||
- You should a natural tone like a real conversation!
|
{ role: "user", content: "raise volume by 5%", },
|
||||||
## Formatting
|
{ 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.", },
|
||||||
- Try to use **bold**, _italics_ and __underline__ extensively. Using bullet points is also encouraged.
|
{ role: "user", content: "main advantages of the nixos operating system", },
|
||||||
- When providing code blocks or facts, precede with h2 heading (\`##\`) and use 2 spaces for indentation, not 4.
|
{ 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", },
|
||||||
- Use dividers (\`---\`) to separate different information.
|
{ role: "user", content: "whats skeumorphism", },
|
||||||
## Content
|
{ 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.", },
|
||||||
- When asked to perform system tasks, include a bash code block to handle it on a Linux desktop with Wayland.
|
{ role: "user", content: "REDALiCE", },
|
||||||
- Unless requested otherwise or asked writing questions, be as short and concise as possible.
|
{ 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", },
|
||||||
`,
|
];
|
||||||
thinking: false,
|
|
||||||
done: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
role: "assistant",
|
|
||||||
content: "Got it! I'll try to give commands to perform Linux tasks. I'll try to use markdown features extensively, use divider when appropriate, and use a heading for code blocks. All code blocks should use 2 spaces for indent. And most importantly, I'll speak naturally.",
|
|
||||||
thinking: false,
|
|
||||||
done: true,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
function expandTilde(path) {
|
function expandTilde(path) {
|
||||||
if (path.startsWith('~')) {
|
if (path.startsWith('~')) {
|
||||||
@@ -129,10 +117,10 @@ class ChatGPTService extends Service {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_assistantPrompt = false;
|
_assistantPrompt = true;
|
||||||
_messages = [];
|
_messages = [];
|
||||||
_cycleModels = true;
|
_cycleModels = true;
|
||||||
_temperature = 0.5;
|
_temperature = 0.9;
|
||||||
_requestCount = 0;
|
_requestCount = 0;
|
||||||
_modelIndex = 0;
|
_modelIndex = 0;
|
||||||
_key = '';
|
_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";
|
import GLib from 'gi://GLib';
|
||||||
var __extends = (this && this.__extends) || (function () {
|
import Gio from 'gi://Gio';
|
||||||
var extendStatics = function (d, b) {
|
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||||
extendStatics = Object.setPrototypeOf ||
|
|
||||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
const SIS = GLib.getenv('SWAYSOCK');
|
||||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
||||||
return extendStatics(d, b);
|
export const PAYLOAD_TYPE = {
|
||||||
};
|
MESSAGE_RUN_COMMAND: 0,
|
||||||
return function (d, b) {
|
MESSAGE_GET_WORKSPACES: 1,
|
||||||
if (typeof b !== "function" && b !== null)
|
MESSAGE_SUBSCRIBE: 2,
|
||||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
MESSAGE_GET_OUTPUTS: 3,
|
||||||
extendStatics(d, b);
|
MESSAGE_GET_TREE: 4,
|
||||||
function __() { this.constructor = d; }
|
MESSAGE_GET_MARKS: 5,
|
||||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
MESSAGE_GET_BAR_CONFIG: 6,
|
||||||
};
|
MESSAGE_GET_VERSION: 7,
|
||||||
})();
|
MESSAGE_GET_BINDING_NODES: 8,
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
MESSAGE_GET_CONFIG: 9,
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
MESSAGE_SEND_TICK: 10,
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
MESSAGE_SYNC: 11,
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
MESSAGE_GET_BINDING_STATE: 12,
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
MESSAGE_GET_INPUTS: 100,
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
MESSAGE_GET_SEATS: 101,
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
EVENT_WORKSPACE: 0x80000000,
|
||||||
});
|
EVENT_MODE: 0x80000002,
|
||||||
};
|
EVENT_WINDOW: 0x80000003,
|
||||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
EVENT_BARCONFIG_UPDATE: 0x80000004,
|
||||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
EVENT_BINDING: 0x80000005,
|
||||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
EVENT_SHUTDOWN: 0x80000006,
|
||||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
EVENT_TICK: 0x80000007,
|
||||||
function step(op) {
|
EVENT_BAR_STATE_UPDATE: 0x80000014,
|
||||||
if (f) throw new TypeError("Generator is already executing.");
|
EVENT_INPUT: 0x80000015,
|
||||||
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];
|
const Client_Event = {
|
||||||
switch (op[0]) {
|
change: undefined,
|
||||||
case 0: case 1: t = op; break;
|
container: undefined,
|
||||||
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;
|
const Workspace_Event = {
|
||||||
default:
|
change: undefined,
|
||||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
current: undefined,
|
||||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
old: undefined,
|
||||||
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();
|
const Geometry = {
|
||||||
_.trys.pop(); continue;
|
x: undefined,
|
||||||
}
|
y: undefined,
|
||||||
op = body.call(thisArg, _);
|
width: undefined,
|
||||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
height: undefined,
|
||||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
}
|
||||||
|
|
||||||
|
//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++) {
|
export class SwayActiveClient extends Service {
|
||||||
if (ar || !(i in from)) {
|
static {
|
||||||
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
Service.register(this, {}, {
|
||||||
ar[i] = from[i];
|
'id': ['int'],
|
||||||
}
|
'name': ['string'],
|
||||||
|
'class': ['string'],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return to.concat(ar || Array.prototype.slice.call(from));
|
|
||||||
};
|
_id = 0;
|
||||||
var _a, _b, _c, _d;
|
_name = '';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
_class = '';
|
||||||
exports.sway = exports.Sway = exports.SwayActives = exports.SwayActiveID = exports.SwayActiveClient = void 0;
|
|
||||||
var _1 = require("gi://GLib");
|
get id() { return this._id; }
|
||||||
var _2 = require("gi://Gio");
|
get name() { return this._name; }
|
||||||
var service_js_1 = require("../service.js");
|
get class() { return this._class; }
|
||||||
var SIS = _1.default.getenv('SWAYSOCK');
|
|
||||||
var SwayActiveClient = /** @class */ (function (_super) {
|
updateProperty(prop, value) {
|
||||||
__extends(SwayActiveClient, _super);
|
if (!['id', 'name', 'class'].includes(prop)) return;
|
||||||
function SwayActiveClient() {
|
super.updateProperty(prop, value);
|
||||||
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);
|
|
||||||
this.emit('changed');
|
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,
|
export class SwayActiveID extends Service {
|
||||||
configurable: true
|
static {
|
||||||
});
|
Service.register(this, {}, {
|
||||||
Object.defineProperty(SwayActiveID.prototype, "name", {
|
'id': ['int'],
|
||||||
get: function () { return this._name; },
|
'name': ['string'],
|
||||||
enumerable: false,
|
});
|
||||||
configurable: true
|
}
|
||||||
});
|
|
||||||
SwayActiveID.prototype.update = function (id, name) {
|
_id = 0;
|
||||||
_super.prototype.updateProperty.call(this, 'id', id);
|
_name = '';
|
||||||
_super.prototype.updateProperty.call(this, 'name', 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');
|
this.emit('changed');
|
||||||
};
|
}
|
||||||
return SwayActiveID;
|
}
|
||||||
}(service_js_1.default));
|
|
||||||
exports.SwayActiveID = SwayActiveID;
|
export class SwayActives extends Service {
|
||||||
_b = SwayActiveID;
|
static {
|
||||||
(function () {
|
Service.register(this, {}, {
|
||||||
service_js_1.default.register(_b, {}, {
|
'client': ['jsobject'],
|
||||||
'id': ['int'],
|
'monitor': ['jsobject'],
|
||||||
'name': ['string'],
|
'workspace': ['jsobject'],
|
||||||
});
|
});
|
||||||
})();
|
}
|
||||||
var SwayActives = /** @class */ (function (_super) {
|
|
||||||
__extends(SwayActives, _super);
|
_client = new SwayActiveClient;
|
||||||
function SwayActives() {
|
_monitor = new SwayActiveID;
|
||||||
var _this = _super.call(this) || this;
|
_workspace = new SwayActiveID;
|
||||||
_this._client = new SwayActiveClient;
|
|
||||||
_this._monitor = new SwayActiveID;
|
constructor() {
|
||||||
_this._workspace = new SwayActiveID;
|
super();
|
||||||
['client', 'workspace', 'monitor'].forEach(function (obj) {
|
|
||||||
_this["_".concat(obj)].connect('changed', function () {
|
(['client', 'workspace', 'monitor']).forEach(obj => {
|
||||||
_this.notify(obj);
|
this[`_${obj}`].connect('changed', () => {
|
||||||
_this.emit('changed');
|
this.notify(obj);
|
||||||
|
this.emit('changed');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return _this;
|
|
||||||
}
|
}
|
||||||
Object.defineProperty(SwayActives.prototype, "client", {
|
|
||||||
get: function () { return this._client; },
|
get client() { return this._client; }
|
||||||
enumerable: false,
|
get monitor() { return this._monitor; }
|
||||||
configurable: true
|
get workspace() { return this._workspace; }
|
||||||
});
|
}
|
||||||
Object.defineProperty(SwayActives.prototype, "monitor", {
|
|
||||||
get: function () { return this._monitor; },
|
export class Sway extends Service {
|
||||||
enumerable: false,
|
static {
|
||||||
configurable: true
|
Service.register(this, {}, {
|
||||||
});
|
'active': ['jsobject'],
|
||||||
Object.defineProperty(SwayActives.prototype, "workspace", {
|
'monitors': ['jsobject'],
|
||||||
get: function () { return this._workspace; },
|
'workspaces': ['jsobject'],
|
||||||
enumerable: false,
|
'clients': ['jsobject'],
|
||||||
configurable: true
|
});
|
||||||
});
|
}
|
||||||
return SwayActives;
|
|
||||||
}(service_js_1.default));
|
_decoder = new TextDecoder();
|
||||||
exports.SwayActives = SwayActives;
|
_encoder = new TextEncoder();
|
||||||
_c = SwayActives;
|
_socket;
|
||||||
(function () {
|
|
||||||
service_js_1.default.register(_c, {}, {
|
_active;
|
||||||
'client': ['jsobject'],
|
_monitors;
|
||||||
'monitor': ['jsobject'],
|
_workspaces;
|
||||||
'workspace': ['jsobject'],
|
_clients;
|
||||||
});
|
|
||||||
})();
|
get active() { return this._active; }
|
||||||
var Sway = /** @class */ (function (_super) {
|
get monitors() { return Array.from(this._monitors.values()); }
|
||||||
__extends(Sway, _super);
|
get workspaces() { return Array.from(this._workspaces.values()); }
|
||||||
function Sway() {
|
get clients() { return Array.from(this._clients.values()); }
|
||||||
var _this = this;
|
|
||||||
|
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)
|
if (!SIS)
|
||||||
console.error('Sway is not running');
|
console.error('Sway is not running');
|
||||||
_this = _super.call(this) || this;
|
super();
|
||||||
_this._decoder = new TextDecoder();
|
|
||||||
_this._encoder = new TextEncoder();
|
this._active = new SwayActives();
|
||||||
_this._active = new SwayActives();
|
this._monitors = new Map();
|
||||||
_this._monitors = new Map();
|
this._workspaces = new Map();
|
||||||
_this._workspaces = new Map();
|
this._clients = new Map();
|
||||||
_this._clients = new Map();
|
|
||||||
var socket = new _2.default.SocketClient().connect(new _2.default.UnixSocketAddress({
|
this._socket = new Gio.SocketClient().connect(new Gio.UnixSocketAddress({
|
||||||
path: "".concat(SIS),
|
path: `${SIS}`,
|
||||||
}), null);
|
}), null);
|
||||||
_this._watchSocket(socket.get_input_stream());
|
|
||||||
_this._output_stream = socket.get_output_stream();
|
this._watchSocket(this._socket.get_input_stream());
|
||||||
_this.send(4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */, '');
|
this._send(PAYLOAD_TYPE.MESSAGE_GET_TREE, '');
|
||||||
_this.send(2 /* PAYLOAD_TYPE.MESSAGE_SUBSCRIBE */, JSON.stringify(['window', 'workspace']));
|
this._send(PAYLOAD_TYPE.MESSAGE_SUBSCRIBE, JSON.stringify(['window', 'workspace']));
|
||||||
_this._active.connect('changed', function () { return _this.emit('changed'); });
|
|
||||||
['monitor', 'workspace', 'client'].forEach(function (active) {
|
this._active.connect('changed', () => this.emit('changed'));
|
||||||
return _this._active.connect("notify::".concat(active), function () { return _this.notify('active'); });
|
['monitor', 'workspace', 'client'].forEach(active =>
|
||||||
});
|
this._active.connect(`notify::${active}`, () => this.notify('active')));
|
||||||
return _this;
|
|
||||||
}
|
}
|
||||||
Object.defineProperty(Sway.prototype, "active", {
|
|
||||||
get: function () { return this._active; },
|
_send(payloadType, payload) {
|
||||||
enumerable: false,
|
const pb = this._encoder.encode(payload);
|
||||||
configurable: true
|
const type = new Uint32Array([payloadType]);
|
||||||
});
|
const pl = new Uint32Array([pb.length]);
|
||||||
Object.defineProperty(Sway.prototype, "monitors", {
|
const magic_string = this._encoder.encode('i3-ipc');
|
||||||
get: function () { return Array.from(this._monitors.values()); },
|
const data = new Uint8Array([
|
||||||
enumerable: false,
|
...magic_string,
|
||||||
configurable: true
|
...(new Uint8Array(pl.buffer)),
|
||||||
});
|
...(new Uint8Array(type.buffer)),
|
||||||
Object.defineProperty(Sway.prototype, "workspaces", {
|
...pb]);
|
||||||
get: function () { return Array.from(this._workspaces.values()); },
|
this._socket.get_output_stream().write(data, null);
|
||||||
enumerable: false,
|
}
|
||||||
configurable: true
|
|
||||||
});
|
_watchSocket(stream) {
|
||||||
Object.defineProperty(Sway.prototype, "clients", {
|
stream.read_bytes_async(14, GLib.PRIORITY_DEFAULT, null, (_, resultHeader) => {
|
||||||
get: function () { return Array.from(this._clients.values()); },
|
const data = stream.read_bytes_finish(resultHeader).get_data();
|
||||||
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();
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return;
|
return;
|
||||||
var payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0];
|
const payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0];
|
||||||
var payloadType = new Uint32Array(data.slice(10, 14).buffer)[0];
|
const payloadType = new Uint32Array(data.slice(10, 14).buffer)[0];
|
||||||
stream.read_bytes_async(payloadLength, _1.default.PRIORITY_DEFAULT, null, function (_, resultPayload) {
|
stream.read_bytes_async(
|
||||||
var data = stream.read_bytes_finish(resultPayload).get_data();
|
payloadLength,
|
||||||
if (!data)
|
GLib.PRIORITY_DEFAULT,
|
||||||
return;
|
null,
|
||||||
_this._onEvent(payloadType, JSON.parse(_this._decoder.decode(data)));
|
(_, resultPayload) => {
|
||||||
_this._watchSocket(stream);
|
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 () {
|
async _onEvent(event_type, event) {
|
||||||
return __generator(this, function (_e) {
|
if (!event)
|
||||||
if (!event)
|
return;
|
||||||
return [2 /*return*/];
|
try {
|
||||||
try {
|
switch (event_type) {
|
||||||
switch (event_type) {
|
case PAYLOAD_TYPE.EVENT_WORKSPACE:
|
||||||
case 2147483648 /* PAYLOAD_TYPE.EVENT_WORKSPACE */:
|
this._handleWorkspaceEvent(event);
|
||||||
this._handleWorkspaceEvent(event);
|
break;
|
||||||
break;
|
case PAYLOAD_TYPE.EVENT_WINDOW:
|
||||||
case 2147483651 /* PAYLOAD_TYPE.EVENT_WINDOW */:
|
this._handleWindowEvent(event);
|
||||||
this._handleWindowEvent(event);
|
break;
|
||||||
break;
|
case PAYLOAD_TYPE.MESSAGE_GET_TREE:
|
||||||
case 4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */:
|
this._handleTreeMessage(event);
|
||||||
this._handleTreeMessage(event);
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
break;
|
||||||
break;
|
}
|
||||||
}
|
} catch (error) {
|
||||||
}
|
logError(error);
|
||||||
catch (error) {
|
}
|
||||||
logError(error);
|
this.emit('changed');
|
||||||
}
|
}
|
||||||
this.emit('changed');
|
|
||||||
return [2 /*return*/];
|
_handleWorkspaceEvent(workspaceEvent) {
|
||||||
});
|
const workspace = workspaceEvent.current;
|
||||||
});
|
|
||||||
};
|
|
||||||
Sway.prototype._handleWorkspaceEvent = function (workspaceEvent) {
|
|
||||||
var workspace = workspaceEvent.current;
|
|
||||||
switch (workspaceEvent.change) {
|
switch (workspaceEvent.change) {
|
||||||
case 'init':
|
case 'init':
|
||||||
this._workspaces.set(workspace.name, workspace);
|
this._workspaces.set(workspace.name, workspace);
|
||||||
this.notify('workspaces');
|
|
||||||
break;
|
break;
|
||||||
case 'empty':
|
case 'empty':
|
||||||
this._workspaces.delete(workspace.name);
|
this._workspaces.delete(workspace.name);
|
||||||
this.notify('workspaces');
|
|
||||||
break;
|
break;
|
||||||
case 'focus':
|
case 'focus':
|
||||||
this._active.workspace.update(workspace.id, workspace.name);
|
this._active.workspace.update(workspace.id, workspace.name);
|
||||||
this._active.monitor.update(1, workspace.output);
|
this._active.monitor.update(1, workspace.output);
|
||||||
|
|
||||||
this._workspaces.set(workspace.name, workspace);
|
this._workspaces.set(workspace.name, workspace);
|
||||||
this._workspaces.set(workspaceEvent.old.name, workspaceEvent.old);
|
this._workspaces.set(workspaceEvent.old.name, workspaceEvent.old);
|
||||||
this.notify('workspaces');
|
|
||||||
break;
|
break;
|
||||||
case 'rename':
|
case 'rename':
|
||||||
if (this._active.workspace.id === workspace.id)
|
if (this._active.workspace.id === workspace.id)
|
||||||
this._active.workspace.updateProperty('name', workspace.name);
|
this._active.workspace.updateProperty('name', workspace.name);
|
||||||
this._workspaces.set(workspace.name, workspace);
|
this._workspaces.set(workspace.name, workspace);
|
||||||
this.notify('workspaces');
|
|
||||||
break;
|
break;
|
||||||
case 'reload':
|
case 'reload':
|
||||||
break;
|
break;
|
||||||
@@ -311,34 +305,36 @@ var Sway = /** @class */ (function (_super) {
|
|||||||
case 'urgent':
|
case 'urgent':
|
||||||
default:
|
default:
|
||||||
this._workspaces.set(workspace.name, workspace);
|
this._workspaces.set(workspace.name, workspace);
|
||||||
this.notify('workspaces');
|
|
||||||
}
|
}
|
||||||
};
|
this.notify('workspaces');
|
||||||
Sway.prototype._handleWindowEvent = function (clientEvent) {
|
}
|
||||||
var _e;
|
|
||||||
var client = clientEvent.container;
|
_handleWindowEvent(clientEvent) {
|
||||||
var id = client.id;
|
const client = clientEvent.container;
|
||||||
|
const id = client.id;
|
||||||
switch (clientEvent.change) {
|
switch (clientEvent.change) {
|
||||||
case 'new':
|
case 'new':
|
||||||
this._clients.set(id, client);
|
|
||||||
this.notify('clients');
|
|
||||||
break;
|
|
||||||
case 'close':
|
case 'close':
|
||||||
this._clients.delete(id);
|
case 'floating':
|
||||||
this.notify('clients');
|
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;
|
break;
|
||||||
case 'focus':
|
case 'focus':
|
||||||
if (this._active.client.id === id)
|
if (this._active.client.id === id)
|
||||||
return;
|
return;
|
||||||
// eslint-disable-next-line no-case-declarations
|
// 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)
|
if (current_active)
|
||||||
current_active.focused = false;
|
current_active.focused = false;
|
||||||
this._active.client.updateProperty('id', id);
|
this._active.client.updateProperty('id', id);
|
||||||
this._active.client.updateProperty('name', client.name);
|
this._active.client.updateProperty('name', client.name);
|
||||||
this._active.client.updateProperty('class', client.shell === 'xwayland'
|
this._active.client.updateProperty('class', client.shell === 'xwayland'
|
||||||
? ((_e = client.window_properties) === null || _e === void 0 ? void 0 : _e.class) || ''
|
? client.window_properties?.class || ''
|
||||||
: client.app_id);
|
: client.app_id,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 'title':
|
case 'title':
|
||||||
if (client.focused)
|
if (client.focused)
|
||||||
@@ -347,43 +343,39 @@ var Sway = /** @class */ (function (_super) {
|
|||||||
this.notify('clients');
|
this.notify('clients');
|
||||||
break;
|
break;
|
||||||
case 'fullscreen_mode':
|
case 'fullscreen_mode':
|
||||||
case 'move':
|
|
||||||
case 'floating':
|
|
||||||
case 'urgent':
|
case 'urgent':
|
||||||
case 'mark':
|
case 'mark':
|
||||||
default:
|
default:
|
||||||
this._clients.set(id, client);
|
this._clients.set(id, client);
|
||||||
this.notify('clients');
|
this.notify('clients');
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
Sway.prototype._handleTreeMessage = function (node) {
|
|
||||||
var _this = this;
|
_handleTreeMessage(node) {
|
||||||
var _e;
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case 'root':
|
case 'root':
|
||||||
this._workspaces.clear();
|
this._workspaces.clear();
|
||||||
this._clients.clear();
|
this._clients.clear();
|
||||||
this._monitors.clear();
|
this._monitors.clear();
|
||||||
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
|
node.nodes.map(n => this._handleTreeMessage(n));
|
||||||
['workspaces', 'clients', 'monitors'].forEach(function (t) {
|
|
||||||
_this.notify(t);
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 'output':
|
case 'output':
|
||||||
this._monitors.set(node.id, node);
|
this._monitors.set(node.id, node);
|
||||||
if (node.active)
|
if (node.active)
|
||||||
this._active.monitor.updateProperty('name', node.name);
|
this._active.monitor.update(node.id, node.name);
|
||||||
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
|
node.nodes.map(n => this._handleTreeMessage(n));
|
||||||
this.notify('monitors');
|
this.notify('monitors');
|
||||||
break;
|
break;
|
||||||
case 'workspace':
|
case 'workspace':
|
||||||
this._workspaces.set(node.name, node);
|
this._workspaces.set(node.name, node);
|
||||||
// I think I'm missing something. There has to be a better way.
|
// I think I'm missing something. There has to be a better way.
|
||||||
// eslint-disable-next-line no-case-declarations
|
// eslint-disable-next-line no-case-declarations
|
||||||
var hasFocusedChild_1 = function (n) { return n.nodes.some(function (c) { return c.focused || hasFocusedChild_1(c); }); };
|
const hasFocusedChild =
|
||||||
if (hasFocusedChild_1(node))
|
(n) => n.nodes.some(c => c.focused || hasFocusedChild(c));
|
||||||
|
if (node.focused || hasFocusedChild(node))
|
||||||
this._active.workspace.update(node.id, node.name);
|
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');
|
this.notify('workspaces');
|
||||||
break;
|
break;
|
||||||
case 'con':
|
case 'con':
|
||||||
@@ -393,25 +385,16 @@ var Sway = /** @class */ (function (_super) {
|
|||||||
this._active.client.updateProperty('id', node.id);
|
this._active.client.updateProperty('id', node.id);
|
||||||
this._active.client.updateProperty('name', node.name);
|
this._active.client.updateProperty('name', node.name);
|
||||||
this._active.client.updateProperty('class', node.shell === 'xwayland'
|
this._active.client.updateProperty('class', node.shell === 'xwayland'
|
||||||
? ((_e = node.window_properties) === null || _e === void 0 ? void 0 : _e.class) || ''
|
? node.window_properties?.class || ''
|
||||||
: node.app_id);
|
: node.app_id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
|
node.nodes.map(n => this._handleTreeMessage(n));
|
||||||
this.notify('clients');
|
this.notify('clients');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
return Sway;
|
}
|
||||||
}(service_js_1.default));
|
|
||||||
exports.Sway = Sway;
|
export const sway = new Sway;
|
||||||
_d = Sway;
|
export default sway;
|
||||||
(function () {
|
|
||||||
service_js_1.default.register(_d, {}, {
|
|
||||||
'active': ['jsobject'],
|
|
||||||
'monitors': ['jsobject'],
|
|
||||||
'workspaces': ['jsobject'],
|
|
||||||
'clients': ['jsobject'],
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
exports.sway = new Sway;
|
|
||||||
exports.default = exports.sway;
|
|
||||||
@@ -32,24 +32,26 @@ class TodoService extends Service {
|
|||||||
return this._todoJson;
|
return this._todoJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(content) {
|
_save() {
|
||||||
this._todoJson.push({ content, done: false });
|
|
||||||
Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
|
Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
|
||||||
.catch(print);
|
.catch(print);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(content) {
|
||||||
|
this._todoJson.push({ content, done: false });
|
||||||
|
this._save();
|
||||||
this.emit('updated');
|
this.emit('updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
check(index) {
|
check(index) {
|
||||||
this._todoJson[index].done = true;
|
this._todoJson[index].done = true;
|
||||||
Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
|
this._save();
|
||||||
.catch(print);
|
|
||||||
this.emit('updated');
|
this.emit('updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
uncheck(index) {
|
uncheck(index) {
|
||||||
this._todoJson[index].done = false;
|
this._todoJson[index].done = false;
|
||||||
Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
|
this._save();
|
||||||
.catch(print);
|
|
||||||
this.emit('updated');
|
this.emit('updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,17 +65,17 @@ class TodoService extends Service {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._todoPath = `${GLib.get_user_cache_dir()}/ags/user/todo.json`;
|
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(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`);
|
||||||
Utils.exec(`touch ${this._todoPath}`);
|
Utils.exec(`touch ${this._todoPath}`);
|
||||||
Utils.writeFile("[]", this._todoPath).then(() => {
|
Utils.writeFile("[]", this._todoPath).then(() => {
|
||||||
this._todoJson = JSON.parse(Utils.readFile(this._todoPath))
|
this._todoJson = JSON.parse(Utils.readFile(this._todoPath))
|
||||||
}).catch(print);
|
}).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;
|
const { Gtk } = imports.gi;
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
|
|
||||||
import { ModuleLeftSpace } from "./leftspace.js";
|
import WindowTitle from "./spaceleft.js";
|
||||||
import { ModuleMusic } from "./music.js";
|
import Indicators from "./spaceright.js";
|
||||||
import { ModuleRightSpace } from "./rightspace.js";
|
import Music from "./music.js";
|
||||||
import { ModuleSystem } from "./system.js";
|
import System from "./system.js";
|
||||||
|
import { RoundedCorner, enableClickthrough } from "../../lib/roundedcorner.js";
|
||||||
|
|
||||||
const OptionalWorkspaces = async () => {
|
const OptionalWorkspaces = async () => {
|
||||||
try {
|
try {
|
||||||
return (await import('./workspaces_hyprland.js')).default();
|
return (await import('./workspaces_hyprland.js')).default();
|
||||||
} catch {
|
} catch {
|
||||||
// return (await import('./workspaces_sway.js')).default();
|
try {
|
||||||
return Box({});
|
return (await import('./workspaces_sway.js')).default();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const left = Widget.Box({
|
export const Bar = async (monitor = 0) => {
|
||||||
className: 'bar-sidemodule',
|
const SideModule = (children) => Widget.Box({
|
||||||
children: [
|
className: 'bar-sidemodule',
|
||||||
ModuleMusic()
|
children: children,
|
||||||
],
|
});
|
||||||
});
|
const barContent = Widget.CenterBox({
|
||||||
|
|
||||||
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({
|
|
||||||
className: 'bar-bg',
|
className: 'bar-bg',
|
||||||
startWidget: ModuleLeftSpace(),
|
|
||||||
centerWidget: Widget.Box({
|
|
||||||
className: 'spacing-h-4',
|
|
||||||
children: [
|
|
||||||
left,
|
|
||||||
center,
|
|
||||||
right,
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
endWidget: ModuleRightSpace(),
|
|
||||||
setup: (self) => {
|
setup: (self) => {
|
||||||
const styleContext = self.get_style_context();
|
const styleContext = self.get_style_context();
|
||||||
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||||
// execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print);
|
// 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 Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.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;
|
const { execAsync, exec } = Utils;
|
||||||
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
||||||
import { showMusicControls } from '../../variables.js';
|
import { showMusicControls } from '../../variables.js';
|
||||||
|
|
||||||
function trimTrackTitle(title) {
|
function trimTrackTitle(title) {
|
||||||
var cleanedTitle = title;
|
if(!title) return '';
|
||||||
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning
|
const cleanRegexes = [
|
||||||
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD]
|
/【[^】]*】/, // Touhou n weeb stuff
|
||||||
return cleanedTitle.trim();
|
/\[FREE DOWNLOAD\]/, // F-777
|
||||||
|
];
|
||||||
|
cleanRegexes.forEach((expr) => title.replace(expr, ''));
|
||||||
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TrackProgress = () => {
|
const TrackProgress = () => {
|
||||||
@@ -30,61 +32,75 @@ const TrackProgress = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModuleMusic = () => Widget.EventBox({ // TODO: use cairo to make button bounce smaller on click
|
const switchToRelativeWorkspace = async (self, num) => {
|
||||||
onScrollUp: () => Hyprland.sendMessage(`dispatch workspace -1`),
|
try {
|
||||||
onScrollDown: () => Hyprland.sendMessage(`dispatch workspace +1`),
|
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
||||||
onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value),
|
Hyprland.sendMessage(`dispatch workspace ${num > 0 ? '+' : ''}${num}`);
|
||||||
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']),
|
} catch {
|
||||||
onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print),
|
execAsync([`${App.configDir}/scripts/sway/swayToRelativeWs.sh`, `${num}`]).catch(print);
|
||||||
child: Widget.Box({
|
}
|
||||||
className: 'bar-group-margin bar-sides',
|
}
|
||||||
children: [
|
|
||||||
Widget.Box({
|
export default () => {
|
||||||
className: 'bar-group bar-group-standalone bar-group-pad-music spacing-h-10',
|
// TODO: use cairo to make button bounce smaller on click, if that's possible
|
||||||
children: [
|
const playingState = Widget.Box({ // Wrap a box cuz overlay can't have margins itself
|
||||||
Widget.Box({ // Wrap a box cuz overlay can't have margins itself
|
homogeneous: true,
|
||||||
homogeneous: true,
|
children: [Widget.Overlay({
|
||||||
children: [Widget.Overlay({
|
child: Widget.Box({
|
||||||
child: Widget.Box({
|
vpack: 'center',
|
||||||
vpack: 'center',
|
className: 'bar-music-playstate',
|
||||||
className: 'bar-music-playstate',
|
homogeneous: true,
|
||||||
homogeneous: true,
|
children: [Widget.Label({
|
||||||
children: [Widget.Label({
|
vpack: 'center',
|
||||||
vpack: 'center',
|
className: 'bar-music-playstate-txt',
|
||||||
className: 'bar-music-playstate-txt',
|
justification: 'center',
|
||||||
justification: 'center',
|
setup: (self) => self.hook(Mpris, label => {
|
||||||
setup: (self) => self.hook(Mpris, label => {
|
const mpris = Mpris.getPlayer('');
|
||||||
const mpris = Mpris.getPlayer('');
|
label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
|
||||||
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(),
|
|
||||||
]
|
|
||||||
})]
|
|
||||||
}),
|
}),
|
||||||
Widget.Scrollable({
|
})],
|
||||||
hexpand: true,
|
setup: (self) => self.hook(Mpris, label => {
|
||||||
child: Widget.Label({
|
const mpris = Mpris.getPlayer('');
|
||||||
className: 'txt-smallie txt-onSurfaceVariant',
|
if (!mpris) return;
|
||||||
setup: (self) => self.hook(Mpris, label => {
|
label.toggleClassName('bar-music-playstate-playing', mpris !== null && mpris.playBackStatus == 'Playing');
|
||||||
const mpris = Mpris.getPlayer('');
|
label.toggleClassName('bar-music-playstate', mpris !== null || mpris.playBackStatus == 'Paused');
|
||||||
if (mpris)
|
}),
|
||||||
label.label = `${trimTrackTitle(mpris.trackTitle)} • ${mpris.trackArtists.join(', ')}`;
|
}),
|
||||||
else
|
overlays: [
|
||||||
label.label = 'No media';
|
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 { Box, Label, Button, Overlay, Revealer, Scrollable, Stack, EventBox } = Widget;
|
||||||
const { exec, execAsync } = Utils;
|
const { exec, execAsync } = Utils;
|
||||||
const { GLib } = imports.gi;
|
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 Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
|
||||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||||
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
||||||
@@ -14,7 +13,7 @@ const BATTERY_LOW = 20;
|
|||||||
|
|
||||||
const BatBatteryProgress = () => {
|
const BatBatteryProgress = () => {
|
||||||
const _updateProgress = (circprog) => { // Set circular progress value
|
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-low', Battery.percent <= BATTERY_LOW);
|
||||||
circprog.toggleClassName('bar-batt-circprog-full', Battery.charged);
|
circprog.toggleClassName('bar-batt-circprog-full', Battery.charged);
|
||||||
@@ -91,7 +90,7 @@ const BarBattery = () => Box({
|
|||||||
transitionDuration: 150,
|
transitionDuration: 150,
|
||||||
revealChild: false,
|
revealChild: false,
|
||||||
transition: 'slide_right',
|
transition: 'slide_right',
|
||||||
child: MaterialIcon('bolt', 'norm'),
|
child: MaterialIcon('bolt', 'norm', {tooltipText: "Charging"}),
|
||||||
setup: (self) => self.hook(Battery, revealer => {
|
setup: (self) => self.hook(Battery, revealer => {
|
||||||
self.revealChild = Battery.charging;
|
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 BarResource = (name, icon, command) => {
|
||||||
const resourceLabel = Label({
|
const resourceLabel = Label({
|
||||||
className: 'txt-smallie txt-onSurfaceVariant',
|
className: 'txt-smallie txt-onSurfaceVariant',
|
||||||
@@ -187,9 +167,18 @@ const BarGroup = ({ child }) => Widget.Box({
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ModuleSystem = () => Widget.EventBox({
|
const switchToRelativeWorkspace = async (self, num) => {
|
||||||
onScrollUp: () => Hyprland.sendMessage(`dispatch workspace -1`),
|
try {
|
||||||
onScrollDown: () => Hyprland.sendMessage(`dispatch workspace +1`),
|
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'),
|
onPrimaryClick: () => App.toggleWindow('sideright'),
|
||||||
child: Widget.Box({
|
child: Widget.Box({
|
||||||
className: 'spacing-h-5',
|
className: 'spacing-h-5',
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const WorkspaceContents = (count = 10) => {
|
|||||||
let workspaceMask = 0;
|
let workspaceMask = 0;
|
||||||
for (let i = 0; i < workspaces.length; i++) {
|
for (let i = 0; i < workspaces.length; i++) {
|
||||||
const ws = workspaces[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 (ws.id > count) return; // Not rendered
|
||||||
if (workspaces[i].windows > 0) {
|
if (workspaces[i].windows > 0) {
|
||||||
workspaceMask |= (1 << ws.id);
|
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 Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||||
import Sway from "../../services/sway.js";
|
import Sway from "../../services/sway.js";
|
||||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||||
import options from "../../options.js";
|
const { execAsync, exec } = Utils;
|
||||||
import { range } from "../../utils.js";
|
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 WorkspaceContents = (count = 10) => {
|
||||||
const ws = options.workspaces.value || 20;
|
return DrawingArea({
|
||||||
return Widget.Box({
|
css: `transition: 90ms cubic-bezier(0.1, 1, 0, 1);`,
|
||||||
children: range(ws).map((i) =>
|
attribute: {
|
||||||
Widget.Button({
|
initialized: false,
|
||||||
setup: (btn) => (btn.id = i),
|
workspaceMask: 0,
|
||||||
on_clicked: () => dispatch(i),
|
updateMask: (self) => {
|
||||||
child: Widget.Label({
|
if (self.attribute.initialized) return; // We only need this to run once
|
||||||
label: `${i}`,
|
const workspaces = Sway.workspaces;
|
||||||
class_name: "indicator",
|
let workspaceMask = 0;
|
||||||
vpack: "center",
|
// console.log('----------------')
|
||||||
}),
|
for (let i = 0; i < workspaces.length; i++) {
|
||||||
setup: (self) => self.hook(Sway, (btn) => {
|
const ws = workspaces[i];
|
||||||
btn.toggleClassName("active", Sway.active.workspace.name == i);
|
// console.log(ws.name, ',', ws.num);
|
||||||
btn.toggleClassName(
|
if (!Number(ws.name)) return;
|
||||||
"occupied",
|
const id = Number(ws.name);
|
||||||
Sway.getWorkspace(`${i}`)?.nodes.length > 0,
|
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;`)
|
||||||
})
|
})
|
||||||
),
|
.hook(Sway, (self) => self.attribute.updateMask(self), 'notify::workspaces')
|
||||||
setup: (self) => self.hook(Sway.active.workspace,
|
// .hook(Hyprland, (self, name) => self.attribute.toggleMask(self, true, name), 'workspace-added')
|
||||||
(box) => box.children.map((btn) => {
|
// .hook(Hyprland, (self, name) => self.attribute.toggleMask(self, false, name), 'workspace-removed')
|
||||||
btn.visible = Sway.workspaces.some(
|
.on('draw', Lang.bind(area, (area, cr) => {
|
||||||
(ws) => ws.name == btn.id,
|
const allocation = area.get_allocation();
|
||||||
);
|
const { width, height } = allocation;
|
||||||
})
|
|
||||||
),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default () => Widget.EventBox({
|
const workspaceStyleContext = dummyWs.get_style_context();
|
||||||
class_name: "workspaces panel-button",
|
const workspaceDiameter = workspaceStyleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||||
child: Widget.Box({
|
const workspaceRadius = workspaceDiameter / 2;
|
||||||
// its nested like this to keep it consistent with other PanelButton widgets
|
const workspaceFontSize = workspaceStyleContext.get_property('font-size', Gtk.StateFlags.NORMAL) / 4 * 3;
|
||||||
child: Widget.EventBox({
|
const workspaceFontFamily = workspaceStyleContext.get_property('font-family', Gtk.StateFlags.NORMAL);
|
||||||
on_scroll_up: () => dispatch("next"),
|
const wsbg = workspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||||
on_scroll_down: () => dispatch("prev"),
|
const wsfg = workspaceStyleContext.get_property('color', Gtk.StateFlags.NORMAL);
|
||||||
class_name: "eventbox",
|
|
||||||
// binds: [["child", options.workspaces, "value", Workspaces]],
|
const occupiedWorkspaceStyleContext = dummyOccupiedWs.get_style_context();
|
||||||
setup: (self) => self
|
const occupiedbg = occupiedWorkspaceStyleContext.get_property('background-color', Gtk.StateFlags.NORMAL);
|
||||||
.hook(options.workspaces, (self) => Selection.child = Workspaces(), "value")
|
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) => {
|
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 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 TimeAndLaunchesWidget from './timeandlaunches.js'
|
||||||
import SystemWidget from './system.js'
|
import SystemWidget from './system.js'
|
||||||
import GraphWidget from './graph.js'
|
|
||||||
|
|
||||||
export default () => Widget.Window({
|
export default (monitor) => Widget.Window({
|
||||||
name: 'desktopbackground',
|
name: `desktopbackground${monitor}`,
|
||||||
anchor: ['top', 'bottom', 'left', 'right'],
|
// anchor: ['top', 'bottom', 'left', 'right'],
|
||||||
layer: 'background',
|
layer: 'background',
|
||||||
exclusivity: 'normal',
|
exclusivity: 'ignore',
|
||||||
visible: true,
|
visible: true,
|
||||||
|
// child: WallpaperImage(monitor),
|
||||||
child: Widget.Overlay({
|
child: Widget.Overlay({
|
||||||
child: Widget.Box({
|
child: WallpaperImage(monitor),
|
||||||
hexpand: true,
|
|
||||||
vexpand: true,
|
|
||||||
}),
|
|
||||||
overlays: [
|
overlays: [
|
||||||
// GraphWidget(),
|
|
||||||
TimeAndLaunchesWidget(),
|
TimeAndLaunchesWidget(),
|
||||||
SystemWidget(),
|
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({
|
Label({
|
||||||
className: 'bg-distro-name',
|
className: 'bg-distro-name',
|
||||||
xalign: 0,
|
xalign: 0,
|
||||||
label: '<version>',
|
label: 'An environment idk',
|
||||||
setup: (label) => {
|
setup: (label) => {
|
||||||
execAsync([`bash`, `-c`, `hyprctl version | grep -oP "Tag: v\\K\\d+\\.\\d+\\.\\d+"`]).then(distro => {
|
// hyprctl will return unsuccessfully if Hyprland isn't running
|
||||||
label.label = `Hyprland ${distro}`;
|
execAsync([`bash`, `-c`, `hyprctl version | grep -oP "Tag: v\\K\\d+\\.\\d+\\.\\d+"`]).then(version => {
|
||||||
}).catch(print);
|
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',
|
className: 'osd-colorscheme spacing-v-5',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
hpack: 'center',
|
hpack: 'center',
|
||||||
@@ -44,7 +44,7 @@ const colorschemeContent = Box({
|
|||||||
export default () => Widget.Revealer({
|
export default () => Widget.Revealer({
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
transitionDuration: 200,
|
transitionDuration: 200,
|
||||||
child: colorschemeContent,
|
child: ColorschemeContent(),
|
||||||
setup: (self) => self.hook(showColorScheme, (revealer) => {
|
setup: (self) => self.hook(showColorScheme, (revealer) => {
|
||||||
revealer.revealChild = showColorScheme.value;
|
revealer.revealChild = showColorScheme.value;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,85 +1,87 @@
|
|||||||
// This file is for brightness/volume indicators
|
// This file is for brightness/volume indicators
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.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 { MarginRevealer } from '../../lib/advancedwidgets.js';
|
||||||
import Brightness from '../../services/brightness.js';
|
import Brightness from '../../services/brightness.js';
|
||||||
import Indicator from '../../services/indicator.js';
|
import Indicator from '../../services/indicator.js';
|
||||||
|
|
||||||
const OsdValue = (name, labelSetup, progressSetup, props = {}) => Box({ // Volume
|
const OsdValue = (name, labelSetup, progressSetup, props = {}) => {
|
||||||
...props,
|
const valueName = Label({
|
||||||
vertical: true,
|
xalign: 0, yalign: 0, hexpand: true,
|
||||||
className: 'osd-bg osd-value',
|
className: 'osd-label',
|
||||||
hexpand: true,
|
label: `${name}`,
|
||||||
children: [
|
});
|
||||||
Box({
|
const valueNumber = Label({
|
||||||
vexpand: true,
|
hexpand: false, className: 'osd-value-txt',
|
||||||
children: [
|
setup: labelSetup,
|
||||||
Label({
|
});
|
||||||
xalign: 0, yalign: 0, hexpand: true,
|
return Box({ // Volume
|
||||||
className: 'osd-label',
|
...props,
|
||||||
label: `${name}`,
|
vertical: true,
|
||||||
}),
|
hexpand: true,
|
||||||
Label({
|
className: 'osd-bg osd-value',
|
||||||
hexpand: false, className: 'osd-value-txt',
|
attribute: {
|
||||||
setup: labelSetup,
|
'disable': () => {
|
||||||
}),
|
valueNumber.label = '';
|
||||||
]
|
}
|
||||||
}),
|
},
|
||||||
ProgressBar({
|
children: [
|
||||||
className: 'osd-progress',
|
Box({
|
||||||
hexpand: true,
|
vexpand: true,
|
||||||
vertical: false,
|
children: [
|
||||||
setup: progressSetup,
|
valueName,
|
||||||
})
|
valueNumber,
|
||||||
],
|
]
|
||||||
});
|
}),
|
||||||
|
ProgressBar({
|
||||||
|
className: 'osd-progress',
|
||||||
|
hexpand: true,
|
||||||
|
vertical: false,
|
||||||
|
setup: progressSetup,
|
||||||
|
})
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const brightnessIndicator = OsdValue('Brightness',
|
export default () => {
|
||||||
(self) => self
|
const brightnessIndicator = OsdValue('Brightness',
|
||||||
.hook(Brightness, self => {
|
(self) => self.hook(Brightness, self => {
|
||||||
self.label = `${Math.round(Brightness.screen_value * 100)}`;
|
self.label = `${Math.round(Brightness.screen_value * 100)}`;
|
||||||
}, 'notify::screen-value')
|
}, 'notify::screen-value'),
|
||||||
,
|
(self) => self.hook(Brightness, (progress) => {
|
||||||
(self) => self
|
|
||||||
.hook(Brightness, (progress) => {
|
|
||||||
const updateValue = Brightness.screen_value;
|
const updateValue = Brightness.screen_value;
|
||||||
progress.value = updateValue;
|
progress.value = updateValue;
|
||||||
}, 'notify::screen-value')
|
}, 'notify::screen-value'),
|
||||||
,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
const volumeIndicator = OsdValue('Volume',
|
const volumeIndicator = OsdValue('Volume',
|
||||||
(self) => self
|
(self) => self.hook(Audio, (label) => {
|
||||||
.hook(Audio, (label) => {
|
|
||||||
label.label = `${Math.round(Audio.speaker?.volume * 100)}`;
|
label.label = `${Math.round(Audio.speaker?.volume * 100)}`;
|
||||||
})
|
}),
|
||||||
,
|
(self) => self.hook(Audio, (progress) => {
|
||||||
(self) => self
|
|
||||||
.hook(Audio, (progress) => {
|
|
||||||
const updateValue = Audio.speaker?.volume;
|
const updateValue = Audio.speaker?.volume;
|
||||||
if (!isNaN(updateValue)) progress.value = updateValue;
|
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 Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
import Indicator from '../../services/indicator.js';
|
import Indicator from '../../services/indicator.js';
|
||||||
import IndicatorValues from './indicatorvalues.js';
|
import IndicatorValues from './indicatorvalues.js';
|
||||||
import MusicControls from './musiccontrols.js';
|
// import MusicControls from './musiccontrols.js';
|
||||||
import ColorScheme from './colorscheme.js';
|
import ColorScheme from './colorscheme.js';
|
||||||
import NotificationPopups from './notificationpopups.js';
|
import NotificationPopups from './notificationpopups.js';
|
||||||
|
|
||||||
export default (monitor) => Widget.Window({
|
export default (monitor = 0) => Widget.Window({
|
||||||
name: `indicator${monitor}`,
|
name: `indicator${monitor}`,
|
||||||
monitor,
|
monitor,
|
||||||
className: 'indicator',
|
className: 'indicator',
|
||||||
@@ -23,7 +23,7 @@ export default (monitor) => Widget.Window({
|
|||||||
css: 'min-height: 2px;',
|
css: 'min-height: 2px;',
|
||||||
children: [
|
children: [
|
||||||
IndicatorValues(),
|
IndicatorValues(),
|
||||||
MusicControls(),
|
// MusicControls(),
|
||||||
NotificationPopups(),
|
NotificationPopups(),
|
||||||
ColorScheme(),
|
ColorScheme(),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -32,17 +32,13 @@ function isRealPlayer(player) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPlayer = (name = PREFERRED_PLAYER) => {
|
export const getPlayer = (name = PREFERRED_PLAYER) => Mpris.getPlayer(name) || Mpris.players[0] || null;
|
||||||
return Mpris.getPlayer(name) || Mpris.players[0] || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lengthStr(length) {
|
function lengthStr(length) {
|
||||||
const min = Math.floor(length / 60);
|
const min = Math.floor(length / 60);
|
||||||
const sec = Math.floor(length % 60);
|
const sec = Math.floor(length % 60);
|
||||||
const sec0 = sec < 10 ? '0' : '';
|
const sec0 = sec < 10 ? '0' : '';
|
||||||
return `${min}:${sec0}${sec}`;
|
return `${min}:${sec0}${sec}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileExists(filePath) {
|
function fileExists(filePath) {
|
||||||
let file = Gio.File.new_for_path(filePath);
|
let file = Gio.File.new_for_path(filePath);
|
||||||
return file.query_exists(null);
|
return file.query_exists(null);
|
||||||
@@ -54,17 +50,11 @@ function detectMediaSource(link) {
|
|||||||
return ' Firefox'
|
return ' Firefox'
|
||||||
return " File";
|
return " File";
|
||||||
}
|
}
|
||||||
// Remove protocol if present
|
|
||||||
let url = link.replace(/(^\w+:|^)\/\//, '');
|
let url = link.replace(/(^\w+:|^)\/\//, '');
|
||||||
// Extract the domain name
|
|
||||||
let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1];
|
let domain = url.match(/(?:[a-z]+\.)?([a-z]+\.[a-z]+)/i)[1];
|
||||||
|
if (domain == 'ytimg.com') return ' Youtube';
|
||||||
if (domain == 'ytimg.com')
|
if (domain == 'discordapp.net') return ' Discord';
|
||||||
return ' Youtube';
|
if (domain == 'sndcdn.com') return ' SoundCloud';
|
||||||
if (domain == 'discordapp.net')
|
|
||||||
return ' Discord';
|
|
||||||
if (domain == 'sndcdn.com')
|
|
||||||
return ' SoundCloud';
|
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,15 +62,20 @@ const DEFAULT_MUSIC_FONT = 'Gabarito, sans-serif';
|
|||||||
function getTrackfont(player) {
|
function getTrackfont(player) {
|
||||||
const title = player.trackTitle;
|
const title = player.trackTitle;
|
||||||
const artists = player.trackArtists.join(' ');
|
const artists = player.trackArtists.join(' ');
|
||||||
if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo')) return 'Chakra Petch'; // Rigid square replacement
|
if (artists.includes('TANO*C') || artists.includes('USAO') || artists.includes('Kobaryo'))
|
||||||
if (title.includes('東方')) return 'Crimson Text, serif'; // Serif for Touhou stuff
|
return 'Chakra Petch'; // Rigid square replacement
|
||||||
|
if (title.includes('東方'))
|
||||||
|
return 'Crimson Text, serif'; // Serif for Touhou stuff
|
||||||
return DEFAULT_MUSIC_FONT;
|
return DEFAULT_MUSIC_FONT;
|
||||||
}
|
}
|
||||||
function trimTrackTitle(title) {
|
function trimTrackTitle(title) {
|
||||||
var cleanedTitle = title;
|
if(!title) return '';
|
||||||
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning
|
const cleanRegexes = [
|
||||||
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD]
|
/【[^】]*】/, // Touhou n weeb stuff
|
||||||
return cleanedTitle.trim();
|
/\[FREE DOWNLOAD\]/, // F-777
|
||||||
|
];
|
||||||
|
cleanRegexes.forEach((expr) => title.replace(expr, ''));
|
||||||
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TrackProgress = ({ player, ...rest }) => {
|
const TrackProgress = ({ player, ...rest }) => {
|
||||||
|
|||||||
@@ -4,37 +4,7 @@ import Notifications from 'resource:///com/github/Aylur/ags/service/notification
|
|||||||
const { Box } = Widget;
|
const { Box } = Widget;
|
||||||
import Notification from '../../lib/notification.js';
|
import Notification from '../../lib/notification.js';
|
||||||
|
|
||||||
const PopupNotification = (notifObject) => Widget.Box({
|
export default () => 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({
|
|
||||||
vertical: true,
|
vertical: true,
|
||||||
className: 'osd-notifs spacing-v-5-revealer',
|
className: 'osd-notifs spacing-v-5-revealer',
|
||||||
attribute: {
|
attribute: {
|
||||||
@@ -72,5 +42,3 @@ const notifPopupList = Box({
|
|||||||
.hook(Notifications, (box, id) => box.attribute.dismiss(box, id, true), 'closed')
|
.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 Service from 'resource:///com/github/Aylur/ags/service.js';
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.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 { Box, EventBox, Button, Revealer } = Widget;
|
||||||
const { execAsync, exec } = Utils;
|
const { execAsync, exec } = Utils;
|
||||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||||
@@ -19,7 +18,7 @@ execAsync(`ydotoold`).catch(print); // Start ydotool daemon
|
|||||||
function releaseAllKeys() {
|
function releaseAllKeys() {
|
||||||
const keycodes = Array.from(Array(249).keys());
|
const keycodes = Array.from(Array(249).keys());
|
||||||
execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)])
|
execAsync([`ydotool`, `key`, ...keycodes.map(keycode => `${keycode}:0`)])
|
||||||
.then(console.log('Released all keys'))
|
.then(console.log('[OSK] Released all keys'))
|
||||||
.catch(print);
|
.catch(print);
|
||||||
}
|
}
|
||||||
var modsPressed = false;
|
var modsPressed = false;
|
||||||
@@ -146,22 +145,32 @@ const keyboardWindow = Box({
|
|||||||
|
|
||||||
const gestureEvBox = EventBox({ child: keyboardWindow })
|
const gestureEvBox = EventBox({ child: keyboardWindow })
|
||||||
const gesture = Gtk.GestureDrag.new(gestureEvBox);
|
const gesture = Gtk.GestureDrag.new(gestureEvBox);
|
||||||
gesture.connect('drag-begin', () => {
|
gesture.connect('drag-begin', async () => {
|
||||||
Hyprland.sendMessage('j/cursorpos').then((out) => {
|
try {
|
||||||
gesture.startY = JSON.parse(out).y;
|
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
||||||
}).catch(print);
|
Hyprland.sendMessage('j/cursorpos').then((out) => {
|
||||||
|
gesture.startY = JSON.parse(out).y;
|
||||||
|
}).catch(print);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
gesture.connect('drag-update', () => {
|
gesture.connect('drag-update', async () => {
|
||||||
Hyprland.sendMessage('j/cursorpos').then((out) => {
|
try {
|
||||||
const currentY = JSON.parse(out).y;
|
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
||||||
const offset = gesture.startY - currentY;
|
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;
|
margin-bottom: ${offset}px;
|
||||||
`);
|
`);
|
||||||
}).catch(print);
|
}).catch(print);
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
gesture.connect('drag-end', () => {
|
gesture.connect('drag-end', () => {
|
||||||
var offset = gesture.get_offset()[2];
|
var offset = gesture.get_offset()[2];
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ function moveClientToWorkspace(address, workspace) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function dumpToWorkspace(from, to) {
|
export function dumpToWorkspace(from, to) {
|
||||||
console.log('dump', from, to);
|
|
||||||
if (from == to) return;
|
if (from == to) return;
|
||||||
Hyprland.clients.forEach(client => {
|
Hyprland.clients.forEach(client => {
|
||||||
if (client.workspace.id == from) {
|
if (client.workspace.id == from) {
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ export function execAndClose(command, terminal) {
|
|||||||
execAsync(command).catch(print);
|
execAsync(command).catch(print);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startsWithNumber(str) {
|
export function couldBeMath(str) {
|
||||||
var pattern = /^\d/;
|
const regex = /^[0-9.+*/-]/;
|
||||||
return pattern.test(str);
|
return regex.test(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expandTilde(path) {
|
export function expandTilde(path) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const { Gdk, Gtk } = imports.gi;
|
const { Gdk, Gtk } = imports.gi;
|
||||||
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||||
import App from 'resource:///com/github/Aylur/ags/app.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 Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.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 OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07;
|
||||||
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
||||||
|
|
||||||
|
const overviewTick = Variable(false);
|
||||||
|
|
||||||
function truncateTitle(str) {
|
function truncateTitle(str) {
|
||||||
let lastDash = -1;
|
let lastDash = -1;
|
||||||
let found = -1; // 0: em dash, 1: en dash, 2: minus, 3: vertical bar, 4: middle dot
|
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.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
||||||
eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => {
|
eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => {
|
||||||
Hyprland.sendMessage(`dispatch movetoworkspacesilent ${index},address:${data.get_text()}`)
|
Hyprland.sendMessage(`dispatch movetoworkspacesilent ${index},address:${data.get_text()}`)
|
||||||
|
overviewTick.setValue(!overviewTick.value);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: fixed,
|
child: fixed,
|
||||||
@@ -275,18 +279,19 @@ const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) =>
|
|||||||
}).catch(print);
|
}).catch(print);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup: (box) => {
|
setup: (box) => box
|
||||||
box
|
.hook(overviewTick, (box) => box.attribute.update(box))
|
||||||
// .hook(Hyprland, (box, name, data) => { // idk, does this make it lag occasionally?
|
// .hook(Hyprland, (box, name, data) => { // idk, does this make it lag occasionally?
|
||||||
// if (["changefloatingmode", "movewindow"].includes(name))
|
// console.log(name)
|
||||||
// box.attribute.update(box);
|
// if (["changefloatingmode", "movewindow"].includes(name))
|
||||||
// }, 'event')
|
// box.attribute.update(box);
|
||||||
.hook(Hyprland, (box) => box.attribute.update(box), 'client-added')
|
// }, 'event')
|
||||||
.hook(Hyprland, (box) => box.attribute.update(box), 'client-removed')
|
.hook(Hyprland, (box) => box.attribute.update(box), 'client-added')
|
||||||
.hook(App, (box, name, visible) => { // Update on open
|
.hook(Hyprland, (box) => box.attribute.update(box), 'client-removed')
|
||||||
if (name == 'overview' && visible) box.attribute.update(box);
|
.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';
|
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||||
const { execAsync, exec } = Utils;
|
const { execAsync, exec } = Utils;
|
||||||
import { searchItem } from './searchitem.js';
|
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 }) => {
|
export const DirectoryButton = ({ parentPath, name, type, icon }) => {
|
||||||
const actionText = Widget.Revealer({
|
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';
|
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
||||||
const { execAsync, exec } = Utils;
|
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 {
|
import {
|
||||||
CalculationResultButton, CustomCommandButton, DirectoryButton,
|
CalculationResultButton, CustomCommandButton, DirectoryButton,
|
||||||
DesktopEntryButton, ExecuteCommandButton, SearchButton
|
DesktopEntryButton, ExecuteCommandButton, SearchButton
|
||||||
@@ -39,7 +39,7 @@ const OptionalOverview = async () => {
|
|||||||
try {
|
try {
|
||||||
return (await import('./overview_hyprland.js')).default();
|
return (await import('./overview_hyprland.js')).default();
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return Widget.Box({});
|
||||||
// return (await import('./overview_hyprland.js')).default();
|
// return (await import('./overview_hyprland.js')).default();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -104,7 +104,7 @@ export const SearchAndWindows = () => {
|
|||||||
const isAction = text.startsWith('>');
|
const isAction = text.startsWith('>');
|
||||||
const isDir = (['/', '~'].includes(entry.text[0]));
|
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 {
|
try {
|
||||||
const fullResult = eval(text);
|
const fullResult = eval(text);
|
||||||
// copy
|
// copy
|
||||||
@@ -166,7 +166,7 @@ export const SearchAndWindows = () => {
|
|||||||
_appSearchResults = Applications.query(text);
|
_appSearchResults = Applications.query(text);
|
||||||
|
|
||||||
// Calculate
|
// 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 {
|
try {
|
||||||
const fullResult = eval(text);
|
const fullResult = eval(text);
|
||||||
resultsBox.add(CalculationResultButton({ result: fullResult, text: 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 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();
|
export default (monitor = 0, where = 'bottom left') => {
|
||||||
const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion);
|
const positionString = where.replace(/\s/, ""); // remove space
|
||||||
|
return Widget.Window({
|
||||||
export const CornerTopleft = () => Widget.Window({
|
monitor,
|
||||||
name: 'cornertl',
|
name: `corner${positionString}${monitor}`,
|
||||||
layer: 'top',
|
layer: 'overlay',
|
||||||
anchor: ['top', 'left'],
|
anchor: where.split(' '),
|
||||||
exclusivity: 'normal',
|
exclusivity: 'ignore',
|
||||||
visible: true,
|
visible: true,
|
||||||
child: RoundedCorner('topleft', { className: 'corner', }),
|
child: RoundedCorner(positionString, { className: 'corner-black', }),
|
||||||
setup: enableClickthrough,
|
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,
|
|
||||||
});
|
|
||||||
+3
-2
@@ -223,7 +223,7 @@ const MessageContent = (content) => {
|
|||||||
return contentBox;
|
return contentBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatMessage = (message, scrolledWindow) => {
|
export const ChatMessage = (message, modelName = 'Model') => {
|
||||||
const messageContentBox = MessageContent(message.content);
|
const messageContentBox = MessageContent(message.content);
|
||||||
const thisMessage = Box({
|
const thisMessage = Box({
|
||||||
className: 'sidebar-chat-message',
|
className: 'sidebar-chat-message',
|
||||||
@@ -241,7 +241,8 @@ export const ChatMessage = (message, scrolledWindow) => {
|
|||||||
xalign: 0,
|
xalign: 0,
|
||||||
className: 'txt txt-bold sidebar-chat-name',
|
className: 'txt txt-bold sidebar-chat-name',
|
||||||
wrap: true,
|
wrap: true,
|
||||||
label: (message.role == 'user' ? USERNAME : 'ChatGPT'),
|
useMarkup: true,
|
||||||
|
label: (message.role == 'user' ? USERNAME : modelName),
|
||||||
}),
|
}),
|
||||||
messageContentBox,
|
messageContentBox,
|
||||||
],
|
],
|
||||||
@@ -8,63 +8,75 @@ const { execAsync, exec } = Utils;
|
|||||||
import ChatGPT from '../../../services/chatgpt.js';
|
import ChatGPT from '../../../services/chatgpt.js';
|
||||||
import { MaterialIcon } from "../../../lib/materialicon.js";
|
import { MaterialIcon } from "../../../lib/materialicon.js";
|
||||||
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.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 { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js';
|
||||||
import { markdownTest } from '../../../lib/md2pango.js';
|
import { markdownTest } from '../../../lib/md2pango.js';
|
||||||
import { MarginRevealer } from '../../../lib/advancedwidgets.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',
|
hpack: 'center',
|
||||||
className: 'sidebar-chat-apiswitcher-icon',
|
className: 'sidebar-chat-apiswitcher-icon',
|
||||||
homogeneous: true,
|
icon: `openai-symbolic`,
|
||||||
children: [
|
setup: (self) => Utils.timeout(513, () => { // stupid condition race
|
||||||
MaterialIcon('forum', 'norm'),
|
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({
|
const ChatGPTInfo = () => {
|
||||||
vertical: true,
|
const openAiLogo = Icon({
|
||||||
className: 'spacing-v-15',
|
hpack: 'center',
|
||||||
children: [
|
className: 'sidebar-chat-welcome-logo',
|
||||||
Icon({
|
icon: `openai-symbolic`,
|
||||||
hpack: 'center',
|
setup: (self) => Utils.timeout(513, () => { // stupid condition race
|
||||||
className: 'sidebar-chat-welcome-logo',
|
const styleContext = self.get_style_context();
|
||||||
icon: `${App.configDir}/assets/openai-logomark.svg`,
|
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||||
setup: (self) => Utils.timeout(1, () => {
|
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||||
const styleContext = self.get_style_context();
|
// console.log(Math.round(Math.max(width, height, 1)));
|
||||||
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
self.size = Math.max(width, height, 1) * 116 / 180;
|
||||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
// ↑ Why such a specific proportion? See https://openai.com/brand#logos
|
||||||
self.size = Math.max(width, height, 1) * 116 / 180; // Why such a specific proportion? See https://openai.com/brand#logos
|
})
|
||||||
})
|
});
|
||||||
}),
|
return Box({
|
||||||
Label({
|
vertical: true,
|
||||||
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
className: 'spacing-v-15',
|
||||||
wrap: true,
|
children: [
|
||||||
justify: Gtk.Justification.CENTER,
|
openAiLogo,
|
||||||
label: 'ChatGPT',
|
Label({
|
||||||
}),
|
className: 'txt txt-title-small sidebar-chat-welcome-txt',
|
||||||
Box({
|
wrap: true,
|
||||||
className: 'spacing-h-5',
|
justify: Gtk.Justification.CENTER,
|
||||||
hpack: 'center',
|
label: 'Assistant (ChatGPT 3.5)',
|
||||||
children: [
|
}),
|
||||||
Label({
|
Box({
|
||||||
className: 'txt-smallie txt-subtext',
|
className: 'spacing-h-5',
|
||||||
wrap: true,
|
hpack: 'center',
|
||||||
justify: Gtk.Justification.CENTER,
|
children: [
|
||||||
label: 'Powered by OpenAI',
|
Label({
|
||||||
}),
|
className: 'txt-smallie txt-subtext',
|
||||||
Button({
|
wrap: true,
|
||||||
className: 'txt-subtext txt-norm icon-material',
|
justify: Gtk.Justification.CENTER,
|
||||||
label: 'info',
|
label: 'Powered by OpenAI',
|
||||||
tooltipText: 'Uses gpt-3.5-turbo.\nNot affiliated, endorsed, or sponsored by OpenAI.',
|
}),
|
||||||
setup: setupCursorHoverInfo,
|
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',
|
transition: 'slide_down',
|
||||||
revealChild: true,
|
revealChild: true,
|
||||||
extraSetup: (self) => self
|
extraSetup: (self) => self
|
||||||
@@ -89,7 +101,7 @@ export const chatGPTSettings = MarginRevealer({
|
|||||||
{ value: 0.50, name: 'Balanced', },
|
{ value: 0.50, name: 'Balanced', },
|
||||||
{ value: 1.00, name: 'Creative', },
|
{ value: 1.00, name: 'Creative', },
|
||||||
],
|
],
|
||||||
initIndex: 1,
|
initIndex: 2,
|
||||||
onChange: (value, name) => {
|
onChange: (value, name) => {
|
||||||
ChatGPT.temperature = value;
|
ChatGPT.temperature = value;
|
||||||
},
|
},
|
||||||
@@ -111,8 +123,8 @@ export const chatGPTSettings = MarginRevealer({
|
|||||||
}),
|
}),
|
||||||
ConfigToggle({
|
ConfigToggle({
|
||||||
icon: 'description',
|
icon: 'description',
|
||||||
name: 'Assistant prompt',
|
name: 'Enhancements',
|
||||||
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.',
|
desc: 'Tells ChatGPT:\n- It\'s a Linux sidebar assistant\n- Be brief and use bullet points',
|
||||||
initValue: ChatGPT.assistantPrompt,
|
initValue: ChatGPT.assistantPrompt,
|
||||||
onChange: (self, newValue) => {
|
onChange: (self, newValue) => {
|
||||||
ChatGPT.assistantPrompt = newValue;
|
ChatGPT.assistantPrompt = newValue;
|
||||||
@@ -124,7 +136,7 @@ export const chatGPTSettings = MarginRevealer({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const openaiApiKeyInstructions = Box({
|
export const OpenaiApiKeyInstructions = () => Box({
|
||||||
homogeneous: true,
|
homogeneous: true,
|
||||||
children: [Revealer({
|
children: [Revealer({
|
||||||
transition: 'slide_down',
|
transition: 'slide_down',
|
||||||
@@ -150,7 +162,7 @@ export const openaiApiKeyInstructions = Box({
|
|||||||
})]
|
})]
|
||||||
});
|
});
|
||||||
|
|
||||||
export const chatGPTWelcome = Box({
|
const chatGPTWelcome = Box({
|
||||||
vexpand: true,
|
vexpand: true,
|
||||||
homogeneous: true,
|
homogeneous: true,
|
||||||
child: Box({
|
child: Box({
|
||||||
@@ -158,9 +170,9 @@ export const chatGPTWelcome = Box({
|
|||||||
vpack: 'center',
|
vpack: 'center',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: [
|
children: [
|
||||||
chatGPTInfo,
|
ChatGPTInfo(),
|
||||||
openaiApiKeyInstructions,
|
OpenaiApiKeyInstructions(),
|
||||||
chatGPTSettings, ``
|
ChatGPTSettings(),
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -172,7 +184,7 @@ export const chatContent = Box({
|
|||||||
.hook(ChatGPT, (box, id) => {
|
.hook(ChatGPT, (box, id) => {
|
||||||
const message = ChatGPT.messages[id];
|
const message = ChatGPT.messages[id];
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
box.add(ChatMessage(message, chatGPTView))
|
box.add(ChatMessage(message, 'ChatGPT'))
|
||||||
}, 'newMsg')
|
}, '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;
|
const { execAsync, exec } = Utils;
|
||||||
import { MaterialIcon } from "../../../lib/materialicon.js";
|
import { MaterialIcon } from "../../../lib/materialicon.js";
|
||||||
import { MarginRevealer } from '../../../lib/advancedwidgets.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';
|
import WaifuService from '../../../services/waifus.js';
|
||||||
|
|
||||||
async function getImageViewerApp(preferredApp) {
|
async function getImageViewerApp(preferredApp) {
|
||||||
@@ -162,7 +162,8 @@ const WaifuImage = (taglist) => {
|
|||||||
blockImage.set_size_request(widgetWidth, widgetHeight);
|
blockImage.set_size_request(widgetWidth, widgetHeight);
|
||||||
const showImage = () => {
|
const showImage = () => {
|
||||||
downloadState.shown = 'done';
|
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.set_size_request(widgetWidth, widgetHeight);
|
||||||
blockImage.connect("draw", (widget, cr) => {
|
blockImage.connect("draw", (widget, cr) => {
|
||||||
@@ -220,10 +221,62 @@ const WaifuImage = (taglist) => {
|
|||||||
return thisBlock;
|
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({
|
const waifuContent = Box({
|
||||||
className: 'spacing-v-15',
|
className: 'spacing-v-15',
|
||||||
vertical: true,
|
vertical: true,
|
||||||
vexpand: true,
|
|
||||||
attribute: {
|
attribute: {
|
||||||
'map': new Map(),
|
'map': new Map(),
|
||||||
},
|
},
|
||||||
@@ -251,6 +304,7 @@ export const waifuView = Scrollable({
|
|||||||
child: Box({
|
child: Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: [
|
children: [
|
||||||
|
waifuWelcome,
|
||||||
waifuContent,
|
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) => {
|
export const sendMessage = (text) => {
|
||||||
// Do something on send
|
// Do something on send
|
||||||
// Commands
|
// Commands
|
||||||
@@ -335,20 +404,20 @@ export const sendMessage = (text) => {
|
|||||||
else if (text.startsWith('/test')) {
|
else if (text.startsWith('/test')) {
|
||||||
const newImage = WaifuImage(['/test']);
|
const newImage = WaifuImage(['/test']);
|
||||||
waifuContent.add(newImage);
|
waifuContent.add(newImage);
|
||||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update({ // Needs timeout or inits won't make it
|
Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update(
|
||||||
// This is an image uploaded to my github repo
|
DummyTag(300, 200, 'https://picsum.photos/600/400'),
|
||||||
status: 200,
|
true
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
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);
|
else WaifuService.fetch(text);
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,32 @@
|
|||||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.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;
|
const { execAsync, exec } = Utils;
|
||||||
import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js";
|
import { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js";
|
||||||
// APIs
|
// APIs
|
||||||
import ChatGPT from '../../services/chatgpt.js';
|
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 { chatGPTView, chatGPTCommands, sendMessage as chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
|
||||||
import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js';
|
import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js';
|
||||||
|
|
||||||
const APIS = [
|
const APIS = [
|
||||||
{
|
{
|
||||||
name: 'ChatGPT',
|
name: 'Assistant (ChatGPT 3.5)',
|
||||||
sendCommand: chatGPTSendMessage,
|
sendCommand: chatGPTSendMessage,
|
||||||
contentWidget: chatGPTView,
|
contentWidget: chatGPTView,
|
||||||
commandBar: chatGPTCommands,
|
commandBar: chatGPTCommands,
|
||||||
tabIcon: chatGPTTabIcon,
|
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',
|
name: 'Waifus',
|
||||||
@@ -35,8 +45,12 @@ export const chatEntry = Entry({
|
|||||||
hexpand: true,
|
hexpand: true,
|
||||||
setup: (self) => self
|
setup: (self) => self
|
||||||
.hook(ChatGPT, (self) => {
|
.hook(ChatGPT, (self) => {
|
||||||
if (APIS[currentApiId].name != 'ChatGPT') return;
|
if (APIS[currentApiId].name != 'Assistant (ChatGPT 3.5)') return;
|
||||||
self.placeholderText = (ChatGPT.key.length > 0 ? 'Ask a question...' : 'Enter OpenAI API Key...');
|
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')
|
}, 'hasKey')
|
||||||
,
|
,
|
||||||
onChange: (entry) => {
|
onChange: (entry) => {
|
||||||
@@ -86,22 +100,27 @@ function switchToTab(id) {
|
|||||||
chatEntry.placeholderText = APIS[id].placeholderText,
|
chatEntry.placeholderText = APIS[id].placeholderText,
|
||||||
currentApiId = id;
|
currentApiId = id;
|
||||||
}
|
}
|
||||||
const apiSwitcher = Box({
|
|
||||||
homogeneous: true,
|
const apiSwitcher = CenterBox({
|
||||||
children: [
|
centerWidget: Box({
|
||||||
Box({
|
className: 'sidebar-chat-apiswitcher spacing-h-5',
|
||||||
className: 'sidebar-chat-apiswitcher spacing-h-5',
|
hpack: 'center',
|
||||||
hpack: 'center',
|
children: APIS.map((api, id) => Button({
|
||||||
children: APIS.map((api, id) => Button({
|
child: api.tabIcon,
|
||||||
child: api.tabIcon,
|
tooltipText: api.name,
|
||||||
tooltipText: api.name,
|
setup: setupCursorHover,
|
||||||
setup: setupCursorHover,
|
onClicked: () => {
|
||||||
onClicked: () => {
|
switchToTab(id);
|
||||||
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({
|
export default Widget.Box({
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export default () => PopupWindow({
|
|||||||
focusable: true,
|
focusable: true,
|
||||||
anchor: ['left', 'top', 'bottom'],
|
anchor: ['left', 'top', 'bottom'],
|
||||||
name: 'sideleft',
|
name: 'sideleft',
|
||||||
// exclusivity: 'exclusive',
|
layer: 'top',
|
||||||
showClassName: 'sideleft-show',
|
showClassName: 'sideleft-show',
|
||||||
hideClassName: 'sideleft-hide',
|
hideClassName: 'sideleft-hide',
|
||||||
child: SidebarLeft(),
|
child: SidebarLeft(),
|
||||||
|
|||||||
@@ -102,22 +102,14 @@ const pinButton = Button({
|
|||||||
self.toggleClassName('sidebar-pin-enabled', self.attribute.enabled);
|
self.toggleClassName('sidebar-pin-enabled', self.attribute.enabled);
|
||||||
|
|
||||||
const sideleftWindow = App.getWindow('sideleft');
|
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];
|
const sideleftContent = sideleftWindow.get_children()[0].get_children()[0].get_children()[1];
|
||||||
|
|
||||||
sideleftContent.toggleClassName('sidebar-pinned', self.attribute.enabled);
|
sideleftContent.toggleClassName('sidebar-pinned', self.attribute.enabled);
|
||||||
|
|
||||||
if (self.attribute.enabled) {
|
if (self.attribute.enabled) {
|
||||||
sideleftWindow.layer = 'bottom';
|
|
||||||
barWindow.layer = 'bottom';
|
|
||||||
cornerTopLeftWindow.layer = 'bottom';
|
|
||||||
sideleftWindow.exclusivity = 'exclusive';
|
sideleftWindow.exclusivity = 'exclusive';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sideleftWindow.layer = 'top';
|
|
||||||
barWindow.layer = 'top';
|
|
||||||
cornerTopLeftWindow.layer = 'top';
|
|
||||||
sideleftWindow.exclusivity = 'normal';
|
sideleftWindow.exclusivity = 'normal';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default Scrollable({
|
|||||||
child: Box({
|
child: Box({
|
||||||
vertical: true,
|
vertical: true,
|
||||||
children: [
|
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 Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
||||||
import Network from 'resource:///com/github/Aylur/ags/service/network.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;
|
const { execAsync, exec } = Utils;
|
||||||
import { BluetoothIndicator, NetworkIndicator } from "../../lib/statusicons.js";
|
import { BluetoothIndicator, NetworkIndicator } from "../../lib/statusicons.js";
|
||||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||||
@@ -12,7 +11,6 @@ import { MaterialIcon } from '../../lib/materialicon.js';
|
|||||||
|
|
||||||
function expandTilde(path) {
|
function expandTilde(path) {
|
||||||
if (path.startsWith('~')) {
|
if (path.startsWith('~')) {
|
||||||
console.log(GLib.get_home_dir() + path.slice(1));
|
|
||||||
return GLib.get_home_dir() + path.slice(1);
|
return GLib.get_home_dir() + path.slice(1);
|
||||||
} else {
|
} else {
|
||||||
return path;
|
return path;
|
||||||
@@ -62,24 +60,30 @@ export const ToggleIconBluetooth = (props = {}) => Widget.Button({
|
|||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const HyprToggleIcon = (icon, name, hyprlandConfigValue, props = {}) => Widget.Button({
|
export const HyprToggleIcon = async (icon, name, hyprlandConfigValue, props = {}) => {
|
||||||
className: 'txt-small sidebar-iconbutton',
|
try {
|
||||||
tooltipText: `${name}`,
|
return Widget.Button({
|
||||||
onClicked: (button) => {
|
className: 'txt-small sidebar-iconbutton',
|
||||||
// Set the value to 1 - value
|
tooltipText: `${name}`,
|
||||||
Utils.execAsync(`hyprctl -j getoption ${hyprlandConfigValue}`).then((result) => {
|
onClicked: (button) => {
|
||||||
const currentOption = JSON.parse(result).int;
|
// Set the value to 1 - value
|
||||||
execAsync(['bash', '-c', `hyprctl keyword ${hyprlandConfigValue} ${1 - currentOption} &`]).catch(print);
|
Utils.execAsync(`hyprctl -j getoption ${hyprlandConfigValue}`).then((result) => {
|
||||||
button.toggleClassName('sidebar-button-active', currentOption == 0);
|
const currentOption = JSON.parse(result).int;
|
||||||
}).catch(print);
|
execAsync(['bash', '-c', `hyprctl keyword ${hyprlandConfigValue} ${1 - currentOption} &`]).catch(print);
|
||||||
},
|
button.toggleClassName('sidebar-button-active', currentOption == 0);
|
||||||
child: MaterialIcon(icon, 'norm', { hpack: 'center' }),
|
}).catch(print);
|
||||||
setup: button => {
|
},
|
||||||
button.toggleClassName('sidebar-button-active', JSON.parse(Utils.exec(`hyprctl -j getoption ${hyprlandConfigValue}`)).int == 1);
|
child: MaterialIcon(icon, 'norm', { hpack: 'center' }),
|
||||||
setupCursorHover(button);
|
setup: button => {
|
||||||
},
|
button.toggleClassName('sidebar-button-active', JSON.parse(Utils.exec(`hyprctl -j getoption ${hyprlandConfigValue}`)).int == 1);
|
||||||
...props,
|
setupCursorHover(button);
|
||||||
})
|
},
|
||||||
|
...props,
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const ModuleNightLight = (props = {}) => Widget.Button({ // TODO: Make this work
|
export const ModuleNightLight = (props = {}) => Widget.Button({ // TODO: Make this work
|
||||||
attribute: {
|
attribute: {
|
||||||
@@ -104,31 +108,36 @@ export const ModuleNightLight = (props = {}) => Widget.Button({ // TODO: Make th
|
|||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ModuleInvertColors = (props = {}) => Widget.Button({
|
export const ModuleInvertColors = async (props = {}) => {
|
||||||
className: 'txt-small sidebar-iconbutton',
|
try {
|
||||||
tooltipText: 'Color inversion',
|
const Hyprland = (await import('resource:///com/github/Aylur/ags/service/hyprland.js')).default;
|
||||||
onClicked: (button) => {
|
return Widget.Button({
|
||||||
// const shaderPath = JSON.parse(exec('hyprctl -j getoption decoration:screen_shader')).str;
|
className: 'txt-small sidebar-iconbutton',
|
||||||
Hyprland.sendMessage('j/getoption decoration:screen_shader')
|
tooltipText: 'Color inversion',
|
||||||
.then((output) => {
|
onClicked: (button) => {
|
||||||
const shaderPath = JSON.parse(output)["str"].trim();
|
// const shaderPath = JSON.parse(exec('hyprctl -j getoption decoration:screen_shader')).str;
|
||||||
console.log(output)
|
Hyprland.sendMessage('j/getoption decoration:screen_shader')
|
||||||
console.log(shaderPath)
|
.then((output) => {
|
||||||
if (shaderPath != "[[EMPTY]]" && shaderPath != "") {
|
const shaderPath = JSON.parse(output)["str"].trim();
|
||||||
execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader '[[EMPTY]]'`]).catch(print);
|
if (shaderPath != "[[EMPTY]]" && shaderPath != "") {
|
||||||
button.toggleClassName('sidebar-button-active', false);
|
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')}`)
|
else {
|
||||||
.catch(print);
|
Hyprland.sendMessage(`j/keyword decoration:screen_shader ${expandTilde('~/.config/hypr/shaders/invert.frag')}`)
|
||||||
button.toggleClassName('sidebar-button-active', true);
|
.catch(print);
|
||||||
}
|
button.toggleClassName('sidebar-button-active', true);
|
||||||
})
|
}
|
||||||
},
|
})
|
||||||
child: MaterialIcon('invert_colors', 'norm'),
|
},
|
||||||
setup: setupCursorHover,
|
child: MaterialIcon('invert_colors', 'norm'),
|
||||||
...props,
|
setup: setupCursorHover,
|
||||||
})
|
...props,
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const ModuleIdleInhibitor = (props = {}) => Widget.Button({ // TODO: Make this work
|
export const ModuleIdleInhibitor = (props = {}) => Widget.Button({ // TODO: Make this work
|
||||||
attribute: {
|
attribute: {
|
||||||
@@ -173,9 +182,9 @@ export const ModuleEditIcon = (props = {}) => Widget.Button({ // TODO: Make this
|
|||||||
export const ModuleReloadIcon = (props = {}) => Widget.Button({
|
export const ModuleReloadIcon = (props = {}) => Widget.Button({
|
||||||
...props,
|
...props,
|
||||||
className: 'txt-small sidebar-iconbutton',
|
className: 'txt-small sidebar-iconbutton',
|
||||||
tooltipText: 'Reload Hyprland',
|
tooltipText: 'Reload Environment config',
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
execAsync(['bash', '-c', 'hyprctl reload &']);
|
execAsync(['bash', '-c', 'hyprctl reload || swaymsg reload &']);
|
||||||
App.toggleWindow('sideright');
|
App.toggleWindow('sideright');
|
||||||
},
|
},
|
||||||
child: MaterialIcon('refresh', 'norm'),
|
child: MaterialIcon('refresh', 'norm'),
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ const togglesBox = Widget.Box({
|
|||||||
children: [
|
children: [
|
||||||
ToggleIconWifi(),
|
ToggleIconWifi(),
|
||||||
ToggleIconBluetooth(),
|
ToggleIconBluetooth(),
|
||||||
HyprToggleIcon('mouse', 'Raw input', 'input:force_no_accel', {}),
|
await HyprToggleIcon('mouse', 'Raw input', 'input:force_no_accel', {}),
|
||||||
HyprToggleIcon('front_hand', 'No touchpad while typing', 'input:touchpad:disable_while_typing', {}),
|
await HyprToggleIcon('front_hand', 'No touchpad while typing', 'input:touchpad:disable_while_typing', {}),
|
||||||
ModuleNightLight(),
|
ModuleNightLight(),
|
||||||
ModuleInvertColors(),
|
await ModuleInvertColors(),
|
||||||
ModuleIdleInhibitor(),
|
ModuleIdleInhibitor(),
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user