forked from Shinonome/dots-hyprland
ags: sync
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
"strict mode";
|
||||
"use strict";
|
||||
// Import
|
||||
import { App, Utils } from './imports.js';
|
||||
const { GLib } = imports.gi;
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js'
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
||||
// Widgets
|
||||
import Bar from './widgets/bar/main.js';
|
||||
import Cheatsheet from './widgets/cheatsheet/main.js';
|
||||
// import DesktopBackground from './widgets/desktopbackground/main.js';
|
||||
import DesktopBackground from './widgets/desktopbackground/main.js';
|
||||
// import Dock from './widgets/dock/main.js';
|
||||
import { CornerTopleft, CornerTopright, CornerBottomleft, CornerBottomright } from './widgets/screencorners/main.js';
|
||||
import Indicator from './widgets/indicators/main.js';
|
||||
@@ -16,8 +18,6 @@ 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
|
||||
|
||||
// Init cache
|
||||
Utils.exec(`bash -c 'mkdir -p ~/.cache/ags/user/colorschemes'`);
|
||||
// SCSS compilation
|
||||
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles
|
||||
Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicmaterial.scss'`); // reset music styles
|
||||
@@ -43,8 +43,8 @@ export default {
|
||||
CornerTopright(),
|
||||
CornerBottomleft(),
|
||||
CornerBottomright(),
|
||||
// DesktopBackground(),
|
||||
// Dock(), // Buggy
|
||||
DesktopBackground(), // If you're going to uncomment these,
|
||||
// Dock(), // Buggy // uncomment the import statement too.
|
||||
Overview(),
|
||||
Indicator(),
|
||||
Cheatsheet(),
|
||||
|
||||
@@ -29,7 +29,7 @@ globalThis['Utils'] = Utils; ///////////////////////////
|
||||
// globalThis['Bluetooth'] = Bluetooth;
|
||||
// globalThis['Hyprland'] = Hyprland;
|
||||
globalThis['Mpris'] = Mpris;
|
||||
// globalThis['Network'] = Network;
|
||||
globalThis['Network'] = Network;
|
||||
globalThis['Notifications'] = Notifications;
|
||||
// globalThis['SystemTray'] = SystemTray;
|
||||
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
// Not yet used. For cool drag and drop stuff. Thanks DevAlien
|
||||
|
||||
const Toggles = {};
|
||||
Toggles.Wifi = NetworkToggle;
|
||||
Toggles.Bluetooth = BluetoothToggle;
|
||||
Toggles.DND = DNDToggle;
|
||||
Toggles.ThemeToggle = ThemeToggle;
|
||||
Toggles.ProfileToggle = ProfileToggle;
|
||||
// Toggles.Record = RecordToggle;
|
||||
// Toggles.Airplane = AirplaneToggle;
|
||||
// Toggles.DoNotDisturb = DoNotDisturbToggle;
|
||||
const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)];
|
||||
|
||||
export class ActionCenter extends Gtk.Box {
|
||||
static {
|
||||
GObject.registerClass({
|
||||
GTypeName: 'ActionCenter',
|
||||
Properties: {
|
||||
|
||||
},
|
||||
}, this);
|
||||
}
|
||||
|
||||
constructor({ className = "ActionCenter", toggles, ...rest }) {
|
||||
super(rest);
|
||||
this.toggles = Toggles
|
||||
this.currentToggles = Settings.getSetting("toggles", []);
|
||||
this.mainFlowBox = this._setupFlowBox(className + QSView.editing && className + "Editing");
|
||||
this.mainFlowBox.connect("drag_motion", this._dragMotionMain);
|
||||
this.mainFlowBox.connect("drag_drop", this._dragDropMain);
|
||||
this._dragged = {};
|
||||
this._draggedExtra = {};
|
||||
|
||||
this._dragged;
|
||||
this._currentPosition = 0;
|
||||
this._orderedState;
|
||||
this._draggedName;
|
||||
|
||||
this.updateList(toggles, this.mainFlowBox)
|
||||
|
||||
this.set_orientation(Gtk.Orientation.VERTICAL);
|
||||
this.add(this.mainFlowBox)
|
||||
this.mainFlowBox.set_size_request(1, 30)
|
||||
if (QSView.editing) {
|
||||
this.extraFlowBox = this._setupFlowBox(className);
|
||||
this.extraFlowBox.connect("drag_motion", this._dragMotionExtra);
|
||||
this.extraFlowBox.connect("drag_drop", this._dragDropExtra);
|
||||
this.updateList(this._getExtraToggles(), this.extraFlowBox)
|
||||
this.add(Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Label("Extra widgets"),
|
||||
Label("Drop here to remove or drag from here to add"),
|
||||
this.extraFlowBox
|
||||
]
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
_getExtraToggles() {
|
||||
let toggles = { ...this.toggles }
|
||||
this.currentToggles.map(t => {
|
||||
if (toggles[t]) {
|
||||
delete toggles[t];
|
||||
}
|
||||
});
|
||||
return Object.keys(toggles);
|
||||
}
|
||||
|
||||
_setupFlowBox(className) {
|
||||
const flowBox = new Gtk.FlowBox();
|
||||
flowBox.set_valign(Gtk.Align.FILL);
|
||||
flowBox.set_min_children_per_line(2);
|
||||
flowBox.set_max_children_per_line(2);
|
||||
flowBox.set_selection_mode(Gtk.SelectionMode.NONE);
|
||||
flowBox.get_style_context().add_class(className);
|
||||
flowBox.set_homogeneous(true);
|
||||
flowBox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
||||
|
||||
return flowBox;
|
||||
}
|
||||
|
||||
createWidget = (name, index, type) => {
|
||||
const editSetup = (widget) => {
|
||||
widget.drag_source_set(
|
||||
Gdk.ModifierType.BUTTON1_MASK,
|
||||
TARGET,
|
||||
Gdk.DragAction.COPY
|
||||
);
|
||||
|
||||
widget.connect("drag-begin", (w, context) => {
|
||||
const widgetContainer = widget.get_parent();
|
||||
|
||||
Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(widgetContainer));
|
||||
this._dragged = {
|
||||
widget: widgetContainer.get_parent().get_parent(),
|
||||
container: widgetContainer,
|
||||
name: name,
|
||||
currentPosition: type === "Main" ? index : null,
|
||||
currentPositionExtra: type === "Extra" ? index : null,
|
||||
from: type,
|
||||
}
|
||||
widgetContainer.get_style_context().add_class("hidden");
|
||||
if (type !== "Main") {
|
||||
this.extraFlowBox.remove(this._dragged.widget);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
});
|
||||
widget.connect("drag-failed", () => {
|
||||
this.updateList(Settings.getSetting("toggles"), this.mainFlowBox)
|
||||
this.updateList(this._getExtraToggles(), this.extraFlowBox)
|
||||
});
|
||||
}
|
||||
|
||||
let row = new Gtk.FlowBoxChild({ visible: true });
|
||||
row.add(Toggles[name]({ setup: QSView.editing && editSetup, QSView: QSView }));
|
||||
row._index = index;
|
||||
row._name = name;
|
||||
return row;
|
||||
}
|
||||
|
||||
updateList(toggles, flowBox) {
|
||||
let type = flowBox === this.mainFlowBox ? "Main" : "Extra"
|
||||
var childrenBox = flowBox.get_children();
|
||||
childrenBox.forEach((element) => {
|
||||
flowBox.remove(element);
|
||||
element.destroy();
|
||||
});
|
||||
|
||||
if (!toggles) return;
|
||||
|
||||
toggles.forEach((name, i) => {
|
||||
if (Toggles[name])
|
||||
flowBox.add(this.createWidget(name, i, type));
|
||||
});
|
||||
flowBox.show_all();
|
||||
}
|
||||
|
||||
|
||||
_dragMotionMain = (widget, context, x, y, time) => {
|
||||
if (this._dragged.currentPositionExtra !== null) {
|
||||
this._dragged.currentPositionExtra = null;
|
||||
if (this._isChild(this.extraFlowBox, this._dragged.widget)) {
|
||||
this.extraFlowBox.remove(this._dragged.widget);
|
||||
}
|
||||
}
|
||||
const children = this.mainFlowBox.get_children();
|
||||
const sampleItem = children[0];
|
||||
const sampleWidth = sampleItem.get_allocation().width;
|
||||
const sampleHeight = sampleItem.get_allocated_height();
|
||||
const perLine = Math.floor(this.mainFlowBox.get_allocation().width / sampleWidth);
|
||||
const pos = Math.floor(y / sampleHeight) * perLine + Math.floor(x / sampleWidth);
|
||||
if (pos >= children.length && pos !== 0) return false;
|
||||
|
||||
if (this._dragged.currentPosition === null) {
|
||||
this.mainFlowBox.insert(this._dragged.widget, pos);
|
||||
|
||||
this._dragged.currentPosition = pos;
|
||||
} else if (this._dragged.currentPosition !== pos) {
|
||||
if (this._isChild(this.mainFlowBox, this._dragged.widget)) {
|
||||
this.mainFlowBox.remove(this._dragged.widget);
|
||||
}
|
||||
|
||||
this.mainFlowBox.insert(this._dragged.widget, pos);
|
||||
|
||||
this._dragged.currentPosition = pos;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_dragDropMain = () => {
|
||||
if (this._dragged.from !== "Main") {
|
||||
this.currentToggles.splice(this._dragged.currentPosition, 0, this._dragged.name);
|
||||
} else {
|
||||
const indexCurrentToggle = this.currentToggles.indexOf(this._dragged.name);
|
||||
this.currentToggles.splice(indexCurrentToggle, 1);
|
||||
this.currentToggles.splice(this._dragged.currentPosition, 0, this._dragged.name);
|
||||
}
|
||||
|
||||
Settings.setSetting("toggles", this.currentToggles);
|
||||
this._dragged.container.get_style_context().remove_class("hidden");
|
||||
return true;
|
||||
}
|
||||
|
||||
_dragDropExtra = () => {
|
||||
if (this._dragged.from === "Main") {
|
||||
const indexCurrentToggle = this.currentToggles.indexOf(this._dragged.name);
|
||||
this.currentToggles.splice(indexCurrentToggle, 1);
|
||||
}
|
||||
|
||||
Settings.setSetting("toggles", this.currentToggles);
|
||||
this._dragged.container.get_style_context().remove_class("hidden");
|
||||
return true;
|
||||
}
|
||||
|
||||
_dragMotionExtra = (widget, context, x, y, time) => {
|
||||
if (this._dragged.currentPosition !== null) {
|
||||
this._dragged.currentPosition = null;
|
||||
if (this._isChild(this.mainFlowBox, this._dragged.widget)) {
|
||||
this.mainFlowBox.remove(this._dragged.widget);
|
||||
}
|
||||
}
|
||||
|
||||
const children = this.extraFlowBox.get_children();
|
||||
const sampleItem = children[0];
|
||||
let pos = 0;
|
||||
if (sampleItem) {
|
||||
const sampleWidth = sampleItem.get_allocation().width;
|
||||
const sampleHeight = sampleItem.get_allocated_height();
|
||||
const perLine = Math.floor(this.extraFlowBox.get_allocation().width / sampleWidth);
|
||||
pos = Math.floor(y / sampleHeight) * perLine + Math.floor(x / sampleWidth);
|
||||
}
|
||||
|
||||
if (pos >= children.length && pos !== 0) return false;
|
||||
|
||||
if (this._dragged.currentPositionExtra === null) {
|
||||
this.extraFlowBox.insert(this._dragged.widget, pos);
|
||||
|
||||
this._dragged.currentPositionExtra = pos;
|
||||
}
|
||||
|
||||
if (this._dragged.currentPositionExtra !== pos) {
|
||||
if (this._isChild(this.extraFlowBox, this._dragged.widget)) {
|
||||
this.extraFlowBox.remove(this._dragged.widget);
|
||||
}
|
||||
|
||||
this.extraFlowBox.insert(this._dragged.widget, pos);
|
||||
|
||||
this._dragged.currentPositionExtra = pos;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_isChild(container, widget) {
|
||||
let found = false;
|
||||
container.get_children().forEach((c) => {
|
||||
if (c === widget) found = true;
|
||||
})
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, SCREEN_WIDTH, SCREEN_HEIGHT, Service, Utils, Variable, Widget } from '../imports.js';
|
||||
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
const { Revealer, Scrollable } = Widget;
|
||||
|
||||
export const MarginRevealer = ({
|
||||
transition = 'slide_down',
|
||||
@@ -8,51 +8,49 @@ export const MarginRevealer = ({
|
||||
revealChild,
|
||||
showClass = 'element-show', // These are for animation curve, they don't really hide
|
||||
hideClass = 'element-hide', // Don't put margins in these classes!
|
||||
extraProperties = [],
|
||||
extraSetup = () => { },
|
||||
...rest
|
||||
}) => {
|
||||
const widget = Scrollable({
|
||||
...rest,
|
||||
css: `min-height: 0px;`,
|
||||
properties: [
|
||||
['revealChild', true], // It'll be set to false after init if it's supposed to hide
|
||||
['transition', transition],
|
||||
['show', (self) => {
|
||||
if (self._revealChild) return;
|
||||
self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER);
|
||||
attribute: {
|
||||
'revealChild': true, // It'll be set to false after init if it's supposed to hide
|
||||
'transition': transition,
|
||||
'show': () => {
|
||||
if (widget.attribute.revealChild) return;
|
||||
widget.hscroll = 'never';
|
||||
widget.vscroll = 'never';
|
||||
child.toggleClassName(hideClass, false);
|
||||
child.toggleClassName(showClass, true);
|
||||
self._revealChild = true;
|
||||
widget.attribute.revealChild = true;
|
||||
child.css = 'margin: 0px;';
|
||||
}],
|
||||
['hide', (self) => {
|
||||
if (!self._revealChild) return;
|
||||
},
|
||||
'hide': () => {
|
||||
if (!widget.attribute.revealChild) return;
|
||||
child.toggleClassName(hideClass, true);
|
||||
child.toggleClassName(showClass, false);
|
||||
self._revealChild = false;
|
||||
if (self._transition == 'slide_left')
|
||||
widget.attribute.revealChild = false;
|
||||
if (widget.attribute.transition == 'slide_left')
|
||||
child.css = `margin-right: -${child.get_allocated_width()}px;`;
|
||||
else if (self._transition == 'slide_right')
|
||||
else if (widget.attribute.transition == 'slide_right')
|
||||
child.css = `margin-left: -${child.get_allocated_width()}px;`;
|
||||
else if (self._transition == 'slide_up')
|
||||
else if (widget.attribute.transition == 'slide_up')
|
||||
child.css = `margin-bottom: -${child.get_allocated_height()}px;`;
|
||||
else if (self._transition == 'slide_down')
|
||||
else if (widget.attribute.transition == 'slide_down')
|
||||
child.css = `margin-top: -${child.get_allocated_height()}px;`;
|
||||
}],
|
||||
['toggle', (self) => {
|
||||
},
|
||||
'toggle': () => {
|
||||
console.log('toggle');
|
||||
if (self._revealChild) self._hide(self);
|
||||
else self._show(self);
|
||||
}],
|
||||
...extraProperties,
|
||||
],
|
||||
setup: (self) => {
|
||||
if (!revealChild)
|
||||
self.set_policy(Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS);
|
||||
else
|
||||
self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER);
|
||||
self.child = child;
|
||||
if (widget.attribute.revealChild) widget.attribute.hide();
|
||||
else widget.attribute.show();
|
||||
},
|
||||
},
|
||||
child: child,
|
||||
hscroll: `${revealChild ? 'never' : 'always'}`,
|
||||
vscroll: `${revealChild ? 'never' : 'always'}`,
|
||||
setup: (self) => {
|
||||
extraSetup(self);
|
||||
}
|
||||
});
|
||||
child.toggleClassName(`${revealChild ? showClass : hideClass}`, true);
|
||||
return widget;
|
||||
@@ -1,7 +1,7 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
const GObject = imports.gi.GObject;
|
||||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
import { Utils, Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
||||
|
||||
// -- Styling --
|
||||
// min-height for diameter
|
||||
@@ -17,6 +17,7 @@ export const AnimatedCircProg = ({
|
||||
initTo = 0,
|
||||
initAnimTime = 2900,
|
||||
initAnimPoints = 1,
|
||||
extraSetup = () => { },
|
||||
...rest
|
||||
}) => Widget.DrawingArea({
|
||||
...rest,
|
||||
@@ -100,5 +101,6 @@ export const AnimatedCircProg = ({
|
||||
// }
|
||||
}
|
||||
else area.css = 'font-size: 0px;';
|
||||
extraSetup(area);
|
||||
},
|
||||
})
|
||||
@@ -1,5 +1,3 @@
|
||||
const { GLib, Gio } = imports.gi;
|
||||
|
||||
function checkLeapYear(year) {
|
||||
return (
|
||||
year % 400 == 0 ||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Variable, Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import { MaterialIcon } from './materialicon.js';
|
||||
import { setupCursorHover } from './cursorhover.js';
|
||||
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { Box, Button, Label, Revealer } = Widget;
|
||||
|
||||
export const ConfigToggle = ({ icon, name, desc = '', initValue, onChange, ...rest }) => {
|
||||
let value = initValue;
|
||||
@@ -37,22 +37,25 @@ export const ConfigToggle = ({ icon, name, desc = '', initValue, onChange, ...re
|
||||
]
|
||||
});
|
||||
const interactionWrapper = Button({
|
||||
child: widgetContent,
|
||||
onClicked: () => { // mouse up/kb press
|
||||
value = !value;
|
||||
toggleIcon.toggleClassName('switch-fg-toggling-false', false);
|
||||
if (!value) {
|
||||
toggleIcon.label = '';
|
||||
toggleIcon.toggleClassName('txt-poof', true);
|
||||
attribute: {
|
||||
toggle: (newValue) => {
|
||||
value = !value;
|
||||
toggleIcon.toggleClassName('switch-fg-toggling-false', false);
|
||||
if (!value) {
|
||||
toggleIcon.label = '';
|
||||
toggleIcon.toggleClassName('txt-poof', true);
|
||||
}
|
||||
toggleButtonIndicator.toggleClassName('switch-fg-true', value);
|
||||
toggleButton.toggleClassName('switch-bg-true', value);
|
||||
if (value) Utils.timeout(1, () => {
|
||||
toggleIcon.label = 'check';
|
||||
toggleIcon.toggleClassName('txt-poof', false);
|
||||
})
|
||||
onChange(interactionWrapper, value);
|
||||
}
|
||||
toggleButtonIndicator.toggleClassName('switch-fg-true', value);
|
||||
toggleButton.toggleClassName('switch-bg-true', value);
|
||||
if(value) Utils.timeout(1, () => {
|
||||
toggleIcon.label = 'check';
|
||||
toggleIcon.toggleClassName('txt-poof', false);
|
||||
})
|
||||
onChange(interactionWrapper, value);
|
||||
},
|
||||
child: widgetContent,
|
||||
onClicked: (self) => self.attribute.toggle(self),
|
||||
setup: (button) => {
|
||||
setupCursorHover(button),
|
||||
button.connect('pressed', () => { // mouse down
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
|
||||
const CLICK_BRIGHTEN_AMOUNT = 0.13;
|
||||
const { Gdk } = imports.gi;
|
||||
|
||||
export function setupCursorHover(button) {
|
||||
const display = Gdk.Display.get_default();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
export const MaterialIcon = (icon, size, props = {}) => Widget.Label({
|
||||
className: `icon-material txt-${size}`,
|
||||
label: icon,
|
||||
...props,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -57,7 +57,7 @@ const pad = (lines, start = 1, end = 1) => {
|
||||
return lines.map((l) => l.padEnd(len + end, ' ').padStart(len + end + start, ' '))
|
||||
}
|
||||
|
||||
export function convert(text) {
|
||||
export default (text) => {
|
||||
let lines = text.split('\n')
|
||||
|
||||
// Indicates if the current line is within a code block
|
||||
@@ -205,33 +205,6 @@ export function convert(text) {
|
||||
return output.join('\n')
|
||||
}
|
||||
|
||||
const readFile = (f) => {
|
||||
// node.js only and when running from the command line
|
||||
const fs = require('fs')
|
||||
return fs.readFileSync(f, 'utf8')
|
||||
}
|
||||
|
||||
let __is_nodejs_main = false
|
||||
try {
|
||||
// node.js specific checks and exports
|
||||
__is_nodejs_main = (require.main === module)
|
||||
exports.convert = convert
|
||||
} catch (e) { }
|
||||
|
||||
if (__is_nodejs_main) {
|
||||
// running in node.js called from the CLI
|
||||
let args = process.argv.slice(2)
|
||||
if (args.length == 0 || args.find((a) => a == '-h')) {
|
||||
console.log(`Usage: ${process.argv[1]} FILE [FILE...]`)
|
||||
process.exit(0)
|
||||
}
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const f = args[i];
|
||||
process.stdout.write(convert(readFile(f)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const markdownTest = `# Heading 1
|
||||
## Heading 2
|
||||
### Heading 3
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
const GObject = imports.gi.GObject;
|
||||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
import { Utils, Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
// min-height/min-width for height/width
|
||||
// background-color/color for background/indicator color
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// This file is for the actual widget for each single notification
|
||||
|
||||
const { GLib, Gdk, Gtk } = imports.gi;
|
||||
import { Utils, Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js'
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'
|
||||
const { lookUpIcon, timeout } = Utils;
|
||||
const { Box, EventBox, Icon, Overlay, Label, Button, Revealer } = Widget;
|
||||
import { MaterialIcon } from "./materialicon.js";
|
||||
import { setupCursorHover } from "./cursorhover.js";
|
||||
import { AnimatedCircProg } from "./animatedcircularprogress.js";
|
||||
import { MarginRevealer } from './advancedrevealers.js';
|
||||
|
||||
function guessMessageType(summary) {
|
||||
if (summary.includes('recording')) return 'screen_record';
|
||||
@@ -93,13 +93,13 @@ export default ({
|
||||
const widget = EventBox({
|
||||
onHover: (self) => {
|
||||
self.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
||||
if (!wholeThing._hovered)
|
||||
wholeThing._hovered = true;
|
||||
if (!wholeThing.attribute.hovered)
|
||||
wholeThing.attribute.hovered = true;
|
||||
},
|
||||
onHoverLost: (self) => {
|
||||
self.window.set_cursor(null);
|
||||
if (wholeThing._hovered)
|
||||
wholeThing._hovered = false;
|
||||
if (wholeThing.attribute.hovered)
|
||||
wholeThing.attribute.hovered = false;
|
||||
if (isPopup) {
|
||||
command();
|
||||
}
|
||||
@@ -109,13 +109,13 @@ export default ({
|
||||
}
|
||||
});
|
||||
const wholeThing = Revealer({
|
||||
properties: [
|
||||
['id', notifObject.id],
|
||||
['close', undefined],
|
||||
['hovered', false],
|
||||
['dragging', false],
|
||||
['destroyWithAnims', () => destroyWithAnims]
|
||||
],
|
||||
attribute: {
|
||||
'id': notifObject.id,
|
||||
'close': undefined,
|
||||
'hovered': false,
|
||||
'dragging': false,
|
||||
'destroyWithAnims': () => destroyWithAnims,
|
||||
},
|
||||
revealChild: false,
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 200,
|
||||
@@ -159,18 +159,23 @@ export default ({
|
||||
label: notifObject.body,
|
||||
}),
|
||||
Box({
|
||||
homogeneous: true,
|
||||
className: 'notif-actions',
|
||||
className: 'notif-actions spacing-h-5',
|
||||
children: [
|
||||
Button({
|
||||
hexpand: true,
|
||||
className: `notif-action notif-action-${notifObject.urgency}`,
|
||||
label: 'Close',
|
||||
onClicked: () => destroyWithAnims(),
|
||||
child: Label({
|
||||
label: 'Close',
|
||||
})
|
||||
}),
|
||||
...notifObject.actions.map(action => Widget.Button({
|
||||
hexpand: true,
|
||||
className: `notif-action notif-action-${notifObject.urgency}`,
|
||||
onClicked: () => notifObject.invoke(action.id),
|
||||
label: action.label,
|
||||
child: Label({
|
||||
label: action.label,
|
||||
})
|
||||
}))
|
||||
],
|
||||
})
|
||||
@@ -258,8 +263,13 @@ export default ({
|
||||
className: `${isPopup ? 'popup-' : ''}notif-${notifObject.urgency} spacing-h-10`,
|
||||
children: [
|
||||
notifIcon,
|
||||
notifText,
|
||||
notifExpandButton,
|
||||
Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
notifText,
|
||||
notifExpandButton,
|
||||
]
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
@@ -334,7 +344,7 @@ export default ({
|
||||
}
|
||||
}
|
||||
|
||||
wholeThing._dragging = Math.abs(offset_x) > 10;
|
||||
wholeThing.attribute.dragging = Math.abs(offset_x) > 10;
|
||||
|
||||
if (widget.window)
|
||||
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grabbing'));
|
||||
@@ -354,9 +364,9 @@ export default ({
|
||||
|
||||
}, 'drag-update')
|
||||
.hook(gesture, self => {
|
||||
if (!self._ready) {
|
||||
if (!self.attribute.ready) {
|
||||
wholeThing.revealChild = true;
|
||||
self._ready = true;
|
||||
self.attribute.ready = true;
|
||||
return;
|
||||
}
|
||||
const offset_h = gesture.get_offset()[1];
|
||||
@@ -387,7 +397,7 @@ export default ({
|
||||
if (widget.window)
|
||||
widget.window.set_cursor(Gdk.Cursor.new_from_name(display, 'grab'));
|
||||
|
||||
wholeThing._dragging = false;
|
||||
wholeThing.attribute.dragging = false;
|
||||
}
|
||||
initDirX = 0;
|
||||
initDirVertical = -1;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { App, Widget } from '../imports.js';
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Box, Window } = Widget;
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Gtk } = imports.gi;
|
||||
const Lang = imports.lang;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { App, Service, Utils, Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
export const separatorLine = Widget.Box({
|
||||
className: 'separator-line',
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { App, Service, Utils, Widget } from '../imports.js';
|
||||
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 { MaterialIcon } from './materialicon.js';
|
||||
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
||||
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
||||
@@ -44,23 +47,21 @@ export const NotificationIndicator = (notifCenterName = 'sideright') => {
|
||||
MaterialIcon('notifications', 'norm'),
|
||||
Widget.Label({
|
||||
className: 'txt-small titlefont',
|
||||
properties: [
|
||||
['increment', (self) => self._unreadCount++],
|
||||
['markread', (self) => self._unreadCount = 0],
|
||||
['update', (self) => self.label = `${self._unreadCount}`],
|
||||
['unreadCount', 0],
|
||||
],
|
||||
attribute: {
|
||||
unreadCount: 0,
|
||||
update: (self) => self.label = `${self.attribute.unreadCount}`,
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(Notifications, (self, id) => {
|
||||
if (!id || Notifications.dnd) return;
|
||||
if (!Notifications.getNotification(id)) return;
|
||||
self._increment(self);
|
||||
self._update(self);
|
||||
self.attribute.unreadCount++;
|
||||
self.attribute.update(self);
|
||||
}, 'notified')
|
||||
.hook(App, (self, currentName, visible) => {
|
||||
if (visible && currentName === notifCenterName) {
|
||||
self._markread(self);
|
||||
self._update(self);
|
||||
self.attribute.unreadCount = 0;
|
||||
self.attribute.update(self);
|
||||
}
|
||||
})
|
||||
,
|
||||
@@ -74,8 +75,8 @@ export const NotificationIndicator = (notifCenterName = 'sideright') => {
|
||||
export const BluetoothIndicator = () => Widget.Stack({
|
||||
transition: 'slide_up_down',
|
||||
items: [
|
||||
['true', Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth' })],
|
||||
['false', Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth_disabled' })],
|
||||
['true', Widget.Label({ className: 'txt-norm icon-material', label: 'bluetooth' })],
|
||||
],
|
||||
setup: (self) => self
|
||||
.hook(Bluetooth, stack => {
|
||||
@@ -99,7 +100,7 @@ const NetworkWiredIndicator = () => Widget.Stack({
|
||||
return;
|
||||
|
||||
const { internet } = Network.wired;
|
||||
if (internet === 'connected' || internet === 'connecting')
|
||||
if (['connecting', 'connected'].includes(internet))
|
||||
stack.shown = internet;
|
||||
else if (Network.connectivity !== 'full')
|
||||
stack.shown = 'disconnected';
|
||||
@@ -135,7 +136,7 @@ const NetworkWifiIndicator = () => Widget.Stack({
|
||||
if (Network.wifi.internet == 'connected') {
|
||||
stack.shown = String(Math.ceil(Network.wifi.strength / 25));
|
||||
}
|
||||
else if (Network.wifi.internet == 'disconnected' || Network.wifi.internet == 'connecting') {
|
||||
else if (["disconnected", "connecting"].includes(Network.wifi.internet)) {
|
||||
stack.shown = Network.wifi.internet;
|
||||
}
|
||||
}),
|
||||
@@ -154,14 +155,14 @@ export const NetworkIndicator = () => Widget.Stack({
|
||||
return;
|
||||
}
|
||||
const primary = Network.primary || 'fallback';
|
||||
if (primary == 'wifi' || primary == 'wired')
|
||||
if (['wifi', 'wired'].includes(primary))
|
||||
stack.shown = primary;
|
||||
else
|
||||
stack.shown = 'fallback';
|
||||
}),
|
||||
});
|
||||
|
||||
const KeyboardLayout = ({ useFlag } = {}) => {
|
||||
const HyprlandXkbKeyboardLayout = async ({ useFlag } = {}) => {
|
||||
var initLangs = [];
|
||||
var languageStackArray = [];
|
||||
var currentKeyboard;
|
||||
@@ -215,15 +216,24 @@ const KeyboardLayout = ({ useFlag } = {}) => {
|
||||
return widgetRevealer;
|
||||
}
|
||||
|
||||
const OptionalKeyboardLayout = async () => {
|
||||
try {
|
||||
return await HyprlandXkbKeyboardLayout({ useFlag: false });
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const optionalKeyboardLayoutInstance = await OptionalKeyboardLayout();
|
||||
|
||||
export const StatusIcons = (props = {}) => Widget.Box({
|
||||
...props,
|
||||
child: Widget.Box({
|
||||
className: 'spacing-h-15',
|
||||
children: [
|
||||
KeyboardLayout({ useFlag: false }),
|
||||
optionalKeyboardLayoutInstance,
|
||||
NotificationIndicator(),
|
||||
BluetoothIndicator(),
|
||||
NetworkIndicator(),
|
||||
BluetoothIndicator(),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
Executable → Regular
@@ -147,7 +147,6 @@ apply_ags() {
|
||||
ags run-js "App.resetCss(); App.applyCss('${HOME}/.config/ags/style.css');"
|
||||
}
|
||||
|
||||
# apply_svgs
|
||||
apply_ags &
|
||||
apply_hyprland &
|
||||
apply_gtk &
|
||||
|
||||
Executable → Regular
Executable → Regular
@@ -1,11 +1,6 @@
|
||||
#!/usr/bin/bash
|
||||
# Switches sww wallpaper
|
||||
# Requires: coreutils, xrandr, hyprland
|
||||
|
||||
color=$(hyprpicker --no-fancy)
|
||||
|
||||
# Generate colors for ags n stuff
|
||||
"$HOME"/.config/ags/scripts/color_generation/colorgen.sh "${color}" --apply
|
||||
sassc "$HOME"/.config/ags/scss/main.scss "$HOME"/.config/ags/style.css
|
||||
ags run-js "App.resetCss(); App.applyCss('${HOME}/.config/ags/style.css');"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/bash
|
||||
# Switches sww wallpaper
|
||||
# Switches swww wallpaper
|
||||
# Requires: coreutils, xrandr, hyprland
|
||||
|
||||
if [ "$1" == "--noswitch" ]; then
|
||||
|
||||
@@ -17,6 +17,6 @@ if [[ "$(pidof wf-recorder)" == "" ]]; then
|
||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$(slurp)" & disown
|
||||
fi
|
||||
else
|
||||
/usr/bin/kill --signal SIGINT wf-recorder
|
||||
kill --signal SIGINT wf-recorder
|
||||
notify-send "Recording Stopped" "Stopped" -a 'record-script.sh'
|
||||
fi
|
||||
|
||||
Executable → Regular
Executable → Regular
@@ -87,9 +87,9 @@
|
||||
"BLACK_500": "#393634",
|
||||
"BLACK_700": "#33302F",
|
||||
"BLACK_900": "#2B2928",
|
||||
"accent_bg_color": "#ffabf1",
|
||||
"accent_fg_color": "#551251",
|
||||
"accent_color": "#ffabf1",
|
||||
"accent_bg_color": "#51d7ef",
|
||||
"accent_fg_color": "#00363f",
|
||||
"accent_color": "#51d7ef",
|
||||
"destructive_bg_color": "#ffb4a9",
|
||||
"destructive_fg_color": "#680003",
|
||||
"destructive_color": "#ffb4a9",
|
||||
@@ -99,26 +99,31 @@
|
||||
"warning_fg_color": "rgba(0, 0, 0, 0.87)",
|
||||
"error_bg_color": "#ffb4a9",
|
||||
"error_fg_color": "#680003",
|
||||
"window_bg_color": "#120F11",
|
||||
"window_fg_color": "#eae0e4",
|
||||
"view_bg_color": "#1f1a1d",
|
||||
"view_fg_color": "#eae0e4",
|
||||
"window_bg_color": "#0F1011",
|
||||
"window_fg_color": "#e1e3e4",
|
||||
"view_bg_color": "#191c1d",
|
||||
"view_fg_color": "#e1e3e4",
|
||||
"headerbar_bg_color": "mix(@dialog_bg_color, @window_bg_color, 0.5)",
|
||||
"headerbar_fg_color": "#f8daee",
|
||||
"headerbar_border_color": "#554050",
|
||||
"headerbar_fg_color": "#cde7ed",
|
||||
"headerbar_border_color": "#334a4f",
|
||||
"headerbar_backdrop_color": "@headerbar_bg_color",
|
||||
"headerbar_shade_color": "rgba(0, 0, 0, 0.09)",
|
||||
"card_bg_color": "#120F11",
|
||||
"card_fg_color": "#f8daee",
|
||||
"card_bg_color": "#0F1011",
|
||||
"card_fg_color": "#cde7ed",
|
||||
"card_shade_color": "rgba(0, 0, 0, 0.09)",
|
||||
"dialog_bg_color": "#554050",
|
||||
"dialog_fg_color": "#f8daee",
|
||||
"popover_bg_color": "#554050",
|
||||
"popover_fg_color": "#f8daee",
|
||||
"dialog_bg_color": "#334a4f",
|
||||
"dialog_fg_color": "#cde7ed",
|
||||
"popover_bg_color": "#334a4f",
|
||||
"popover_fg_color": "#cde7ed",
|
||||
"thumbnail_bg_color": "#1a1b26",
|
||||
"thumbnail_fg_color": "#AEE5FA",
|
||||
"shade_color": "rgba(0, 0, 0, 0.36)",
|
||||
"scrollbar_outline_color": "rgba(0, 0, 0, 0.5)"
|
||||
"scrollbar_outline_color": "rgba(0, 0, 0, 0.5)",
|
||||
|
||||
"sidebar_bg_color": "@window_bg_color",
|
||||
"sidebar_fg_color":"@window_fg_color",
|
||||
"sidebar_border_color": "@sidebar_bg_color",
|
||||
"sidebar_backdrop_color": "@sidebar_bg_color"
|
||||
},
|
||||
"palette": {
|
||||
"blue_": {},
|
||||
@@ -132,8 +137,8 @@
|
||||
"dark_": {}
|
||||
},
|
||||
"custom_css": {
|
||||
"gtk4": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; ",
|
||||
"gtk3": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; "
|
||||
"gtk4": "",
|
||||
"gtk3": ""
|
||||
},
|
||||
"plugins": {}
|
||||
}
|
||||
@@ -118,7 +118,12 @@
|
||||
"thumbnail_bg_color": "#1a1b26",
|
||||
"thumbnail_fg_color": "#AEE5FA",
|
||||
"shade_color": "rgba(0, 0, 0, 0.36)",
|
||||
"scrollbar_outline_color": "rgba(0, 0, 0, 0.5)"
|
||||
"scrollbar_outline_color": "rgba(0, 0, 0, 0.5)",
|
||||
|
||||
"sidebar_bg_color": "@window_bg_color",
|
||||
"sidebar_fg_color":"@window_fg_color",
|
||||
"sidebar_border_color": "@sidebar_bg_color",
|
||||
"sidebar_backdrop_color": "@sidebar_bg_color"
|
||||
},
|
||||
"palette": {
|
||||
"blue_": {},
|
||||
@@ -132,8 +137,8 @@
|
||||
"dark_": {}
|
||||
},
|
||||
"custom_css": {
|
||||
"gtk4": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; ",
|
||||
"gtk3": "@define-color sidebar_bg_color @window_bg_color; @define-color sidebar_fg_color @window_fg_color; @define-color sidebar_border_color @window_bg_color; @define-color sidebar_backdrop_color @window_bg_color; "
|
||||
"gtk4": "",
|
||||
"gtk3": ""
|
||||
},
|
||||
"plugins": {}
|
||||
}
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
+4
-4
@@ -1,4 +1,4 @@
|
||||
$SLURP_COMMAND="$(slurp -d -c {{ $onSecondaryContainer }}BB -b {{ $secondaryContainer }}22 -s 00000000)"
|
||||
$SLURP_COMMAND="$(slurp -d -c {{ $onSecondaryContainer }}BB -b {{ $secondaryContainer }}44 -s 00000000)"
|
||||
|
||||
general {
|
||||
col.active_border = rgba({{ $onPrimary }}FF)
|
||||
@@ -6,13 +6,13 @@ general {
|
||||
}
|
||||
|
||||
plugin {
|
||||
droidbars {
|
||||
droidbars { # This is my hyprbars mod that broke :(
|
||||
# example config
|
||||
bar_height = 30
|
||||
background_color = rgba({{ $background }}FF)
|
||||
# background_color_active = rgba({{ $surfaceVariant }}FF) # Not added yet
|
||||
text_color = rgba({{ $onSecondaryContainer }}FF)
|
||||
font_family = Lexend
|
||||
font_family = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
|
||||
button_font_fmily = JetBrainsMono NF
|
||||
|
||||
# example buttons (R -> L)
|
||||
@@ -24,7 +24,7 @@ plugin {
|
||||
}
|
||||
hyprbars {
|
||||
# Honestly idk if it works like css, but well, why not
|
||||
bar_text_font = Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
|
||||
bar_text_font = Rubik, Geist, AR One Sans, Reddit Sans, Inter, Roboto, Ubuntu, Noto Sans, sans-serif
|
||||
bar_height = 30
|
||||
bar_padding = 10
|
||||
bar_button_padding = 5
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
$black: black;
|
||||
$white: white;
|
||||
$bar_ws_width: 1.774rem;
|
||||
$bar_subgroup_bg: mix($surfaceVariant, $primary, 89%);
|
||||
@if $darkmode ==true {
|
||||
$bar_subgroup_bg: $surfaceVariant;
|
||||
}
|
||||
$bar_subgroup_bg: $surfaceVariant;
|
||||
|
||||
@mixin bar-group-rounding {
|
||||
@include small-rounding;
|
||||
@@ -45,8 +42,7 @@ $bar_subgroup_bg: mix($surfaceVariant, $primary, 89%);
|
||||
}
|
||||
|
||||
.bar-group-pad-system {
|
||||
padding-left: 1.023rem;
|
||||
padding-right: 0.341rem;
|
||||
padding: 0rem 0.341rem;
|
||||
}
|
||||
|
||||
.bar-group-pad-music {
|
||||
@@ -131,6 +127,10 @@ $bar_subgroup_bg: mix($surfaceVariant, $primary, 89%);
|
||||
margin: 0rem 0.341rem;
|
||||
}
|
||||
|
||||
.bar-clock-box {
|
||||
margin: 0rem 0.682rem;
|
||||
}
|
||||
|
||||
.bar-clock {
|
||||
@include titlefont;
|
||||
font-size: 1.2727rem;
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
|
||||
.cheatsheet-closebtn:hover,
|
||||
.cheatsheet-closebtn:focus {
|
||||
background-color: $surfaceVariant;
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.cheatsheet-closebtn:active {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 70%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.cheatsheet-category-title {
|
||||
|
||||
Executable → Regular
@@ -47,14 +47,14 @@ menu {
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
menubar>menuitem {
|
||||
menubar > menuitem {
|
||||
border-radius: 0.545rem;
|
||||
-gtk-outline-radius: 0.545rem;
|
||||
min-width: 13.636rem;
|
||||
min-height: 2.727rem;
|
||||
}
|
||||
|
||||
menu>menuitem {
|
||||
menu > menuitem {
|
||||
padding: 0.4em 1.5rem;
|
||||
background: transparent;
|
||||
transition: 0.2s ease background;
|
||||
@@ -62,15 +62,31 @@ menu>menuitem {
|
||||
-gtk-outline-radius: 0.545rem;
|
||||
}
|
||||
|
||||
menu>menuitem:hover,
|
||||
menu>menuitem:focus {
|
||||
menu > menuitem:hover,
|
||||
menu > menuitem:focus {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 90%);
|
||||
}
|
||||
menu > menuitem:active {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 80%);
|
||||
}
|
||||
|
||||
.separator-line {
|
||||
background-color: $surfaceVariant;
|
||||
min-width: 0.068rem;
|
||||
min-height: 0.068rem;
|
||||
radio {
|
||||
@include full-rounding;
|
||||
margin: 0.273rem;
|
||||
min-width: 15px;
|
||||
min-height: 15px;
|
||||
border: 0.068rem solid $outline;
|
||||
}
|
||||
|
||||
// radio:first-child {
|
||||
// background-color: red;
|
||||
// }
|
||||
|
||||
radio:checked {
|
||||
min-width: 8px;
|
||||
min-height: 8px;
|
||||
background-color: $onPrimary;
|
||||
border: 0.477rem solid $primary;
|
||||
}
|
||||
|
||||
tooltip {
|
||||
@@ -132,17 +148,17 @@ tooltip {
|
||||
border: 0.068rem solid $outline;
|
||||
}
|
||||
|
||||
.segment-container>*:first-child {
|
||||
.segment-container > *:first-child {
|
||||
border-top-left-radius: 9999px;
|
||||
border-bottom-left-radius: 9999px;
|
||||
}
|
||||
|
||||
.segment-container>* {
|
||||
.segment-container > * {
|
||||
border-right: 0.068rem solid $outline;
|
||||
padding: 0.341rem 0.682rem;
|
||||
}
|
||||
|
||||
.segment-container>*:last-child {
|
||||
.segment-container > *:last-child {
|
||||
border-right: 0rem solid transparent;
|
||||
border-top-right-radius: 9999px;
|
||||
border-bottom-right-radius: 9999px;
|
||||
@@ -154,18 +170,17 @@ tooltip {
|
||||
|
||||
.segment-btn:focus,
|
||||
.segment-btn:hover {
|
||||
background-color: $surfaceVariant;
|
||||
color: $onSurfaceVariant;
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.segment-btn-enabled {
|
||||
background-color: $secondaryContainer;
|
||||
background-color: $secondaryContainer;
|
||||
color: $onSecondaryContainer;
|
||||
}
|
||||
|
||||
.segment-btn-enabled:hover,
|
||||
.segment-btn-enabled:focus {
|
||||
background-color: $secondaryContainer;
|
||||
background-color: $secondaryContainer;
|
||||
color: $onSecondaryContainer;
|
||||
}
|
||||
|
||||
@@ -191,4 +206,4 @@ tooltip {
|
||||
|
||||
.gap-h-15 {
|
||||
min-width: 1.023rem;
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
@@ -21,12 +21,11 @@
|
||||
}
|
||||
|
||||
.test {
|
||||
background-image: linear-gradient(45deg,
|
||||
#F4D609 0%, #F4D609 10%, #212121 10%, #212121 20%,
|
||||
#F4D609 20%, #F4D609 30%, #212121 30%, #212121 40%,
|
||||
#F4D609 40%, #F4D609 50%, #212121 50%, #212121 60%,
|
||||
#F4D609 60%, #F4D609 70%, #212121 70%, #212121 80%,
|
||||
#F4D609 80%, #F4D609 90%, #212121 90%, #212121 100%);
|
||||
background-image: linear-gradient(
|
||||
45deg, #f4d609 0%, #f4d609 10%, #212121 10%, #212121 20%, #f4d609 20%, #f4d609 30%, #212121 30%,
|
||||
#212121 40%, #f4d609 40%, #f4d609 50%, #212121 50%, #212121 60%, #f4d609 60%,
|
||||
#f4d609 70%, #212121 70%, #212121 80%, #f4d609 80%, #f4d609 90%, #212121 90%, #212121 100%
|
||||
);
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
@@ -178,191 +177,204 @@
|
||||
@include icon-nerd;
|
||||
}
|
||||
|
||||
.separator-circle {
|
||||
@include full-rounding;
|
||||
background-color: $onSurface;
|
||||
margin: 0rem 0.682rem;
|
||||
min-width: 0.545rem;
|
||||
min-height: 0.545rem;
|
||||
.separator-line {
|
||||
background-color: $outline;
|
||||
min-width: 0.068rem;
|
||||
min-height: 0.068rem;
|
||||
}
|
||||
|
||||
.spacing-h-3>* {
|
||||
.separator-circle {
|
||||
@include full-rounding;
|
||||
background-color: $outline;
|
||||
margin: 0rem 0.682rem;
|
||||
min-width: 0.273rem;
|
||||
min-height: 0.273rem;
|
||||
}
|
||||
|
||||
.spacing-h-3 > * {
|
||||
margin-right: 0.205rem;
|
||||
}
|
||||
|
||||
.spacing-h-3>*:last-child {
|
||||
.spacing-h-3 > *:last-child {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-15>* {
|
||||
.spacing-v-3 > * {
|
||||
margin-bottom: 0.205rem;
|
||||
}
|
||||
|
||||
.spacing-v-3 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-15 > * {
|
||||
margin-bottom: 1.023rem;
|
||||
}
|
||||
|
||||
.spacing-v-15>*:last-child {
|
||||
.spacing-v-15 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-15>* {
|
||||
.spacing-h-15 > * {
|
||||
margin-right: 1.023rem;
|
||||
}
|
||||
|
||||
.spacing-h-15>*:last-child {
|
||||
.spacing-h-15 > *:last-child {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-15>revealer>* {
|
||||
.spacing-h-15 > revealer > * {
|
||||
margin-right: 1.023rem;
|
||||
}
|
||||
|
||||
.spacing-h-15>revealer:last-child>* {
|
||||
.spacing-h-15 > revealer:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-15>scrolledwindow>* {
|
||||
.spacing-h-15 > scrolledwindow > * {
|
||||
margin-right: 1.023rem;
|
||||
}
|
||||
|
||||
.spacing-h-15>scrolledwindow:last-child>* {
|
||||
.spacing-h-15 > scrolledwindow:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-5>box {
|
||||
.spacing-v-5 > box {
|
||||
margin-bottom: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-v-5>box:last-child {
|
||||
.spacing-v-5 > box:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-5>* {
|
||||
.spacing-v-5 > * {
|
||||
margin-bottom: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-v-5>*:last-child {
|
||||
.spacing-v-5 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-5-revealer>revealer>* {
|
||||
.spacing-v-5-revealer > revealer > * {
|
||||
margin-bottom: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-v-5-revealer>revealer:last-child>* {
|
||||
.spacing-v-5-revealer > revealer:last-child > * {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-5-revealer>scrolledwindow>* {
|
||||
.spacing-v-5-revealer > scrolledwindow > * {
|
||||
margin-bottom: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-v-5-revealer>scrolledwindow:last-child>* {
|
||||
.spacing-v-5-revealer > scrolledwindow:last-child > * {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-4>* {
|
||||
.spacing-h-4 > * {
|
||||
margin-right: 0.273rem;
|
||||
}
|
||||
|
||||
.spacing-h-4>*:last-child {
|
||||
.spacing-h-4 > *:last-child {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>* {
|
||||
.spacing-h-5 > * {
|
||||
margin-right: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>*:last-child {
|
||||
.spacing-h-5 > *:last-child {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>widget>* {
|
||||
.spacing-h-5 > widget > * {
|
||||
margin-right: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>widget:last-child>* {
|
||||
.spacing-h-5 > widget:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>revealer>* {
|
||||
.spacing-h-5 > revealer > * {
|
||||
margin-right: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>revealer:last-child>* {
|
||||
.spacing-h-5 > revealer:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>scrolledwindow>* {
|
||||
.spacing-h-5 > scrolledwindow > * {
|
||||
margin-right: 0.341rem;
|
||||
}
|
||||
|
||||
.spacing-h-5>scrolledwindow:last-child>* {
|
||||
.spacing-h-5 > scrolledwindow:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-minus5>* {
|
||||
.spacing-v-minus5 > * {
|
||||
margin-bottom: -0.341rem;
|
||||
}
|
||||
|
||||
.spacing-v-minus5>*:last-child {
|
||||
.spacing-v-minus5 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>* {
|
||||
.spacing-h-10 > * {
|
||||
margin-right: 0.682rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>*:last-child {
|
||||
.spacing-h-10 > *:last-child {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>revealer>* {
|
||||
.spacing-h-10 > revealer > * {
|
||||
margin-right: 0.682rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>revealer:last-child>* {
|
||||
.spacing-h-10 > revealer:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>scrolledwindow>* {
|
||||
.spacing-h-10 > scrolledwindow > * {
|
||||
margin-right: 0.682rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>scrolledwindow:last-child>* {
|
||||
.spacing-h-10 > scrolledwindow:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>flowboxchild>* {
|
||||
.spacing-h-10 > flowboxchild > * {
|
||||
margin-right: 0.682rem;
|
||||
}
|
||||
|
||||
.spacing-h-10>flowboxchild:last-child>* {
|
||||
.spacing-h-10 > flowboxchild:last-child > * {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-10>* {
|
||||
.spacing-v-10 > * {
|
||||
margin-bottom: 0.682rem;
|
||||
}
|
||||
|
||||
.spacing-v-10>*:last-child {
|
||||
.spacing-v-10 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h-20>* {
|
||||
.spacing-h-20 > * {
|
||||
margin-right: 1.364rem;
|
||||
}
|
||||
|
||||
.spacing-h-20>*:last-child {
|
||||
.spacing-h-20 > *:last-child {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v-20>* {
|
||||
.spacing-v-20 > * {
|
||||
margin-bottom: 1.364rem;
|
||||
}
|
||||
|
||||
.spacing-v-20>*:last-child {
|
||||
.spacing-v-20 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
|
||||
.anim-enter {
|
||||
@include anim-enter;
|
||||
}
|
||||
@@ -429,51 +441,51 @@
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.spacing-h--5>box {
|
||||
.spacing-h--5 > box {
|
||||
margin-right: -0.341rem;
|
||||
}
|
||||
|
||||
.spacing-h--5>box:last-child {
|
||||
.spacing-h--5 > box:last-child {
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v--5>* {
|
||||
.spacing-v--5 > * {
|
||||
margin-bottom: -0.341rem;
|
||||
}
|
||||
|
||||
.spacing-v--5>*:last-child {
|
||||
.spacing-v--5 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h--10>* {
|
||||
.spacing-h--10 > * {
|
||||
margin-left: -1.364rem;
|
||||
}
|
||||
|
||||
.spacing-h--10>*:first-child {
|
||||
.spacing-h--10 > *:first-child {
|
||||
margin-left: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v--10>* {
|
||||
.spacing-v--10 > * {
|
||||
margin-bottom: -0.682rem;
|
||||
}
|
||||
|
||||
.spacing-v--10>*:last-child {
|
||||
.spacing-v--10 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-v--10>* {
|
||||
.spacing-v--10 > * {
|
||||
margin-bottom: -0.682rem;
|
||||
}
|
||||
|
||||
.spacing-v--10>*:last-child {
|
||||
.spacing-v--10 > *:last-child {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.spacing-h--20>* {
|
||||
.spacing-h--20 > * {
|
||||
margin-left: -1.364rem;
|
||||
}
|
||||
|
||||
.spacing-h--20>*:first-child {
|
||||
.spacing-h--20 > *:first-child {
|
||||
margin-left: 0rem;
|
||||
}
|
||||
|
||||
@@ -484,11 +496,24 @@
|
||||
.menu-decel {
|
||||
@include menu_decel;
|
||||
}
|
||||
|
||||
.element-show {
|
||||
@include element_easeInOut;
|
||||
}
|
||||
|
||||
.element-hide {
|
||||
@include element_easeInOut;
|
||||
}
|
||||
}
|
||||
.element-move {
|
||||
@include element_easeInOut;
|
||||
}
|
||||
.element-decel {
|
||||
@include element_decel;
|
||||
}
|
||||
.element-bounceout {
|
||||
@include element_bounceOut;
|
||||
}
|
||||
.element-accel {
|
||||
@include element_accel;
|
||||
}
|
||||
.page-move {
|
||||
@include page_move;
|
||||
}
|
||||
|
||||
@@ -30,56 +30,38 @@ $rounding_large: 1.705rem;
|
||||
|
||||
@mixin titlefont {
|
||||
// Geometric sans-serif
|
||||
font-family:
|
||||
'Gabarito',
|
||||
'Poppins',
|
||||
'Lexend',
|
||||
sans-serif;
|
||||
font-family: "Gabarito", "Poppins", "Lexend", sans-serif;
|
||||
}
|
||||
|
||||
@mixin mainfont {
|
||||
// Other clean sans-serif
|
||||
font-family:
|
||||
'Rubik',
|
||||
'Geist',
|
||||
'AR One Sans',
|
||||
'Reddit Sans',
|
||||
'Inter',
|
||||
'Roboto',
|
||||
'Ubuntu',
|
||||
'Noto Sans',
|
||||
sans-serif;
|
||||
font-family: "Rubik", "Geist", "AR One Sans", "Reddit Sans", "Inter",
|
||||
"Roboto", "Ubuntu", "Noto Sans", sans-serif;
|
||||
// font-weight: 500;
|
||||
}
|
||||
|
||||
@mixin icon-material {
|
||||
// Material Design Icons
|
||||
font-family:
|
||||
'Material Symbols Rounded',
|
||||
'Material Symbols Outlined',
|
||||
'Material Symbols Sharp';
|
||||
font-family: "Material Symbols Rounded", "Material Symbols Outlined",
|
||||
"Material Symbols Sharp";
|
||||
}
|
||||
|
||||
@mixin icon-nerd {
|
||||
// Nerd Fonts
|
||||
font-family:
|
||||
'SpaceMono NF', 'SpaceMono Nerd Font',
|
||||
'JetBrains Mono NF', 'JetBrains Mono Nerd Font',
|
||||
monospace;
|
||||
font-family: "SpaceMono NF", "SpaceMono Nerd Font", "JetBrains Mono NF",
|
||||
"JetBrains Mono Nerd Font", monospace;
|
||||
}
|
||||
|
||||
@mixin techfont {
|
||||
// Monospace for sys info n stuff. Doesn't have to be a nerd font, but it's cool.
|
||||
font-family: 'JetBrains Mono NF', 'JetBrains Mono Nerd Font', 'JetBrains Mono NL', 'SpaceMono NF', 'SpaceMono Nerd Font', monospace;
|
||||
font-family: "JetBrains Mono NF", "JetBrains Mono Nerd Font",
|
||||
"JetBrains Mono NL", "SpaceMono NF", "SpaceMono Nerd Font", monospace;
|
||||
}
|
||||
|
||||
@mixin readingfont {
|
||||
// The most readable fonts, for a comfortable reading experience
|
||||
// in stuff like ChatGPT widget
|
||||
font-family:
|
||||
'Lexend',
|
||||
'Noto Sans',
|
||||
sans-serif;
|
||||
font-family: "Lexend", "Noto Sans", sans-serif;
|
||||
// font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -91,7 +73,6 @@ $rounding_large: 1.705rem;
|
||||
color: $actiontext;
|
||||
}
|
||||
|
||||
|
||||
$elevation_margin: 0.476rem;
|
||||
|
||||
@mixin elevation-safe {
|
||||
@@ -164,14 +145,18 @@ $elevation_margin: 0.476rem;
|
||||
@mixin element_decel {
|
||||
transition: 300ms cubic-bezier(0, 0.55, 0.45, 1);
|
||||
}
|
||||
|
||||
@mixin element_bounceOut {
|
||||
transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
@mixin element_accel {
|
||||
transition: 300ms cubic-bezier(0.55, 0, 1, 0.45);
|
||||
}
|
||||
|
||||
@mixin element_easeInOut {
|
||||
transition: 300ms cubic-bezier(0.85, 0, 0.15, 1);
|
||||
}
|
||||
@mixin page_move {
|
||||
transition: 500ms cubic-bezier(0.85, 0, 0.15, 1);
|
||||
}
|
||||
|
||||
@function tint($color, $percentage) {
|
||||
@return mix(rgb(245, 250, 255), $color, $percentage);
|
||||
@@ -215,4 +200,4 @@ $overlay2: mix($onSurface, rgba(0, 0, 0, 0), 40%);
|
||||
}
|
||||
|
||||
$white: white;
|
||||
$black: black;
|
||||
$black: black;
|
||||
|
||||
Executable → Regular
+23
-23
@@ -1,29 +1,29 @@
|
||||
$darkmode: true;
|
||||
$primary: #ffabf1;
|
||||
$onPrimary: #551251;
|
||||
$primaryContainer: #702c69;
|
||||
$onPrimaryContainer: #ffd6f5;
|
||||
$secondary: #dbbed2;
|
||||
$onSecondary: #3d2b39;
|
||||
$secondaryContainer: #554050;
|
||||
$onSecondaryContainer: #f8daee;
|
||||
$tertiary: #f5b9a6;
|
||||
$onTertiary: #4c2619;
|
||||
$tertiaryContainer: #663c2e;
|
||||
$onTertiaryContainer: #ffdbcf;
|
||||
$primary: #51d7ef;
|
||||
$onPrimary: #00363f;
|
||||
$primaryContainer: #004e5a;
|
||||
$onPrimaryContainer: #9cefff;
|
||||
$secondary: #b1cbd1;
|
||||
$onSecondary: #1c3439;
|
||||
$secondaryContainer: #334a4f;
|
||||
$onSecondaryContainer: #cde7ed;
|
||||
$tertiary: #bcc5ea;
|
||||
$onTertiary: #262f4d;
|
||||
$tertiaryContainer: #3d4665;
|
||||
$onTertiaryContainer: #dae1ff;
|
||||
$error: #ffb4a9;
|
||||
$onError: #680003;
|
||||
$errorContainer: #930006;
|
||||
$onErrorContainer: #ffb4a9;
|
||||
$colorbarbg: #120F11;
|
||||
$background: #120F11;
|
||||
$onBackground: #eae0e4;
|
||||
$surface: #1f1a1d;
|
||||
$onSurface: #eae0e4;
|
||||
$surfaceVariant: #4e444b;
|
||||
$onSurfaceVariant: #d1c2cb;
|
||||
$outline: #9a8d95;
|
||||
$colorbarbg: #0F1011;
|
||||
$background: #0F1011;
|
||||
$onBackground: #e1e3e4;
|
||||
$surface: #191c1d;
|
||||
$onSurface: #e1e3e4;
|
||||
$surfaceVariant: #3f484a;
|
||||
$onSurfaceVariant: #bfc8ca;
|
||||
$outline: #899294;
|
||||
$shadow: #000000;
|
||||
$inverseSurface: #eae0e4;
|
||||
$inverseOnSurface: #342f32;
|
||||
$inversePrimary: #8c4483;
|
||||
$inverseSurface: #e1e3e4;
|
||||
$inverseOnSurface: #2d3132;
|
||||
$inversePrimary: #006877;
|
||||
|
||||
Executable → Regular
+1
-29
@@ -1,29 +1 @@
|
||||
$darkmode: true;
|
||||
$primary: #ecb1ff;
|
||||
$onPrimary: #4f076e;
|
||||
$primaryContainer: #682886;
|
||||
$onPrimaryContainer: #f9d8ff;
|
||||
$secondary: #d4c0d8;
|
||||
$onSecondary: #392c3d;
|
||||
$secondaryContainer: #504254;
|
||||
$onSecondaryContainer: #f1dcf4;
|
||||
$tertiary: #f5b7b5;
|
||||
$onTertiary: #4c2525;
|
||||
$tertiaryContainer: #663b3a;
|
||||
$onTertiaryContainer: #ffdad8;
|
||||
$error: #ffb4a9;
|
||||
$onError: #680003;
|
||||
$errorContainer: #930006;
|
||||
$onErrorContainer: #ffb4a9;
|
||||
$colorbarbg: #120F12;
|
||||
$background: #120F12;
|
||||
$onBackground: #e8e0e5;
|
||||
$surface: #1e1a1e;
|
||||
$onSurface: #e8e0e5;
|
||||
$surfaceVariant: #4c444d;
|
||||
$onSurfaceVariant: #cec3cd;
|
||||
$outline: #978e97;
|
||||
$shadow: #000000;
|
||||
$inverseSurface: #e8e0e5;
|
||||
$inverseOnSurface: #332f33;
|
||||
$inversePrimary: #8342a1;
|
||||
|
||||
|
||||
@@ -1,26 +1 @@
|
||||
// SCSS Variables
|
||||
// Generated by 'wal'
|
||||
$wallpaper: "/home/end/.cache/ags/media/c77cd3721bb54437609b42bd5254d1a16437f855";
|
||||
|
||||
// Special
|
||||
$background: #0C0820;
|
||||
$foreground: #e5c7e7;
|
||||
$cursor: #e5c7e7;
|
||||
|
||||
// Colors
|
||||
$color0: #0C0820;
|
||||
$color1: #3F26DE;
|
||||
$color2: #671AD9;
|
||||
$color3: #A152BD;
|
||||
$color4: #9E1EEA;
|
||||
$color5: #E424F5;
|
||||
$color6: #E65BF0;
|
||||
$color7: #e5c7e7;
|
||||
$color8: #a08ba1;
|
||||
$color9: #3F26DE;
|
||||
$color10: #671AD9;
|
||||
$color11: #A152BD;
|
||||
$color12: #9E1EEA;
|
||||
$color13: #E424F5;
|
||||
$color14: #E65BF0;
|
||||
$color15: #e5c7e7;
|
||||
|
||||
@@ -108,20 +108,34 @@ $notif_surface: $t_background;
|
||||
background: $activecolor;
|
||||
}
|
||||
|
||||
.notif-closeall-btn {
|
||||
.notif-listaction-btn {
|
||||
@include notif-rounding;
|
||||
padding: 0.341rem 0.341rem;
|
||||
padding: 0.341rem 0.682rem;
|
||||
}
|
||||
|
||||
.notif-closeall-btn:hover,
|
||||
.notif-closeall-btn:focus {
|
||||
.notif-listaction-btn:hover,
|
||||
.notif-listaction-btn:focus {
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.notif-closeall-btn:active {
|
||||
.notif-listaction-btn:active {
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.notif-listaction-btn-enabled {
|
||||
background-color: $secondaryContainer;
|
||||
color: $onSecondaryContainer;
|
||||
}
|
||||
|
||||
.notif-listaction-btn-enabled:hover,
|
||||
.notif-listaction-btn-enabled:focus {
|
||||
background-color: mix($secondaryContainer, $onSecondaryContainer, 90%);
|
||||
}
|
||||
|
||||
.notif-listaction-btn-enabled:active {
|
||||
background-color: mix($secondaryContainer, $onSecondaryContainer, 75%);
|
||||
}
|
||||
|
||||
.osd-notif {
|
||||
@include notif-rounding;
|
||||
background-color: transparentize(
|
||||
@@ -170,11 +184,11 @@ $notif_surface: $t_background;
|
||||
|
||||
.notif-action-low:focus,
|
||||
.notif-action-low:hover {
|
||||
background-color: mix($t_onSurfaceVariant, $t_surface, 18%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.notif-action-low:active {
|
||||
background-color: mix($t_onSurfaceVariant, $t_surface, 23%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.notif-action-normal {
|
||||
@@ -184,11 +198,11 @@ $notif_surface: $t_background;
|
||||
|
||||
.notif-action-normal:focus,
|
||||
.notif-action-normal:hover {
|
||||
background-color: mix($t_onSurfaceVariant, $t_surface, 18%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.notif-action-normal:active {
|
||||
background-color: mix($t_onSurfaceVariant, $t_surface, 23%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.notif-action-critical {
|
||||
|
||||
@@ -50,16 +50,16 @@ $osk_key_fontsize: 1.091rem;
|
||||
|
||||
.osk-key:hover,
|
||||
.osk-key:focus {
|
||||
background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 90%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.osk-key:active {
|
||||
background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 70%);
|
||||
background-color: $activecolor;
|
||||
font-size: $osk_key_fontsize;
|
||||
}
|
||||
|
||||
.osk-key-active {
|
||||
background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 70%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.osk-key-normal {
|
||||
@@ -109,4 +109,4 @@ $osk_key_fontsize: 1.091rem;
|
||||
.osk-control-button:active {
|
||||
background-color: mix($t_surfaceVariant, $t_onSurfaceVariant, 70%);
|
||||
font-size: $osk_key_fontsize;
|
||||
}
|
||||
}
|
||||
|
||||
+106
-57
@@ -85,11 +85,11 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-iconbutton:hover,
|
||||
.sidebar-iconbutton:focus {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-iconbutton:active {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-button {
|
||||
@@ -107,20 +107,20 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-button:hover,
|
||||
.sidebar-button:focus {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-button:active {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-button-nopad:hover,
|
||||
.sidebar-button-nopad:focus {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-button-nopad:active {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 60%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-button-left {
|
||||
@@ -148,11 +148,11 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-button-active:hover,
|
||||
.sidebar-button-active:focus {
|
||||
background-color: mix($primary, $hovercolor, 90%);
|
||||
background-color: mix($primary, $hovercolor, 70%);
|
||||
}
|
||||
|
||||
.sidebar-button-active:active {
|
||||
background-color: mix($primary, $hovercolor, 70%);
|
||||
background-color: mix($primary, $hovercolor, 40%);
|
||||
}
|
||||
|
||||
.sidebar-buttons-separator {
|
||||
@@ -166,27 +166,27 @@ $onChatgpt: $onPrimary;
|
||||
padding: 0rem $rounding_medium;
|
||||
}
|
||||
|
||||
.sidebar-navrail-btn>box>label {
|
||||
.sidebar-navrail-btn > box > label {
|
||||
@include full-rounding;
|
||||
@include menu_decel;
|
||||
}
|
||||
|
||||
.sidebar-navrail-btn:hover>box>label:first-child,
|
||||
.sidebar-navrail-btn:focus>box>label:first-child {
|
||||
background-color: mix($t_surfaceVariant, $onSurfaceVariant, 90%);
|
||||
.sidebar-navrail-btn:hover > box > label:first-child,
|
||||
.sidebar-navrail-btn:focus > box > label:first-child {
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-navrail-btn:active>box>label:first-child {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 75%);
|
||||
.sidebar-navrail-btn:active > box > label:first-child {
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-navrail-btn-active>box>label:first-child {
|
||||
.sidebar-navrail-btn-active > box > label:first-child {
|
||||
background-color: $secondaryContainer;
|
||||
color: $onSecondaryContainer;
|
||||
}
|
||||
|
||||
.sidebar-navrail-btn-active:hover>box>label:first-child,
|
||||
.sidebar-navrail-btn-active:focus>box>label:first-child {
|
||||
.sidebar-navrail-btn-active:hover > box > label:first-child,
|
||||
.sidebar-navrail-btn-active:focus > box > label:first-child {
|
||||
background-color: mix($secondaryContainer, $hovercolor, 90%);
|
||||
color: mix($onSecondaryContainer, $hovercolor, 90%);
|
||||
}
|
||||
@@ -279,11 +279,11 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-calendar-btn-today:hover,
|
||||
.sidebar-calendar-btn-today:focus {
|
||||
background-color: mix($primary, $hovercolor, 90%);
|
||||
background-color: mix($primary, $hovercolor, 70%);
|
||||
}
|
||||
|
||||
.sidebar-calendar-btn-today:active {
|
||||
background-color: mix($primary, $hovercolor, 70%);
|
||||
background-color: mix($primary, $hovercolor, 40%);
|
||||
}
|
||||
|
||||
.sidebar-calendar-btn-othermonth {
|
||||
@@ -303,12 +303,12 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-calendar-monthyear-btn:hover,
|
||||
.sidebar-calendar-monthyear-btn:focus {
|
||||
background-color: mix($t_surfaceVariant, $onSurfaceVariant, 95%);
|
||||
background-color: $hovercolor;
|
||||
color: mix($onSurfaceVariant, $surfaceVariant, 95%);
|
||||
}
|
||||
|
||||
.sidebar-calendar-monthyear-btn:active {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 85%);
|
||||
background-color: $activecolor;
|
||||
color: mix($onSurfaceVariant, $surfaceVariant, 85%);
|
||||
}
|
||||
|
||||
@@ -321,12 +321,12 @@ $onChatgpt: $onPrimary;
|
||||
}
|
||||
|
||||
.sidebar-calendar-monthshift-btn:hover {
|
||||
background-color: mix($t_surfaceVariant, $onSurfaceVariant, 95%);
|
||||
background-color: $hovercolor;
|
||||
color: mix($onSurfaceVariant, $surfaceVariant, 95%);
|
||||
}
|
||||
|
||||
.sidebar-calendar-monthshift-btn:active {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 85%);
|
||||
background-color: $activecolor;
|
||||
color: mix($onSurfaceVariant, $surfaceVariant, 85%);
|
||||
}
|
||||
|
||||
@@ -339,14 +339,14 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-selector-tab:hover,
|
||||
.sidebar-selector-tab:focus {
|
||||
background-color: mix($t_surfaceVariant, $onSurfaceVariant, 90%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-selector-tab:active {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 75%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-selector-tab-active>box>label {
|
||||
.sidebar-selector-tab-active > box > label {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
@@ -378,11 +378,11 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-todo-item-action:hover,
|
||||
.sidebar-todo-item-action:focus {
|
||||
background-color: mix($t_surface, $t_onSurface, 80%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-todo-item-action:active {
|
||||
background-color: mix($t_surface, $t_onSurface, 65%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-todo-crosser {
|
||||
@@ -398,23 +398,6 @@ $onChatgpt: $onPrimary;
|
||||
background-color: $error;
|
||||
}
|
||||
|
||||
.sidebar-clipboard-item {
|
||||
border-radius: $rounding_small;
|
||||
min-height: 2.045rem;
|
||||
padding: 0.341rem;
|
||||
background-color: $t_secondaryContainer;
|
||||
color: $onSecondaryContainer;
|
||||
}
|
||||
|
||||
.sidebar-clipboard-item:hover,
|
||||
.sidebar-clipboard-item:focus {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 90%);
|
||||
}
|
||||
|
||||
.sidebar-clipboard-item:active {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 80%);
|
||||
}
|
||||
|
||||
.sidebar-todo-new {
|
||||
@include full-rounding;
|
||||
color: $onSecondaryContainer;
|
||||
@@ -423,7 +406,7 @@ $onChatgpt: $onPrimary;
|
||||
border: 0.068rem solid $onSurface;
|
||||
}
|
||||
|
||||
.sidebar-todo-new:hover,
|
||||
.sidebar-todo-newz,
|
||||
.sidebar-todo-new:focus {
|
||||
background-color: mix($t_secondaryContainer, $t_onSecondaryContainer, 97%);
|
||||
}
|
||||
@@ -509,7 +492,7 @@ $onChatgpt: $onPrimary;
|
||||
.sidebar-chat-apiswitcher-icon {
|
||||
@include menu_decel;
|
||||
@include full-rounding;
|
||||
min-width: 2.182rem;
|
||||
min-width: 2.182rem;
|
||||
min-height: 2.182rem;
|
||||
color: $onSurface;
|
||||
}
|
||||
@@ -548,11 +531,19 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-chat-send:hover,
|
||||
.sidebar-chat-send:focus {
|
||||
background-color: mix($sidebar_chat_textboxareaColor, $t_onSecondaryContainer, 97%);
|
||||
background-color: mix(
|
||||
$sidebar_chat_textboxareaColor,
|
||||
$t_onSecondaryContainer,
|
||||
97%
|
||||
);
|
||||
}
|
||||
|
||||
.sidebar-chat-send:active {
|
||||
background-color: mix($sidebar_chat_textboxareaColor, $t_onSecondaryContainer, 80%);
|
||||
background-color: mix(
|
||||
$sidebar_chat_textboxareaColor,
|
||||
$t_onSecondaryContainer,
|
||||
80%
|
||||
);
|
||||
}
|
||||
|
||||
.sidebar-chat-send-available {
|
||||
@@ -574,8 +565,10 @@ $onChatgpt: $onPrimary;
|
||||
}
|
||||
|
||||
.sidebar-chat-indicator {
|
||||
@include menu_decel;
|
||||
@include full-rounding;
|
||||
min-width: 0.136rem;
|
||||
background-color: $onBackground;
|
||||
}
|
||||
|
||||
.sidebar-chat-indicator-user {
|
||||
@@ -594,7 +587,6 @@ $onChatgpt: $onPrimary;
|
||||
@include titlefont;
|
||||
padding: 0.341rem;
|
||||
margin-left: -0.136rem;
|
||||
padding: 0.341rem;
|
||||
padding-left: 0.818rem;
|
||||
}
|
||||
|
||||
@@ -621,7 +613,7 @@ $onChatgpt: $onPrimary;
|
||||
@include mainfont;
|
||||
margin: 0.273rem;
|
||||
margin-bottom: 0rem;
|
||||
background-color: $secondaryContainer;
|
||||
background-color: mix($t_secondaryContainer, $t_onSurfaceVariant, 30%);
|
||||
color: $onSecondaryContainer;
|
||||
border-radius: $rounding_medium - 0.273rem;
|
||||
border: 0.068rem solid mix($secondaryContainer, $onSecondaryContainer, 90%);
|
||||
@@ -637,7 +629,7 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-chat-codeblock-topbar-btn {
|
||||
@include full-rounding;
|
||||
padding: 0.273rem;
|
||||
padding: 0.273rem 0.477rem;
|
||||
}
|
||||
|
||||
.sidebar-chat-codeblock-topbar-btn:hover,
|
||||
@@ -695,12 +687,11 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-chat-chip-action:hover,
|
||||
.sidebar-chat-chip-action:focus {
|
||||
background-color: $sidebar_chat_textboxareaColor;
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-chat-chip-action:active {
|
||||
background-color: mix($sidebar_chat_textboxareaColor, $onSurfaceVariant, 70%);
|
||||
color: mix($sidebar_chat_textboxareaColor, $surfaceVariant, 70%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-chat-chip-action-active {
|
||||
@@ -708,6 +699,21 @@ $onChatgpt: $onPrimary;
|
||||
border: 0.068rem solid $sidebar_chat_textboxareaColor;
|
||||
}
|
||||
|
||||
.sidebar-chat-chip-toggle {
|
||||
@include menu_decel;
|
||||
@include small-rounding;
|
||||
padding: 0.341rem 0.477rem;
|
||||
background-color: $t_surfaceVariant;
|
||||
color: $onSurfaceVariant;
|
||||
}
|
||||
.sidebar-chat-chip-toggle:focus,
|
||||
.sidebar-chat-chip-toggle:hover {
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
.sidebar-chat-chip-toggle:active {
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-pin {
|
||||
@include small-rounding;
|
||||
@include menu_decel;
|
||||
@@ -718,11 +724,11 @@ $onChatgpt: $onPrimary;
|
||||
|
||||
.sidebar-pin:hover,
|
||||
.sidebar-pin:focus {
|
||||
background-color: mix($t_surfaceVariant, $onSurfaceVariant, 90%);
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
.sidebar-pin:active {
|
||||
background-color: mix($surfaceVariant, $onSurfaceVariant, 75%);
|
||||
background-color: $activecolor;
|
||||
}
|
||||
|
||||
.sidebar-pin-enabled {
|
||||
@@ -741,3 +747,46 @@ $onChatgpt: $onPrimary;
|
||||
background-color: mix($primary, $onPrimary, 80%);
|
||||
}
|
||||
|
||||
.sidebar-waifu-heading {
|
||||
@include titlefont;
|
||||
padding: 0.341rem;
|
||||
margin-left: -0.136rem;
|
||||
padding-left: 0.818rem;
|
||||
}
|
||||
.sidebar-waifu-content {
|
||||
margin-left: 0.682rem;
|
||||
}
|
||||
.sidebar-waifu-txt {
|
||||
@include readingfont;
|
||||
margin-left: 0.682rem;
|
||||
}
|
||||
.sidebar-waifu-image {
|
||||
margin-left: 0.682rem;
|
||||
@include normal-rounding;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
.sidebar-waifu-image-actions {
|
||||
padding: 0.313rem;
|
||||
}
|
||||
$waifu_image_overlay_transparency: 0.7;
|
||||
.sidebar-waifu-image-action {
|
||||
@include full-rounding;
|
||||
min-width: 1.875rem;
|
||||
min-height: 1.875rem;
|
||||
background-color: rgba(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
$waifu_image_overlay_transparency
|
||||
); // Fixed cuz on image
|
||||
color: rgba(255, 255, 255, $waifu_image_overlay_transparency);
|
||||
}
|
||||
.sidebar-waifu-image-action:hover,
|
||||
.sidebar-waifu-image-action:focus {
|
||||
background-color: rgba(30, 30, 30, $waifu_image_overlay_transparency);
|
||||
}
|
||||
.sidebar-waifu-image-action:active {
|
||||
background-color: rgba(60, 60, 60, $waifu_image_overlay_transparency);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Service, Utils } from '../imports.js';
|
||||
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);
|
||||
@@ -41,7 +42,7 @@ class BrightnessService extends Service {
|
||||
this._screenValue = current / max;
|
||||
}
|
||||
|
||||
// overwriting connectWidget method, let's you
|
||||
// overwriting connectWidget method, lets you
|
||||
// change the default event that widgets connect to
|
||||
connectWidget(widget, callback, event = 'screen-changed') {
|
||||
super.connectWidget(widget, callback, event);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Utils, Widget } from '../imports.js';
|
||||
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';
|
||||
@@ -44,7 +45,17 @@ function expandTilde(path) {
|
||||
|
||||
// We're using many models to not be restricted to 3 messages per minute.
|
||||
// The whole chat will be sent every request anyway.
|
||||
const KEY_FILE_LOCATION = `~/.cache/ags/user/openai_api_key.txt`;
|
||||
const KEY_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/openai_api_key.txt`;
|
||||
const APIDOM_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/openai_api_dom.txt`;
|
||||
function replaceapidom(URL) {
|
||||
//Utils.writeFile(URL, "/tmp/openai-url-old.log"); // For debugging
|
||||
if (fileExists(expandTilde(APIDOM_FILE_LOCATION))) {
|
||||
var contents = Utils.readFile(expandTilde(APIDOM_FILE_LOCATION)).trim();
|
||||
var URL = URL.toString().replace("api.openai.com", contents);
|
||||
}
|
||||
//Utils.writeFile(URL, "/tmp/openai-url.log"); // For debugging
|
||||
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;
|
||||
|
||||
@@ -126,7 +137,8 @@ class ChatGPTService extends Service {
|
||||
_modelIndex = 0;
|
||||
_key = '';
|
||||
_decoder = new TextDecoder();
|
||||
url = GLib.Uri.parse('https://api.openai.com/v1/chat/completions', GLib.UriFlags.NONE);
|
||||
|
||||
url = GLib.Uri.parse(replaceapidom('https://api.openai.com/v1/chat/completions'), GLib.UriFlags.NONE);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Service, Utils } from '../imports.js';
|
||||
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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { Notify, GLib, Gio } = imports.gi;
|
||||
import { Utils } from '../imports.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ const FIRST_RUN_NOTIF_BODY = `Looks like this is your first run.\nHit <span fore
|
||||
|
||||
export async function firstRunWelcome() {
|
||||
if (!fileExists(FIRST_RUN_PATH)) {
|
||||
console.log('uuwuwuwuwuwuwuwuu');
|
||||
Utils.writeFile(FIRST_RUN_FILE_CONTENT, FIRST_RUN_PATH)
|
||||
.then(() => {
|
||||
// Note that we add a little delay to make sure the cool circular progress works
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
if (typeof b !== "function" && b !== null)
|
||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
||||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
||||
if (ar || !(i in from)) {
|
||||
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
||||
ar[i] = from[i];
|
||||
}
|
||||
}
|
||||
return to.concat(ar || Array.prototype.slice.call(from));
|
||||
};
|
||||
var _a, _b, _c, _d;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.sway = exports.Sway = exports.SwayActives = exports.SwayActiveID = exports.SwayActiveClient = void 0;
|
||||
var _1 = require("gi://GLib");
|
||||
var _2 = require("gi://Gio");
|
||||
var service_js_1 = require("../service.js");
|
||||
var SIS = _1.default.getenv('SWAYSOCK');
|
||||
var SwayActiveClient = /** @class */ (function (_super) {
|
||||
__extends(SwayActiveClient, _super);
|
||||
function SwayActiveClient() {
|
||||
var _this = _super !== null && _super.apply(this, arguments) || this;
|
||||
_this._id = 0;
|
||||
_this._name = '';
|
||||
_this._class = '';
|
||||
return _this;
|
||||
}
|
||||
Object.defineProperty(SwayActiveClient.prototype, "id", {
|
||||
get: function () { return this._id; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(SwayActiveClient.prototype, "name", {
|
||||
get: function () { return this._name; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(SwayActiveClient.prototype, "class", {
|
||||
get: function () { return this._class; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
SwayActiveClient.prototype.updateProperty = function (prop, value) {
|
||||
_super.prototype.updateProperty.call(this, prop, value);
|
||||
this.emit('changed');
|
||||
};
|
||||
return SwayActiveClient;
|
||||
}(service_js_1.default));
|
||||
exports.SwayActiveClient = SwayActiveClient;
|
||||
_a = SwayActiveClient;
|
||||
(function () {
|
||||
service_js_1.default.register(_a, {}, {
|
||||
'id': ['int'],
|
||||
'name': ['string'],
|
||||
'class': ['string'],
|
||||
});
|
||||
})();
|
||||
var SwayActiveID = /** @class */ (function (_super) {
|
||||
__extends(SwayActiveID, _super);
|
||||
function SwayActiveID() {
|
||||
var _this = _super !== null && _super.apply(this, arguments) || this;
|
||||
_this._id = 1;
|
||||
_this._name = '';
|
||||
return _this;
|
||||
}
|
||||
Object.defineProperty(SwayActiveID.prototype, "id", {
|
||||
get: function () { return this._id; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(SwayActiveID.prototype, "name", {
|
||||
get: function () { return this._name; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
SwayActiveID.prototype.update = function (id, name) {
|
||||
_super.prototype.updateProperty.call(this, 'id', id);
|
||||
_super.prototype.updateProperty.call(this, 'name', name);
|
||||
this.emit('changed');
|
||||
};
|
||||
return SwayActiveID;
|
||||
}(service_js_1.default));
|
||||
exports.SwayActiveID = SwayActiveID;
|
||||
_b = SwayActiveID;
|
||||
(function () {
|
||||
service_js_1.default.register(_b, {}, {
|
||||
'id': ['int'],
|
||||
'name': ['string'],
|
||||
});
|
||||
})();
|
||||
var SwayActives = /** @class */ (function (_super) {
|
||||
__extends(SwayActives, _super);
|
||||
function SwayActives() {
|
||||
var _this = _super.call(this) || this;
|
||||
_this._client = new SwayActiveClient;
|
||||
_this._monitor = new SwayActiveID;
|
||||
_this._workspace = new SwayActiveID;
|
||||
['client', 'workspace', 'monitor'].forEach(function (obj) {
|
||||
_this["_".concat(obj)].connect('changed', function () {
|
||||
_this.notify(obj);
|
||||
_this.emit('changed');
|
||||
});
|
||||
});
|
||||
return _this;
|
||||
}
|
||||
Object.defineProperty(SwayActives.prototype, "client", {
|
||||
get: function () { return this._client; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(SwayActives.prototype, "monitor", {
|
||||
get: function () { return this._monitor; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(SwayActives.prototype, "workspace", {
|
||||
get: function () { return this._workspace; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
return SwayActives;
|
||||
}(service_js_1.default));
|
||||
exports.SwayActives = SwayActives;
|
||||
_c = SwayActives;
|
||||
(function () {
|
||||
service_js_1.default.register(_c, {}, {
|
||||
'client': ['jsobject'],
|
||||
'monitor': ['jsobject'],
|
||||
'workspace': ['jsobject'],
|
||||
});
|
||||
})();
|
||||
var Sway = /** @class */ (function (_super) {
|
||||
__extends(Sway, _super);
|
||||
function Sway() {
|
||||
var _this = this;
|
||||
if (!SIS)
|
||||
console.error('Sway is not running');
|
||||
_this = _super.call(this) || this;
|
||||
_this._decoder = new TextDecoder();
|
||||
_this._encoder = new TextEncoder();
|
||||
_this._active = new SwayActives();
|
||||
_this._monitors = new Map();
|
||||
_this._workspaces = new Map();
|
||||
_this._clients = new Map();
|
||||
var socket = new _2.default.SocketClient().connect(new _2.default.UnixSocketAddress({
|
||||
path: "".concat(SIS),
|
||||
}), null);
|
||||
_this._watchSocket(socket.get_input_stream());
|
||||
_this._output_stream = socket.get_output_stream();
|
||||
_this.send(4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */, '');
|
||||
_this.send(2 /* PAYLOAD_TYPE.MESSAGE_SUBSCRIBE */, JSON.stringify(['window', 'workspace']));
|
||||
_this._active.connect('changed', function () { return _this.emit('changed'); });
|
||||
['monitor', 'workspace', 'client'].forEach(function (active) {
|
||||
return _this._active.connect("notify::".concat(active), function () { return _this.notify('active'); });
|
||||
});
|
||||
return _this;
|
||||
}
|
||||
Object.defineProperty(Sway.prototype, "active", {
|
||||
get: function () { return this._active; },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(Sway.prototype, "monitors", {
|
||||
get: function () { return Array.from(this._monitors.values()); },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(Sway.prototype, "workspaces", {
|
||||
get: function () { return Array.from(this._workspaces.values()); },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(Sway.prototype, "clients", {
|
||||
get: function () { return Array.from(this._clients.values()); },
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
Sway.prototype.getMonitor = function (id) { return this._monitors.get(id); };
|
||||
Sway.prototype.getWorkspace = function (name) { return this._workspaces.get(name); };
|
||||
Sway.prototype.getClient = function (id) { return this._clients.get(id); };
|
||||
Sway.prototype.send = function (payloadType, payload) {
|
||||
var pb = this._encoder.encode(payload);
|
||||
var type = new Uint32Array([payloadType]);
|
||||
var pl = new Uint32Array([pb.length]);
|
||||
var magic_string = this._encoder.encode('i3-ipc');
|
||||
var data = new Uint8Array(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], magic_string, true), (new Uint8Array(pl.buffer)), true), (new Uint8Array(type.buffer)), true), pb, true));
|
||||
this._output_stream.write(data, null);
|
||||
};
|
||||
Sway.prototype._watchSocket = function (stream) {
|
||||
var _this = this;
|
||||
stream.read_bytes_async(14, _1.default.PRIORITY_DEFAULT, null, function (_, resultHeader) {
|
||||
var data = stream.read_bytes_finish(resultHeader).get_data();
|
||||
if (!data)
|
||||
return;
|
||||
var payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0];
|
||||
var payloadType = new Uint32Array(data.slice(10, 14).buffer)[0];
|
||||
stream.read_bytes_async(payloadLength, _1.default.PRIORITY_DEFAULT, null, function (_, resultPayload) {
|
||||
var data = stream.read_bytes_finish(resultPayload).get_data();
|
||||
if (!data)
|
||||
return;
|
||||
_this._onEvent(payloadType, JSON.parse(_this._decoder.decode(data)));
|
||||
_this._watchSocket(stream);
|
||||
});
|
||||
});
|
||||
};
|
||||
Sway.prototype._onEvent = function (event_type, event) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_e) {
|
||||
if (!event)
|
||||
return [2 /*return*/];
|
||||
try {
|
||||
switch (event_type) {
|
||||
case 2147483648 /* PAYLOAD_TYPE.EVENT_WORKSPACE */:
|
||||
this._handleWorkspaceEvent(event);
|
||||
break;
|
||||
case 2147483651 /* PAYLOAD_TYPE.EVENT_WINDOW */:
|
||||
this._handleWindowEvent(event);
|
||||
break;
|
||||
case 4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */:
|
||||
this._handleTreeMessage(event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
logError(error);
|
||||
}
|
||||
this.emit('changed');
|
||||
return [2 /*return*/];
|
||||
});
|
||||
});
|
||||
};
|
||||
Sway.prototype._handleWorkspaceEvent = function (workspaceEvent) {
|
||||
var workspace = workspaceEvent.current;
|
||||
switch (workspaceEvent.change) {
|
||||
case 'init':
|
||||
this._workspaces.set(workspace.name, workspace);
|
||||
this.notify('workspaces');
|
||||
break;
|
||||
case 'empty':
|
||||
this._workspaces.delete(workspace.name);
|
||||
this.notify('workspaces');
|
||||
break;
|
||||
case 'focus':
|
||||
this._active.workspace.update(workspace.id, workspace.name);
|
||||
this._active.monitor.update(1, workspace.output);
|
||||
this._workspaces.set(workspace.name, workspace);
|
||||
this._workspaces.set(workspaceEvent.old.name, workspaceEvent.old);
|
||||
this.notify('workspaces');
|
||||
break;
|
||||
case 'rename':
|
||||
if (this._active.workspace.id === workspace.id)
|
||||
this._active.workspace.updateProperty('name', workspace.name);
|
||||
this._workspaces.set(workspace.name, workspace);
|
||||
this.notify('workspaces');
|
||||
break;
|
||||
case 'reload':
|
||||
break;
|
||||
case 'move':
|
||||
case 'urgent':
|
||||
default:
|
||||
this._workspaces.set(workspace.name, workspace);
|
||||
this.notify('workspaces');
|
||||
}
|
||||
};
|
||||
Sway.prototype._handleWindowEvent = function (clientEvent) {
|
||||
var _e;
|
||||
var client = clientEvent.container;
|
||||
var id = client.id;
|
||||
switch (clientEvent.change) {
|
||||
case 'new':
|
||||
this._clients.set(id, client);
|
||||
this.notify('clients');
|
||||
break;
|
||||
case 'close':
|
||||
this._clients.delete(id);
|
||||
this.notify('clients');
|
||||
break;
|
||||
case 'focus':
|
||||
if (this._active.client.id === id)
|
||||
return;
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
var current_active = this._clients.get(this._active.client.id);
|
||||
if (current_active)
|
||||
current_active.focused = false;
|
||||
this._active.client.updateProperty('id', id);
|
||||
this._active.client.updateProperty('name', client.name);
|
||||
this._active.client.updateProperty('class', client.shell === 'xwayland'
|
||||
? ((_e = client.window_properties) === null || _e === void 0 ? void 0 : _e.class) || ''
|
||||
: client.app_id);
|
||||
break;
|
||||
case 'title':
|
||||
if (client.focused)
|
||||
this._active.client.updateProperty('name', client.name);
|
||||
this._clients.set(id, client);
|
||||
this.notify('clients');
|
||||
break;
|
||||
case 'fullscreen_mode':
|
||||
case 'move':
|
||||
case 'floating':
|
||||
case 'urgent':
|
||||
case 'mark':
|
||||
default:
|
||||
this._clients.set(id, client);
|
||||
this.notify('clients');
|
||||
}
|
||||
};
|
||||
Sway.prototype._handleTreeMessage = function (node) {
|
||||
var _this = this;
|
||||
var _e;
|
||||
switch (node.type) {
|
||||
case 'root':
|
||||
this._workspaces.clear();
|
||||
this._clients.clear();
|
||||
this._monitors.clear();
|
||||
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
|
||||
['workspaces', 'clients', 'monitors'].forEach(function (t) {
|
||||
_this.notify(t);
|
||||
});
|
||||
break;
|
||||
case 'output':
|
||||
this._monitors.set(node.id, node);
|
||||
if (node.active)
|
||||
this._active.monitor.updateProperty('name', node.name);
|
||||
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
|
||||
this.notify('monitors');
|
||||
break;
|
||||
case 'workspace':
|
||||
this._workspaces.set(node.name, node);
|
||||
// I think I'm missing something. There has to be a better way.
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
var hasFocusedChild_1 = function (n) { return n.nodes.some(function (c) { return c.focused || hasFocusedChild_1(c); }); };
|
||||
if (hasFocusedChild_1(node))
|
||||
this._active.workspace.update(node.id, node.name);
|
||||
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
|
||||
this.notify('workspaces');
|
||||
break;
|
||||
case 'con':
|
||||
case 'floating_con':
|
||||
this._clients.set(node.id, node);
|
||||
if (node.focused) {
|
||||
this._active.client.updateProperty('id', node.id);
|
||||
this._active.client.updateProperty('name', node.name);
|
||||
this._active.client.updateProperty('class', node.shell === 'xwayland'
|
||||
? ((_e = node.window_properties) === null || _e === void 0 ? void 0 : _e.class) || ''
|
||||
: node.app_id);
|
||||
}
|
||||
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
|
||||
this.notify('clients');
|
||||
break;
|
||||
}
|
||||
};
|
||||
return Sway;
|
||||
}(service_js_1.default));
|
||||
exports.Sway = Sway;
|
||||
_d = Sway;
|
||||
(function () {
|
||||
service_js_1.default.register(_d, {}, {
|
||||
'active': ['jsobject'],
|
||||
'monitors': ['jsobject'],
|
||||
'workspaces': ['jsobject'],
|
||||
'clients': ['jsobject'],
|
||||
});
|
||||
})();
|
||||
exports.sway = new Sway;
|
||||
exports.default = exports.sway;
|
||||
@@ -1,5 +1,6 @@
|
||||
const { Gio, Gdk, GLib, Gtk } = imports.gi;
|
||||
import { Service, Utils } from '../imports.js';
|
||||
const { Gio, GLib } = imports.gi;
|
||||
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { exec, execAsync } = Utils;
|
||||
|
||||
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
||||
@@ -63,7 +64,7 @@ class TodoService extends Service {
|
||||
super();
|
||||
this._todoPath = `${GLib.get_user_cache_dir()}/ags/user/todo.json`;
|
||||
if (!fileExists(this._todoPath)) { // No? create file with empty array
|
||||
Utils.exec(`bash -c 'mkdir -p ~/.cache/ags/user'`);
|
||||
Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`);
|
||||
Utils.exec(`touch ${this._todoPath}`);
|
||||
Utils.writeFile("[]", this._todoPath).then(() => {
|
||||
this._todoJson = JSON.parse(Utils.readFile(this._todoPath))
|
||||
|
||||
+100
-111
@@ -1,68 +1,35 @@
|
||||
import { Utils, Widget } from '../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
||||
import Gio from 'gi://Gio';
|
||||
import GLib from 'gi://GLib';
|
||||
import Soup from 'gi://Soup?version=3.0';
|
||||
import { fileExists } from './messages.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
class WaifuResponse extends Service {
|
||||
static {
|
||||
Service.register(this,
|
||||
{
|
||||
'delta': ['string'],
|
||||
},
|
||||
{
|
||||
'content': ['string'],
|
||||
'thinking': ['boolean'],
|
||||
'done': ['boolean'],
|
||||
});
|
||||
}
|
||||
// Usage from my python waifu fetcher, for reference
|
||||
// Usage: waifu-get.py [OPTION]... [TAG]...
|
||||
// Options:
|
||||
// --im\tUse waifu.im API. You can use many tags
|
||||
// --pics\tUse waifu.pics API. Use 1 tag only.
|
||||
// --nekos\tUse nekos.life (old) API. No tags.
|
||||
// --segs\tForce NSFW images
|
||||
|
||||
_role = '';
|
||||
_content = '';
|
||||
_thinking = false;
|
||||
_done = false;
|
||||
// Tags:
|
||||
// waifu.im (type):
|
||||
// maid waifu marin-kitagawa mori-calliope raiden-shogun oppai selfies uniform
|
||||
// waifu.im (nsfw tags):
|
||||
// ecchi hentai ero ass paizuri oral milf
|
||||
|
||||
constructor(role, content, thinking = false, done = false) {
|
||||
super();
|
||||
this._role = role;
|
||||
this._content = content;
|
||||
this._thinking = thinking;
|
||||
this._done = done;
|
||||
}
|
||||
|
||||
get done() { return this._done }
|
||||
set done(isDone) { this._done = isDone; this.notify('done') }
|
||||
|
||||
get role() { return this._role }
|
||||
set role(role) { this._role = role; this.emit('changed') }
|
||||
|
||||
get content() { return this._content }
|
||||
set content(content) {
|
||||
this._content = content;
|
||||
this.notify('content')
|
||||
this.emit('changed')
|
||||
}
|
||||
|
||||
get label() { return this._parserState.parsed + this._parserState.stack.join('') }
|
||||
|
||||
get thinking() { return this._thinking }
|
||||
set thinking(thinking) {
|
||||
this._thinking = thinking;
|
||||
this.notify('thinking')
|
||||
this.emit('changed')
|
||||
}
|
||||
|
||||
addDelta(delta) {
|
||||
if (this.thinking) {
|
||||
this.thinking = false;
|
||||
this.content = delta;
|
||||
}
|
||||
else {
|
||||
this.content += delta;
|
||||
}
|
||||
this.emit('delta', delta);
|
||||
}
|
||||
function paramStringFromObj(params) {
|
||||
return Object.entries(params)
|
||||
.map(([key, value]) => {
|
||||
if (Array.isArray(value)) { // If it's an array, repeat
|
||||
if (value.length == 0) return '';
|
||||
let thisKey = `${encodeURIComponent(key)}=${encodeURIComponent(value[0])}`
|
||||
for (let i = 1; i < value.length; i++) {
|
||||
thisKey += `&${encodeURIComponent(key)}=${encodeURIComponent(value[i])}`;
|
||||
}
|
||||
return thisKey;
|
||||
}
|
||||
return `${key}=${value}`;
|
||||
})
|
||||
.join('&');
|
||||
}
|
||||
|
||||
class WaifuService extends Service {
|
||||
@@ -76,17 +43,20 @@ class WaifuService extends Service {
|
||||
'nekos': {},
|
||||
'pics': {},
|
||||
}
|
||||
_url = 'https://api.waifu.im/search';
|
||||
_baseUrl = 'https://api.waifu.im/search';
|
||||
_mode = 'im'; // Allowed: im
|
||||
_responses = [];
|
||||
_queries = [];
|
||||
_nsfw = false;
|
||||
_minHeight = 600;
|
||||
_status = 0;
|
||||
|
||||
static {
|
||||
Service.register(this, {
|
||||
'initialized': [],
|
||||
'clear': [],
|
||||
'newResponse': ['string'],
|
||||
'newResponse': ['int'],
|
||||
'updateResponse': ['int'],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -97,71 +67,90 @@ class WaifuService extends Service {
|
||||
|
||||
clear() {
|
||||
this._responses = [];
|
||||
this._queries = [];
|
||||
this.emit('clear');
|
||||
}
|
||||
|
||||
get mode() { return this._mode }
|
||||
set mode(value) {
|
||||
this._mode = value;
|
||||
this._url = this._endpoints[this._mode];
|
||||
this._baseUrl = this._endpoints[this._mode];
|
||||
}
|
||||
get nsfw() { return this._nsfw }
|
||||
set nsfw(value) { this._nsfw = value }
|
||||
get queries() { return this._queries }
|
||||
get responses() { return this._responses }
|
||||
|
||||
readResponseRecursive(stream, response) {
|
||||
stream.read_line_async(
|
||||
0, null,
|
||||
(stream, res) => {
|
||||
if (!stream) return;
|
||||
const [bytes] = stream.read_line_finish(res);
|
||||
const line = this._decoder.decode(bytes);
|
||||
if (line && line != '') {
|
||||
let data = line.substr(6);
|
||||
if (data == '[DONE]') return;
|
||||
try {
|
||||
const result = JSON.parse(data);
|
||||
if (result.choices[0].finish_reason === 'stop') {
|
||||
response.done = true;
|
||||
return;
|
||||
}
|
||||
response.addDelta(result.choices[0].delta.content);
|
||||
}
|
||||
catch {
|
||||
response.addDelta(line + '\n');
|
||||
}
|
||||
}
|
||||
this.readResponseRecursive(stream, response);
|
||||
});
|
||||
}
|
||||
|
||||
fetch(msg) {
|
||||
const taglist = msg.split(' ');
|
||||
this.emit('newResponse', msg);
|
||||
this._responses.push(msg);
|
||||
|
||||
async fetch(msg) {
|
||||
// Init
|
||||
const userArgs = msg.split(' ');
|
||||
let taglist = [];
|
||||
this._nsfw = false;
|
||||
// Construct body/headers
|
||||
for (let i = 0; i < userArgs.length; i++) {
|
||||
const thisArg = userArgs[i];
|
||||
if (thisArg == '--im') this._mode = 'im';
|
||||
else if (thisArg == '--nekos') this._mode = 'nekos';
|
||||
else if (thisArg.includes('pics')) this._mode = 'pics';
|
||||
else if (thisArg.includes('segs') || thisArg.includes('sex') || thisArg.includes('lewd')) this._nsfw = true;
|
||||
else {
|
||||
taglist.push(thisArg);
|
||||
if(['ecchi', 'hentai', 'ero', 'ass', 'paizuri', 'oral', 'milf'].includes(thisArg)) this._nsfw = true;
|
||||
}
|
||||
}
|
||||
const newMessageId = this._queries.length;
|
||||
this._queries.push(taglist);
|
||||
this.emit('newResponse', newMessageId);
|
||||
const params = {
|
||||
'included_tags': taglist,
|
||||
'height': `>=${this._minHeight}`,
|
||||
'nsfw': this._nsfw,
|
||||
};
|
||||
|
||||
const session = new Soup.Session();
|
||||
const message = new Soup.Message({
|
||||
const paramString = paramStringFromObj(params);
|
||||
// Fetch
|
||||
// Note: body isn't included since passing directly to url is more reliable
|
||||
const options = {
|
||||
method: 'GET',
|
||||
uri: GLib.Uri.parse(this._url, GLib.UriFlags.NONE),
|
||||
});
|
||||
session.send_message(message, (session, message) => {
|
||||
if (message.status_code === 200) {
|
||||
const responseBody = message.response_body.data;
|
||||
const data = JSON.parse(responseBody);
|
||||
// Process the response data as needed
|
||||
console.log(data);
|
||||
log(data);
|
||||
} else {
|
||||
logError('Request failed with status code: ' + message.status_code);
|
||||
}
|
||||
});
|
||||
headers: this._headers[this._mode],
|
||||
};
|
||||
var status = 0;
|
||||
Utils.fetch(`${this._endpoints[this._mode]}?${paramString}`, options)
|
||||
.then(result => {
|
||||
status = result.status;
|
||||
return result.text();
|
||||
})
|
||||
.then((dataString) => { // Store interesting stuff and emit
|
||||
const parsedData = JSON.parse(dataString);
|
||||
if (!parsedData.images) this._responses.push({
|
||||
status: status,
|
||||
signature: -1,
|
||||
url: '',
|
||||
extension: '',
|
||||
source: '',
|
||||
dominant_color: '#383A40',
|
||||
is_nsfw: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
tags: [],
|
||||
});
|
||||
else {
|
||||
const imageData = parsedData.images[0];
|
||||
this._responses.push({
|
||||
status: status,
|
||||
signature: imageData?.signature || -1,
|
||||
url: imageData?.url || undefined,
|
||||
extension: imageData.extension,
|
||||
source: imageData?.source,
|
||||
dominant_color: imageData?.dominant_color || '#9392A6',
|
||||
is_nsfw: imageData?.is_nsfw || false,
|
||||
width: imageData?.width || 0,
|
||||
height: imageData?.height || 0,
|
||||
tags: imageData?.tags.map(obj => obj["name"]) || [],
|
||||
});
|
||||
}
|
||||
this.emit('updateResponse', newMessageId);
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+526
-436
File diff suppressed because it is too large
Load Diff
@@ -1,44 +1,42 @@
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
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';
|
||||
const { CONFIG_DIR, exec, execAsync } = Utils;
|
||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||
import { RoundedCorner } from "../../lib/roundedcorner.js";
|
||||
import Brightness from '../../services/brightness.js';
|
||||
import Indicator from '../../services/indicator.js';
|
||||
|
||||
// Removes everything after the last
|
||||
// em dash, en dash, minus, vertical bar, or middle dot (note: maybe add open parenthesis?)
|
||||
// For example:
|
||||
// • Discord | #ricing-theming | r/unixporn — Mozilla Firefox --> • Discord | #ricing-theming
|
||||
// GJS Error · Issue #112 · Aylur/ags — Mozilla Firefox --> GJS Error · Issue #112
|
||||
function truncateTitle(str) {
|
||||
let lastDash = -1;
|
||||
let found = -1; // 0: em dash, 1: en dash, 2: minus, 3: vertical bar, 4: middle dot
|
||||
for (let i = str.length - 1; i >= 0; i--) {
|
||||
if (str[i] === '—') {
|
||||
found = 0;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '–' && found < 1) {
|
||||
found = 1;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '-' && found < 2) {
|
||||
found = 2;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '|' && found < 3) {
|
||||
found = 3;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '·' && found < 4) {
|
||||
found = 4;
|
||||
lastDash = i;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (lastDash === -1) return str;
|
||||
return str.substring(0, lastDash);
|
||||
}
|
||||
};
|
||||
const OptionalWindowTitleInstance = await OptionalWindowTitle();
|
||||
|
||||
export const ModuleLeftSpace = () => Widget.EventBox({
|
||||
onScrollUp: () => {
|
||||
@@ -65,29 +63,7 @@ export const ModuleLeftSpace = () => Widget.EventBox({
|
||||
vertical: true,
|
||||
className: 'bar-space-button',
|
||||
children: [
|
||||
Widget.Scrollable({
|
||||
hexpand: true, vexpand: true,
|
||||
hscroll: 'automatic', vscroll: 'never',
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
className: 'txt-smaller bar-topdesc txt',
|
||||
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}` : truncateTitle(Hyprland.active.client._title);
|
||||
}),
|
||||
})
|
||||
]
|
||||
})
|
||||
})
|
||||
OptionalWindowTitleInstance,
|
||||
]
|
||||
})]
|
||||
}),
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Gtk } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
import { ModuleLeftSpace } from "./leftspace.js";
|
||||
import { ModuleMusic } from "./music.js";
|
||||
import { ModuleRightSpace } from "./rightspace.js";
|
||||
import { ModuleSystem } from "./system.js";
|
||||
import ModuleWorkspaces from "./workspaces.js";
|
||||
import { RoundedCorner } from "../../lib/roundedcorner.js";
|
||||
const OptionalWorkspaces = async () => {
|
||||
try {
|
||||
return (await import('./workspaces_hyprland.js')).default();
|
||||
} catch {
|
||||
// return (await import('./workspaces_sway.js')).default();
|
||||
return Box({});
|
||||
}
|
||||
};
|
||||
|
||||
const left = Widget.Box({
|
||||
className: 'bar-sidemodule',
|
||||
@@ -18,7 +23,7 @@ const left = Widget.Box({
|
||||
|
||||
const center = Widget.Box({
|
||||
children: [
|
||||
ModuleWorkspaces(),
|
||||
await OptionalWorkspaces(),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { Service, Utils, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
||||
import { showMusicControls } from '../../variables.js';
|
||||
|
||||
function trimTrackTitle(title) {
|
||||
var cleanedTitle = title;
|
||||
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning
|
||||
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD]
|
||||
return cleanedTitle.trim();
|
||||
}
|
||||
|
||||
const TrackProgress = () => {
|
||||
const _updateProgress = (circprog) => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
@@ -15,19 +23,19 @@ const TrackProgress = () => {
|
||||
return AnimatedCircProg({
|
||||
className: 'bar-music-circprog',
|
||||
vpack: 'center', hpack: 'center',
|
||||
connections: [ // Update on change/once every 3 seconds
|
||||
[Mpris, _updateProgress],
|
||||
[3000, _updateProgress]
|
||||
]
|
||||
extraSetup: (self) => self
|
||||
.hook(Mpris, _updateProgress)
|
||||
.poll(3000, _updateProgress)
|
||||
,
|
||||
})
|
||||
}
|
||||
|
||||
export const ModuleMusic = () => Widget.EventBox({
|
||||
onScrollUp: () => execAsync('hyprctl dispatch workspace -1'),
|
||||
onScrollDown: () => execAsync('hyprctl dispatch workspace +1'),
|
||||
export const ModuleMusic = () => Widget.EventBox({ // TODO: use cairo to make button bounce smaller on click
|
||||
onScrollUp: () => Hyprland.sendMessage(`dispatch workspace -1`),
|
||||
onScrollDown: () => Hyprland.sendMessage(`dispatch workspace +1`),
|
||||
onPrimaryClickRelease: () => showMusicControls.setValue(!showMusicControls.value),
|
||||
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']),
|
||||
onMiddleClickRelease: () => Mpris.getPlayer('')?.playPause(),
|
||||
onMiddleClickRelease: () => execAsync('playerctl play-pause').catch(print),
|
||||
child: Widget.Box({
|
||||
className: 'bar-group-margin bar-sides',
|
||||
children: [
|
||||
@@ -45,17 +53,17 @@ export const ModuleMusic = () => Widget.EventBox({
|
||||
vpack: 'center',
|
||||
className: 'bar-music-playstate-txt',
|
||||
justification: 'center',
|
||||
connections: [[Mpris, label => {
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
label.label = `${mpris !== null && mpris.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
|
||||
}]],
|
||||
}),
|
||||
})],
|
||||
connections: [[Mpris, label => {
|
||||
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(),
|
||||
@@ -66,13 +74,13 @@ export const ModuleMusic = () => Widget.EventBox({
|
||||
hexpand: true,
|
||||
child: Widget.Label({
|
||||
className: 'txt-smallie txt-onSurfaceVariant',
|
||||
connections: [[Mpris, label => {
|
||||
setup: (self) => self.hook(Mpris, label => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
if (mpris)
|
||||
label.label = `${mpris.trackTitle} • ${mpris.trackArtists.join(', ')}`;
|
||||
label.label = `${trimTrackTitle(mpris.trackTitle)} • ${mpris.trackArtists.join(', ')}`;
|
||||
else
|
||||
label.label = 'No media';
|
||||
}]],
|
||||
}),
|
||||
})
|
||||
})
|
||||
]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { App, Utils, Widget } from '../../imports.js';
|
||||
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 Mpris from 'resource:///com/github/Aylur/ags/service/mpris.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";
|
||||
@@ -32,8 +35,8 @@ export const ModuleRightSpace = () => {
|
||||
// onHover: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', true) },
|
||||
// onHoverLost: () => { barStatusIcons.toggleClassName('bar-statusicons-hover', false) },
|
||||
onPrimaryClick: () => App.toggleWindow('sideright'),
|
||||
onSecondaryClickRelease: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` &']),
|
||||
onMiddleClickRelease: () => Mpris.getPlayer('')?.playPause(),
|
||||
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: [
|
||||
@@ -43,10 +46,29 @@ export const ModuleRightSpace = () => {
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
className: 'spacing-h-15 txt',
|
||||
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,
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// This is for the right pill of the bar.
|
||||
// For the cool memory indicator on the sidebar, see sysinfo.js
|
||||
import { Service, Utils, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { Box, Label, Button, Overlay, Revealer, Scrollable, Stack, EventBox } = Widget;
|
||||
const { exec, execAsync } = Utils;
|
||||
const { GLib } = imports.gi;
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
||||
@@ -20,15 +22,15 @@ const BatBatteryProgress = () => {
|
||||
return AnimatedCircProg({
|
||||
className: 'bar-batt-circprog',
|
||||
vpack: 'center', hpack: 'center',
|
||||
connections: [
|
||||
[Battery, _updateProgress],
|
||||
],
|
||||
extraSetup: (self) => self
|
||||
.hook(Battery, _updateProgress)
|
||||
,
|
||||
})
|
||||
}
|
||||
|
||||
const BarClock = () => Widget.Box({
|
||||
vpack: 'center',
|
||||
className: 'spacing-h-5 txt-onSurfaceVariant',
|
||||
className: 'spacing-h-5 txt-onSurfaceVariant bar-clock-box',
|
||||
children: [
|
||||
Widget.Label({
|
||||
className: 'bar-clock',
|
||||
@@ -59,75 +61,39 @@ const UtilButton = ({ name, icon, onClicked }) => Button({
|
||||
label: `${icon}`,
|
||||
})
|
||||
|
||||
const Utilities = () => Scrollable({
|
||||
hexpand: true,
|
||||
child: Box({
|
||||
hpack: 'center',
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
UtilButton({
|
||||
name: 'Screen snip', icon: 'screenshot_region', onClicked: () => {
|
||||
Utils.execAsync(['bash', '-c', `grim -g "$(slurp -d -c e2e2e2BB -b 31313122 -s 00000000)" - | wl-copy &`])
|
||||
.catch(print)
|
||||
}
|
||||
}),
|
||||
UtilButton({
|
||||
name: 'Color picker', icon: 'colorize', onClicked: () => {
|
||||
Utils.execAsync(['hyprpicker', '-a']).catch(print)
|
||||
}
|
||||
}),
|
||||
UtilButton({
|
||||
name: 'Toggle on-screen keyboard', icon: 'keyboard', onClicked: () => {
|
||||
App.toggleWindow('osk');
|
||||
}
|
||||
}),
|
||||
]
|
||||
})
|
||||
const Utilities = () => Box({
|
||||
hpack: 'center',
|
||||
className: 'spacing-h-5 txt-onSurfaceVariant',
|
||||
children: [
|
||||
UtilButton({
|
||||
name: 'Screen snip', icon: 'screenshot_region', onClicked: () => {
|
||||
Utils.execAsync(['bash', '-c', `grim -g "$(slurp -d -c e2e2e2BB -b 31313122 -s 00000000)" - | wl-copy &`])
|
||||
.catch(print)
|
||||
}
|
||||
}),
|
||||
UtilButton({
|
||||
name: 'Color picker', icon: 'colorize', onClicked: () => {
|
||||
Utils.execAsync(['hyprpicker', '-a']).catch(print)
|
||||
}
|
||||
}),
|
||||
UtilButton({
|
||||
name: 'Toggle on-screen keyboard', icon: 'keyboard', onClicked: () => {
|
||||
App.toggleWindow('osk');
|
||||
}
|
||||
}),
|
||||
]
|
||||
})
|
||||
|
||||
const BarBattery = () => Box({
|
||||
className: 'spacing-h-4 txt-onSurfaceVariant',
|
||||
children: [
|
||||
// Revealer({ // A dot for charging state
|
||||
// transitionDuration: 150,
|
||||
// revealChild: false,
|
||||
// transition: 'crossfade',
|
||||
// child: Widget.Box({
|
||||
// className: 'spacing-h-3',
|
||||
// children: [
|
||||
// Widget.Box({
|
||||
// vpack: 'center',
|
||||
// className: 'bar-batt-chargestate-charging-smaller',
|
||||
// setup: (self) => self.hook(Battery, box => {
|
||||
// box.toggleClassName('bar-batt-chargestate-low', Battery.percent <= BATTERY_LOW);
|
||||
// box.toggleClassName('bar-batt-chargestate-full', Battery.charged);
|
||||
// }),
|
||||
// }),
|
||||
// Widget.Box({
|
||||
// vpack: 'center',
|
||||
// className: 'bar-batt-chargestate-charging',
|
||||
// setup: (self) => self.hook(Battery, box => {
|
||||
// box.toggleClassName('bar-batt-chargestate-low', Battery.percent <= BATTERY_LOW);
|
||||
// box.toggleClassName('bar-batt-chargestate-full', Battery.charged);
|
||||
// }),
|
||||
// }),
|
||||
// ]
|
||||
// }),
|
||||
// setup: (self) => self.hook(Battery, revealer => {
|
||||
// revealer.revealChild = Battery.charging;
|
||||
// }),
|
||||
// }),
|
||||
Stack({
|
||||
transition: 'slide_up_down',
|
||||
items: [
|
||||
['discharging', Widget.Label({
|
||||
className: 'txt-norm txt',
|
||||
label: '•',
|
||||
}),],
|
||||
['charging', MaterialIcon('bolt', 'norm')],
|
||||
],
|
||||
Revealer({
|
||||
transitionDuration: 150,
|
||||
revealChild: false,
|
||||
transition: 'slide_right',
|
||||
child: MaterialIcon('bolt', 'norm'),
|
||||
setup: (self) => self.hook(Battery, revealer => {
|
||||
self.shown = Battery.charging ? 'charging' : 'discharging';
|
||||
self.revealChild = Battery.charging;
|
||||
}),
|
||||
}),
|
||||
Label({
|
||||
@@ -156,21 +122,101 @@ const BarBattery = () => Box({
|
||||
]
|
||||
});
|
||||
|
||||
const BarResourceValue = (name, icon, command) => Widget.Box({
|
||||
vpack: 'center',
|
||||
className: 'bar-batt spacing-h-5',
|
||||
children: [
|
||||
MaterialIcon(icon, 'small'),
|
||||
Widget.ProgressBar({ // Progress
|
||||
vpack: 'center', hexpand: true,
|
||||
className: 'bar-prog-batt',
|
||||
setup: (self) => self.poll(5000, (progress) => execAsync(['bash', '-c', command])
|
||||
.then((output) => {
|
||||
progress.value = Number(output) / 100;
|
||||
progress.tooltipText = `${name}: ${Number(output)}%`
|
||||
})
|
||||
.catch(print)
|
||||
),
|
||||
}),
|
||||
]
|
||||
});
|
||||
|
||||
const BarResource = (name, icon, command) => {
|
||||
const resourceLabel = Label({
|
||||
className: 'txt-smallie txt-onSurfaceVariant',
|
||||
});
|
||||
const resourceCircProg = AnimatedCircProg({
|
||||
className: 'bar-batt-circprog',
|
||||
vpack: 'center', hpack: 'center',
|
||||
});
|
||||
const widget = Box({
|
||||
className: 'spacing-h-4 txt-onSurfaceVariant',
|
||||
children: [
|
||||
resourceLabel,
|
||||
Overlay({
|
||||
child: Widget.Box({
|
||||
vpack: 'center',
|
||||
className: 'bar-batt',
|
||||
homogeneous: true,
|
||||
children: [
|
||||
MaterialIcon(icon, 'small'),
|
||||
],
|
||||
}),
|
||||
overlays: [resourceCircProg]
|
||||
}),
|
||||
],
|
||||
setup: (self) => self
|
||||
.poll(5000, () => execAsync(['bash', '-c', command])
|
||||
.then((output) => {
|
||||
resourceCircProg.css = `font-size: ${Number(output)}px;`;
|
||||
resourceLabel.label = `${Math.round(Number(output))}%`;
|
||||
widget.tooltipText = `${name}: ${Math.round(Number(output))}%`;
|
||||
}).catch(print))
|
||||
,
|
||||
});
|
||||
return widget;
|
||||
}
|
||||
|
||||
const BarGroup = ({ child }) => Widget.Box({
|
||||
className: 'bar-group-margin bar-sides',
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: 'bar-group bar-group-standalone bar-group-pad-system',
|
||||
children: [child],
|
||||
}),
|
||||
]
|
||||
});
|
||||
|
||||
export const ModuleSystem = () => Widget.EventBox({
|
||||
onScrollUp: () => execAsync('hyprctl dispatch workspace -1'),
|
||||
onScrollDown: () => execAsync('hyprctl dispatch workspace +1'),
|
||||
onScrollUp: () => Hyprland.sendMessage(`dispatch workspace -1`),
|
||||
onScrollDown: () => Hyprland.sendMessage(`dispatch workspace +1`),
|
||||
onPrimaryClick: () => App.toggleWindow('sideright'),
|
||||
child: Widget.Box({
|
||||
className: 'bar-group-margin bar-sides',
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: 'bar-group bar-group-standalone bar-group-pad-system spacing-h-5',
|
||||
children: [
|
||||
BarClock(),
|
||||
Utilities(),
|
||||
BarBattery(),
|
||||
BarGroup({ child: BarClock() }),
|
||||
Stack({
|
||||
transition: 'slide_up_down',
|
||||
transitionDuration: 150,
|
||||
items: [
|
||||
['laptop', Box({
|
||||
className: 'spacing-h-5', children: [
|
||||
BarGroup({ child: Utilities() }),
|
||||
BarGroup({ child: BarBattery() }),
|
||||
]
|
||||
})],
|
||||
['desktop', Box({
|
||||
className: 'spacing-h-5', children: [
|
||||
BarGroup({ child: BarResource('RAM usage', 'memory', `free | awk '/^Mem/ {printf("%.2f\\n", ($3/$2) * 100)}'`), }),
|
||||
BarGroup({ child: BarResource('Swap usage', 'swap_horiz', `free | awk '/^Swap/ {printf("%.2f\\n", ($3/$2) * 100)}'`), }),
|
||||
]
|
||||
})],
|
||||
],
|
||||
}),
|
||||
setup: (stack) => Utils.timeout(10, () => {
|
||||
if (!Battery.available) stack.shown = 'desktop';
|
||||
else stack.shown = 'laptop';
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,59 +1,63 @@
|
||||
const { GLib, Gdk, Gtk } = imports.gi;
|
||||
import { Service, Widget } from '../../imports.js';
|
||||
const { Gtk } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js';
|
||||
const { Box, Icon, Button, Revealer } = Widget;
|
||||
const { Gravity } = imports.gi.Gdk;
|
||||
|
||||
const revealerDuration = 200;
|
||||
|
||||
const SysTrayItem = item => Button({
|
||||
const SysTrayItem = (item) => Button({
|
||||
className: 'bar-systray-item',
|
||||
child: Icon({
|
||||
hpack: 'center',
|
||||
binds: [['icon', item, 'icon']],
|
||||
setup: (self) => Utils.timeout(1, () => {
|
||||
const styleContext = self.get_parent().get_style_context();
|
||||
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
self.size = Math.max(width, height, 1); // im too lazy to add another box lol
|
||||
}),
|
||||
setup: (self) => {
|
||||
self.hook(item, (self) => self.icon = item.icon);
|
||||
Utils.timeout(1, () => {
|
||||
const styleContext = self.get_parent().get_style_context();
|
||||
const width = styleContext.get_property('min-width', Gtk.StateFlags.NORMAL);
|
||||
const height = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL);
|
||||
self.size = Math.max(width, height, 1); // im too lazy to add another box lol
|
||||
})
|
||||
},
|
||||
}),
|
||||
binds: [['tooltipMarkup', item, 'tooltip-markup']],
|
||||
setup: (self) => self
|
||||
.hook(item, (self) => self.tooltipMarkup = item['tooltip-markup'])
|
||||
,
|
||||
onClicked: btn => item.menu.popup_at_widget(btn, Gravity.SOUTH, Gravity.NORTH, null),
|
||||
onSecondaryClick: btn => item.menu.popup_at_widget(btn, Gravity.SOUTH, Gravity.NORTH, null),
|
||||
});
|
||||
|
||||
export const Tray = (props = {}) => {
|
||||
const trayContent = Box({
|
||||
className: 'bar-systray spacing-h-10',
|
||||
properties: [
|
||||
['items', new Map()],
|
||||
['onAdded', (box, id) => {
|
||||
className: 'margin-right-5 spacing-h-15',
|
||||
attribute: {
|
||||
items: new Map(),
|
||||
onAdded: (box, id) => {
|
||||
const item = SystemTray.getItem(id);
|
||||
if (!item) return;
|
||||
item.menu.className = 'menu';
|
||||
if (box._items.has(id) || !item)
|
||||
if (box.attribute.items.has(id) || !item)
|
||||
return;
|
||||
const widget = SysTrayItem(item);
|
||||
box._items.set(id, widget);
|
||||
box.attribute.items.set(id, widget);
|
||||
box.add(widget);
|
||||
box.show_all();
|
||||
if (box._items.size === 1)
|
||||
if (box.attribute.items.size === 1)
|
||||
trayRevealer.revealChild = true;
|
||||
}],
|
||||
['onRemoved', (box, id) => {
|
||||
if (!box._items.has(id))
|
||||
},
|
||||
onRemoved: (box, id) => {
|
||||
if (!box.attribute.items.has(id))
|
||||
return;
|
||||
|
||||
box._items.get(id).destroy();
|
||||
box._items.delete(id);
|
||||
if (box._items.size === 0)
|
||||
box.attribute.items.get(id).destroy();
|
||||
box.attribute.items.delete(id);
|
||||
if (box.attribute.items.size === 0)
|
||||
trayRevealer.revealChild = false;
|
||||
}],
|
||||
],
|
||||
},
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(SystemTray, (box, id) => box._onAdded(box, id), 'added')
|
||||
.hook(SystemTray, (box, id) => box._onRemoved(box, id), 'removed')
|
||||
.hook(SystemTray, (box, id) => box.attribute.onAdded(box, id), 'added')
|
||||
.hook(SystemTray, (box, id) => box.attribute.onRemoved(box, id), 'removed')
|
||||
,
|
||||
});
|
||||
const trayRevealer = Widget.Revealer({
|
||||
|
||||
+30
-23
@@ -3,7 +3,8 @@ const Lang = imports.lang;
|
||||
const Cairo = imports.cairo;
|
||||
const Pango = imports.gi.Pango;
|
||||
const PangoCairo = imports.gi.PangoCairo;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Box, DrawingArea, EventBox } = Widget;
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
|
||||
@@ -15,15 +16,12 @@ const dummyOccupiedWs = Box({ className: 'bar-ws bar-ws-occupied' }); // Not sho
|
||||
// Font size = workspace id
|
||||
const WorkspaceContents = (count = 10) => {
|
||||
return DrawingArea({
|
||||
properties: [
|
||||
['workspaceMask', 0],
|
||||
],
|
||||
css: `transition: 500ms cubic-bezier(0.1, 1, 0, 1);`,
|
||||
setup: (area) => area
|
||||
.hook(Hyprland.active.workspace, (area) =>
|
||||
area.setCss(`font-size: ${Hyprland.active.workspace.id}px;`)
|
||||
)
|
||||
.hook(Hyprland, (area) => {
|
||||
css: `transition: 90ms cubic-bezier(0.1, 1, 0, 1);`,
|
||||
attribute: {
|
||||
initialized: false,
|
||||
workspaceMask: 0,
|
||||
updateMask: (self) => {
|
||||
if (self.attribute.initialized) return; // We only need this to run once
|
||||
const workspaces = Hyprland.workspaces;
|
||||
let workspaceMask = 0;
|
||||
for (let i = 0; i < workspaces.length; i++) {
|
||||
@@ -34,8 +32,21 @@ const WorkspaceContents = (count = 10) => {
|
||||
workspaceMask |= (1 << ws.id);
|
||||
}
|
||||
}
|
||||
area._workspaceMask = workspaceMask;
|
||||
}, 'notify::workspaces')
|
||||
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(Hyprland.active.workspace, (area) =>
|
||||
area.setCss(`font-size: ${Hyprland.active.workspace.id}px;`)
|
||||
)
|
||||
.hook(Hyprland, (self) => self.attribute.updateMask(self), 'notify::workspaces')
|
||||
.hook(Hyprland, (self, name) => self.attribute.toggleMask(self, true, name), 'workspace-added')
|
||||
.hook(Hyprland, (self, name) => self.attribute.toggleMask(self, false, name), 'workspace-removed')
|
||||
.on('draw', Lang.bind(area, (area, cr) => {
|
||||
const allocation = area.get_allocation();
|
||||
const { width, height } = allocation;
|
||||
@@ -75,12 +86,12 @@ const WorkspaceContents = (count = 10) => {
|
||||
|
||||
// Draw workspace numbers
|
||||
for (let i = 1; i <= count; i++) {
|
||||
if (area._workspaceMask & (1 << 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._workspaceMask & (1 << (i - 1)))) { // Left
|
||||
if (!(area.attribute.workspaceMask & (1 << (i - 1)))) { // Left
|
||||
cr.arc(wsCenterX, wsCenterY, workspaceRadius, 0.5 * Math.PI, 1.5 * Math.PI);
|
||||
cr.fill();
|
||||
}
|
||||
@@ -88,7 +99,7 @@ const WorkspaceContents = (count = 10) => {
|
||||
cr.rectangle(wsCenterX - workspaceRadius, wsCenterY - workspaceRadius, workspaceRadius, workspaceRadius * 2)
|
||||
cr.fill();
|
||||
}
|
||||
if (!(area._workspaceMask & (1 << (i + 1)))) { // Right
|
||||
if (!(area.attribute.workspaceMask & (1 << (i + 1)))) { // Right
|
||||
cr.arc(wsCenterX, wsCenterY, workspaceRadius, -0.5 * Math.PI, 0.5 * Math.PI);
|
||||
cr.fill();
|
||||
}
|
||||
@@ -131,9 +142,7 @@ export default () => EventBox({
|
||||
onScrollDown: () => Hyprland.sendMessage(`dispatch workspace +1`),
|
||||
onMiddleClickRelease: () => App.toggleWindow('overview'),
|
||||
onSecondaryClickRelease: () => App.toggleWindow('osk'),
|
||||
properties: [
|
||||
['clicked', false],
|
||||
],
|
||||
attribute: { clicked: false },
|
||||
child: Box({
|
||||
homogeneous: true,
|
||||
className: 'bar-group-margin',
|
||||
@@ -148,8 +157,7 @@ export default () => EventBox({
|
||||
setup: (self) => {
|
||||
self.add_events(Gdk.EventMask.POINTER_MOTION_MASK);
|
||||
self.on('motion-notify-event', (self, event) => {
|
||||
if (!self._clicked) return;
|
||||
console.log('switching move');
|
||||
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);
|
||||
@@ -157,13 +165,12 @@ export default () => EventBox({
|
||||
})
|
||||
self.on('button-press-event', (self, event) => {
|
||||
if (!(event.get_button()[1] === 1)) return; // We're only interested in left-click here
|
||||
console.log('switching');
|
||||
self._clicked = true;
|
||||
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);
|
||||
Hyprland.sendMessage(`dispatch workspace ${wsId}`);
|
||||
})
|
||||
self.on('button-release-event', (self) => self._clicked = false);
|
||||
self.on('button-release-event', (self) => self.attribute.clicked = false);
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,58 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Sway from "../../services/sway.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import options from "../../options.js";
|
||||
import { range } from "../../utils.js";
|
||||
|
||||
|
||||
const dispatch = (arg) => Utils.execAsync(`swaymsg workspace ${arg}`);
|
||||
|
||||
const Workspaces = () => {
|
||||
const ws = options.workspaces.value || 20;
|
||||
return Widget.Box({
|
||||
children: range(ws).map((i) =>
|
||||
Widget.Button({
|
||||
setup: (btn) => (btn.id = i),
|
||||
on_clicked: () => dispatch(i),
|
||||
child: Widget.Label({
|
||||
label: `${i}`,
|
||||
class_name: "indicator",
|
||||
vpack: "center",
|
||||
}),
|
||||
setup: (self) => self.hook(Sway, (btn) => {
|
||||
btn.toggleClassName("active", Sway.active.workspace.name == i);
|
||||
btn.toggleClassName(
|
||||
"occupied",
|
||||
Sway.getWorkspace(`${i}`)?.nodes.length > 0,
|
||||
);
|
||||
}),
|
||||
})
|
||||
),
|
||||
setup: (self) => self.hook(Sway.active.workspace,
|
||||
(box) => box.children.map((btn) => {
|
||||
btn.visible = Sway.workspaces.some(
|
||||
(ws) => ws.name == btn.id,
|
||||
);
|
||||
})
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
export default () => Widget.EventBox({
|
||||
class_name: "workspaces panel-button",
|
||||
child: Widget.Box({
|
||||
// its nested like this to keep it consistent with other PanelButton widgets
|
||||
child: Widget.EventBox({
|
||||
on_scroll_up: () => dispatch("next"),
|
||||
on_scroll_down: () => dispatch("prev"),
|
||||
class_name: "eventbox",
|
||||
// binds: [["child", options.workspaces, "value", Workspaces]],
|
||||
setup: (self) => self
|
||||
.hook(options.workspaces, (self) => Selection.child = Workspaces(), "value")
|
||||
,
|
||||
}),
|
||||
}),
|
||||
setup: (self) => {
|
||||
console.log('[LOG] Sway workspace module loaded')
|
||||
}
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { keybindList } from "../../data/keybinds.js";
|
||||
|
||||
export const Keybinds = () => Widget.Box({
|
||||
@@ -38,7 +38,7 @@ export const Keybinds = () => Widget.Box({
|
||||
children: category.binds.map((keybinds, i) => Widget.Box({ // Binds
|
||||
vertical: false,
|
||||
children: keybinds.keys.map((key, i) => Widget.Label({ // Specific keys
|
||||
className: `${key == 'OR' || key == '+' ? 'cheatsheet-key-notkey' : 'cheatsheet-key'} txt-small`,
|
||||
className: `${['OR', '+'].includes(key) ? 'cheatsheet-key-notkey' : 'cheatsheet-key'} txt-small`,
|
||||
label: key,
|
||||
}))
|
||||
}))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { Service, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
||||
import { Keybinds } from "./keybinds.js";
|
||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.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 TimeAndLaunchesWidget from './timeandlaunches.js'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, EventBox, Label, Revealer, Overlay } = Widget;
|
||||
import { AnimatedCircProg } from '../../lib/animatedcircularprogress.js'
|
||||
@@ -24,7 +25,9 @@ const ResourceValue = (name, icon, interval, valueUpdateCmd, displayFunc, props
|
||||
Label({
|
||||
xalign: 1,
|
||||
className: 'titlefont txt-norm txt-onSecondaryContainer',
|
||||
connections: [[interval, (label) => displayFunc(label)]]
|
||||
setup: (self) => self
|
||||
.poll(interval, (label) => displayFunc(label))
|
||||
,
|
||||
})
|
||||
]
|
||||
})
|
||||
@@ -32,11 +35,13 @@ const ResourceValue = (name, icon, interval, valueUpdateCmd, displayFunc, props
|
||||
Overlay({
|
||||
child: AnimatedCircProg({
|
||||
className: 'bg-system-circprog',
|
||||
connections: [[interval, (self) => {
|
||||
execAsync(['bash', '-c', `${valueUpdateCmd}`]).then((newValue) => {
|
||||
self.css = `font-size: ${Math.round(newValue)}px;`
|
||||
}).catch(print);
|
||||
}]]
|
||||
extraSetup: (self) => self
|
||||
.poll(interval, (self) => {
|
||||
execAsync(['bash', '-c', `${valueUpdateCmd}`]).then((newValue) => {
|
||||
self.css = `font-size: ${Math.round(newValue)}px;`
|
||||
}).catch(print);
|
||||
})
|
||||
,
|
||||
}),
|
||||
overlays: [
|
||||
MaterialIcon(`${icon}`, 'hugeass'),
|
||||
@@ -143,7 +148,7 @@ export default () => Box({
|
||||
const firstChild = child.get_children()[0];
|
||||
firstChild.revealChild = !firstChild.revealChild;
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
})
|
||||
],
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
const { GLib, Gio } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { GLib } = imports.gi;
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, Label, Button, Revealer, EventBox } = Widget;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||
const { Gtk } = imports.gi;
|
||||
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { EventBox } = Widget;
|
||||
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, EventBox, Label, Revealer, Overlay } = Widget;
|
||||
import { AnimatedCircProg } from '../../lib/animatedcircularprogress.js'
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
import { setupCursorHover, setupCursorHoverAim } from "../../lib/cursorhover.js";
|
||||
const { Box, Revealer } = Widget;
|
||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||
|
||||
const ANIMATION_TIME = 150;
|
||||
const pinnedApps = [
|
||||
@@ -41,9 +43,9 @@ const DockSeparator = (props = {}) => Box({
|
||||
})
|
||||
|
||||
const AppButton = ({ icon, ...rest }) => Widget.Revealer({
|
||||
properties: [
|
||||
['workspace', 0],
|
||||
],
|
||||
attribute: {
|
||||
'workspace': 0
|
||||
},
|
||||
revealChild: false,
|
||||
transition: 'slide_right',
|
||||
transitionDuration: ANIMATION_TIME,
|
||||
@@ -80,12 +82,12 @@ const AppButton = ({ icon, ...rest }) => Widget.Revealer({
|
||||
|
||||
const Taskbar = () => Widget.Box({
|
||||
className: 'dock-apps',
|
||||
properties: [
|
||||
['map', new Map()],
|
||||
['clientSortFunc', (a, b) => {
|
||||
return a._workspace > b._workspace;
|
||||
}],
|
||||
['update', (box) => {
|
||||
attribute: {
|
||||
'map': new Map(),
|
||||
'clientSortFunc': (a, b) => {
|
||||
return a.attribute.workspace > b.attribute.workspace;
|
||||
},
|
||||
'update': (box) => {
|
||||
for (let i = 0; i < Hyprland.clients.length; i++) {
|
||||
const client = Hyprland.clients[i];
|
||||
if (client["pid"] == -1) return;
|
||||
@@ -99,15 +101,15 @@ const Taskbar = () => Widget.Box({
|
||||
tooltipText: `${client.title} (${appClass})`,
|
||||
onClicked: () => focus(client),
|
||||
});
|
||||
newButton._workspace = client.workspace.id;
|
||||
newButton.attribute.workspace = client.workspace.id;
|
||||
newButton.revealChild = true;
|
||||
box._map.set(client.address, newButton);
|
||||
box.attribute.map.set(client.address, newButton);
|
||||
}
|
||||
box.children = Array.from(box._map.values());
|
||||
}],
|
||||
['add', (box, address) => {
|
||||
box.children = Array.from(box.attribute.map.values());
|
||||
},
|
||||
'add': (box, address) => {
|
||||
if (!address) { // First active emit is undefined
|
||||
box._update(box);
|
||||
box.attribute.update(box);
|
||||
return;
|
||||
}
|
||||
const newClient = Hyprland.clients.find(client => {
|
||||
@@ -120,28 +122,29 @@ const Taskbar = () => Widget.Box({
|
||||
tooltipText: `${newClient.title} (${appClass})`,
|
||||
onClicked: () => focus(newClient),
|
||||
})
|
||||
newButton._workspace = newClient.workspace.id;
|
||||
box._map.set(address, newButton);
|
||||
box.children = Array.from(box._map.values());
|
||||
newButton.attribute.workspace = newClient.workspace.id;
|
||||
box.attribute.map.set(address, newButton);
|
||||
box.children = Array.from(box.attribute.map.values());
|
||||
newButton.revealChild = true;
|
||||
}],
|
||||
['remove', (box, address) => {
|
||||
},
|
||||
'remove': (box, address) => {
|
||||
if (!address) return;
|
||||
|
||||
const removedButton = box._map.get(address);
|
||||
const removedButton = box.attribute.map.get(address);
|
||||
if (!removedButton) return;
|
||||
removedButton.revealChild = false;
|
||||
|
||||
Utils.timeout(ANIMATION_TIME, () => {
|
||||
removedButton.destroy();
|
||||
box._map.delete(address);
|
||||
box.children = Array.from(box._map.values());
|
||||
box.attribute.map.delete(address);
|
||||
box.children = Array.from(box.attribute.map.values());
|
||||
})
|
||||
}],
|
||||
],
|
||||
},
|
||||
},
|
||||
setup: (self) => {
|
||||
self.hook(Hyprland, (box, address) => box._add(box, address), 'client-added')
|
||||
.hook(Hyprland, (box, address) => box._remove(box, address), 'client-removed')
|
||||
Utils.timeout(100, () => self._update(self));
|
||||
self.hook(Hyprland, (box, address) => box.attribute.add(box, address), 'client-added')
|
||||
.hook(Hyprland, (box, address) => box.attribute.remove(box, address), 'client-removed')
|
||||
Utils.timeout(100, () => self.attribute.update(self));
|
||||
},
|
||||
});
|
||||
|
||||
@@ -191,8 +194,8 @@ export default () => {
|
||||
]
|
||||
})
|
||||
const dockRevealer = Revealer({
|
||||
properties: [
|
||||
['updateShow', self => { // I only use mouse to resize. I don't care about keyboard resize if that's a thing
|
||||
attribute: {
|
||||
'updateShow': self => { // I only use mouse to resize. I don't care about keyboard resize if that's a thing
|
||||
const dockSize = [
|
||||
dockContent.get_allocated_width(),
|
||||
dockContent.get_allocated_height()
|
||||
@@ -229,18 +232,18 @@ export default () => {
|
||||
}
|
||||
}
|
||||
self.revealChild = true;
|
||||
}]
|
||||
],
|
||||
}
|
||||
},
|
||||
revealChild: false,
|
||||
transition: 'slide_up',
|
||||
transitionDuration: 200,
|
||||
child: dockContent,
|
||||
// setup: (self) => self
|
||||
// .hook(Hyprland, (self) => self._updateShow(self))
|
||||
// .hook(Hyprland.active.workspace, (self) => self._updateShow(self))
|
||||
// .hook(Hyprland.active.client, (self) => self._updateShow(self))
|
||||
// .hook(Hyprland, (self) => self._updateShow(self), 'client-added')
|
||||
// .hook(Hyprland, (self) => self._updateShow(self), 'client-removed')
|
||||
// .hook(Hyprland, (self) => self.attribute.updateShow(self))
|
||||
// .hook(Hyprland.active.workspace, (self) => self.attribute.updateShow(self))
|
||||
// .hook(Hyprland.active.client, (self) => self.attribute.updateShow(self))
|
||||
// .hook(Hyprland, (self) => self.attribute.updateShow(self), 'client-added')
|
||||
// .hook(Hyprland, (self) => self.attribute.updateShow(self), 'client-removed')
|
||||
// ,
|
||||
})
|
||||
return EventBox({
|
||||
@@ -248,7 +251,7 @@ export default () => {
|
||||
dockRevealer.revealChild = true;
|
||||
},
|
||||
onHoverLost: () => {
|
||||
if (Hyprland.active.client._class.length === 0) return;
|
||||
if (Hyprland.active.client.attribute.class.length === 0) return;
|
||||
dockRevealer.revealChild = false;
|
||||
},
|
||||
child: Box({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { App, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Dock from './dock.js';
|
||||
|
||||
export default () => Widget.Window({
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
const { Gio, GLib, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { exec, execAsync } = Utils;
|
||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
|
||||
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
import { showColorScheme } from '../../variables.js';
|
||||
|
||||
const ColorBox = ({
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// This file is for brightness/volume indicators
|
||||
const { GLib, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
||||
const { Box, Label, ProgressBar, Revealer } = Widget;
|
||||
import { MarginRevealer } from '../../lib/advancedrevealers.js';
|
||||
import { MarginRevealer } from '../../lib/advancedwidgets.js';
|
||||
import Brightness from '../../services/brightness.js';
|
||||
import Indicator from '../../services/indicator.js';
|
||||
|
||||
const OsdValue = (name, labelConnections, progressConnections, props = {}) => Box({ // Volume
|
||||
const OsdValue = (name, labelSetup, progressSetup, props = {}) => Box({ // Volume
|
||||
...props,
|
||||
vertical: true,
|
||||
className: 'osd-bg osd-value',
|
||||
@@ -23,8 +22,7 @@ const OsdValue = (name, labelConnections, progressConnections, props = {}) => Bo
|
||||
}),
|
||||
Label({
|
||||
hexpand: false, className: 'osd-value-txt',
|
||||
label: '100',
|
||||
connections: labelConnections,
|
||||
setup: labelSetup,
|
||||
}),
|
||||
]
|
||||
}),
|
||||
@@ -32,41 +30,49 @@ const OsdValue = (name, labelConnections, progressConnections, props = {}) => Bo
|
||||
className: 'osd-progress',
|
||||
hexpand: true,
|
||||
vertical: false,
|
||||
connections: progressConnections,
|
||||
setup: progressSetup,
|
||||
})
|
||||
],
|
||||
});
|
||||
|
||||
const brightnessIndicator = OsdValue('Brightness',
|
||||
[[Brightness, self => {
|
||||
self.label = `${Math.round(Brightness.screen_value * 100)}`;
|
||||
}, 'notify::screen-value']],
|
||||
[[Brightness, (progress) => {
|
||||
const updateValue = Brightness.screen_value;
|
||||
progress.value = updateValue;
|
||||
}, 'notify::screen-value']],
|
||||
(self) => self
|
||||
.hook(Brightness, self => {
|
||||
self.label = `${Math.round(Brightness.screen_value * 100)}`;
|
||||
}, 'notify::screen-value')
|
||||
,
|
||||
(self) => self
|
||||
.hook(Brightness, (progress) => {
|
||||
const updateValue = Brightness.screen_value;
|
||||
progress.value = updateValue;
|
||||
}, 'notify::screen-value')
|
||||
,
|
||||
)
|
||||
|
||||
const volumeIndicator = OsdValue('Volume',
|
||||
[[Audio, (label) => {
|
||||
label.label = `${Math.round(Audio.speaker?.volume * 100)}`;
|
||||
}]],
|
||||
[[Audio, (progress) => {
|
||||
const updateValue = Audio.speaker?.volume;
|
||||
if (!isNaN(updateValue)) progress.value = updateValue;
|
||||
}]],
|
||||
(self) => self
|
||||
.hook(Audio, (label) => {
|
||||
label.label = `${Math.round(Audio.speaker?.volume * 100)}`;
|
||||
})
|
||||
,
|
||||
(self) => self
|
||||
.hook(Audio, (progress) => {
|
||||
const updateValue = Audio.speaker?.volume;
|
||||
if (!isNaN(updateValue)) progress.value = updateValue;
|
||||
})
|
||||
,
|
||||
);
|
||||
|
||||
export default () => MarginRevealer({
|
||||
transition: 'slide_down',
|
||||
showClass: 'osd-show',
|
||||
hideClass: 'osd-hide',
|
||||
connections: [
|
||||
[Indicator, (revealer, value) => {
|
||||
if(value > -1) revealer._show(revealer);
|
||||
else revealer._hide(revealer);
|
||||
}, 'popup'],
|
||||
],
|
||||
extraSetup: (self) => self
|
||||
.hook(Indicator, (revealer, value) => {
|
||||
if (value > -1) revealer.attribute.show();
|
||||
else revealer.attribute.hide();
|
||||
}, 'popup')
|
||||
,
|
||||
child: Box({
|
||||
hpack: 'center',
|
||||
vertical: false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Indicator from '../../services/indicator.js';
|
||||
import IndicatorValues from './indicatorvalues.js';
|
||||
import MusicControls from './musiccontrols.js';
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
const { Gio, GLib, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { Gio, GLib } = imports.gi;
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { exec, execAsync } = Utils;
|
||||
import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
|
||||
|
||||
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
|
||||
import { MarginRevealer } from '../../lib/advancedrevealers.js';
|
||||
import { MarginRevealer } from '../../lib/advancedwidgets.js';
|
||||
import { AnimatedCircProg } from "../../lib/animatedcircularprogress.js";
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
import { showMusicControls } from '../../variables.js';
|
||||
@@ -17,7 +19,7 @@ function expandTilde(path) {
|
||||
}
|
||||
}
|
||||
|
||||
const LIGHTDARK_FILE_LOCATION = '~/.cache/ags/user/colormode.txt'
|
||||
const LIGHTDARK_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/colormode.txt`;
|
||||
const lightDark = Utils.readFile(expandTilde(LIGHTDARK_FILE_LOCATION)).trim();
|
||||
const COVER_COLORSCHEME_SUFFIX = '_colorscheme.css';
|
||||
const PREFERRED_PLAYER = 'plasma-browser-integration';
|
||||
@@ -74,6 +76,12 @@ function getTrackfont(player) {
|
||||
if (title.includes('東方')) return 'Crimson Text, serif'; // Serif for Touhou stuff
|
||||
return DEFAULT_MUSIC_FONT;
|
||||
}
|
||||
function trimTrackTitle(title) {
|
||||
var cleanedTitle = title;
|
||||
cleanedTitle = cleanedTitle.replace(/【[^】]*】/, ''); // Remove stuff like【C93】 at beginning
|
||||
cleanedTitle = cleanedTitle.replace(/\[FREE DOWNLOAD\]/g, ''); // Remove F-777's [FREE DOWNLOAD]
|
||||
return cleanedTitle.trim();
|
||||
}
|
||||
|
||||
const TrackProgress = ({ player, ...rest }) => {
|
||||
const _updateProgress = (circprog) => {
|
||||
@@ -86,10 +94,10 @@ const TrackProgress = ({ player, ...rest }) => {
|
||||
...rest,
|
||||
className: 'osd-music-circprog',
|
||||
vpack: 'center',
|
||||
connections: [ // Update on change/once every 3 seconds
|
||||
[Mpris, _updateProgress],
|
||||
[3000, _updateProgress]
|
||||
],
|
||||
extraSetup: (self) => self
|
||||
.hook(Mpris, _updateProgress)
|
||||
.poll(3000, _updateProgress)
|
||||
,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,13 +108,13 @@ const TrackTitle = ({ player, ...rest }) => Label({
|
||||
truncate: 'end',
|
||||
// wrap: true,
|
||||
className: 'osd-music-title',
|
||||
connections: [[player, (self) => {
|
||||
setup: (self) => self.hook(player, (self) => {
|
||||
// Player name
|
||||
self.label = player.trackTitle.length > 0 ? player.trackTitle : 'No media';
|
||||
self.label = player.trackTitle.length > 0 ? trimTrackTitle(player.trackTitle) : 'No media';
|
||||
// Font based on track/artist
|
||||
const fontForThisTrack = getTrackfont(player);
|
||||
self.css = `font-family: ${fontForThisTrack}, ${DEFAULT_MUSIC_FONT};`;
|
||||
}, 'notify::track-title']]
|
||||
}, 'notify::track-title'),
|
||||
});
|
||||
|
||||
const TrackArtists = ({ player, ...rest }) => Label({
|
||||
@@ -114,9 +122,9 @@ const TrackArtists = ({ player, ...rest }) => Label({
|
||||
xalign: 0,
|
||||
className: 'osd-music-artists',
|
||||
truncate: 'end',
|
||||
connections: [[player, (self) => {
|
||||
setup: (self) => self.hook(player, (self) => {
|
||||
self.label = player.trackArtists.length > 0 ? player.trackArtists.join(', ') : '';
|
||||
}, 'notify::track-artists']]
|
||||
}, 'notify::track-artists'),
|
||||
})
|
||||
|
||||
const CoverArt = ({ player, ...rest }) => Box({
|
||||
@@ -134,8 +142,8 @@ const CoverArt = ({ player, ...rest }) => Box({
|
||||
}),
|
||||
overlays: [ // Real
|
||||
Box({
|
||||
properties: [
|
||||
['updateCover', (self) => {
|
||||
attribute: {
|
||||
'updateCover': (self) => {
|
||||
const player = Mpris.getPlayer();
|
||||
|
||||
// Player closed
|
||||
@@ -165,17 +173,17 @@ const CoverArt = ({ player, ...rest }) => Box({
|
||||
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' > ${App.configDir}/scss/_musicmaterial.scss ${lightDark}`])
|
||||
.then(() => {
|
||||
exec(`wal -i "${player.coverPath}" -n -t -s -e -q ${lightDark}`)
|
||||
exec(`bash -c "cp ~/.cache/wal/colors.scss ${App.configDir}/scss/_musicwal.scss"`)
|
||||
exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${App.configDir}/scss/_musicwal.scss`);
|
||||
exec(`sassc ${App.configDir}/scss/_music.scss ${stylePath}`);
|
||||
self.css = `background-image: url('${coverPath}');`;
|
||||
App.applyCss(`${stylePath}`);
|
||||
})
|
||||
.catch(print);
|
||||
}],
|
||||
],
|
||||
},
|
||||
},
|
||||
className: 'osd-music-cover-art',
|
||||
connections: [
|
||||
[player, (self) => self._updateCover(self), 'notify::cover-path']
|
||||
$: [
|
||||
[player, (self) => self.attribute.updateCover(self), 'notify::cover-path']
|
||||
],
|
||||
})
|
||||
]
|
||||
@@ -194,6 +202,7 @@ const TrackControls = ({ player, ...rest }) => Widget.Revealer({
|
||||
children: [
|
||||
Button({
|
||||
className: 'osd-music-controlbtn',
|
||||
onClicked: () => execAsync('playerctl previous').catch(print),
|
||||
child: Label({
|
||||
className: 'icon-material osd-music-controlbtn-txt',
|
||||
label: 'skip_previous',
|
||||
@@ -201,6 +210,9 @@ const TrackControls = ({ player, ...rest }) => Widget.Revealer({
|
||||
}),
|
||||
Button({
|
||||
className: 'osd-music-controlbtn',
|
||||
onClicked: () => execAsync(['bash', '-c', 'playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"`'])
|
||||
.catch(print)
|
||||
,
|
||||
child: Label({
|
||||
className: 'icon-material osd-music-controlbtn-txt',
|
||||
label: 'skip_next',
|
||||
@@ -208,13 +220,13 @@ const TrackControls = ({ player, ...rest }) => Widget.Revealer({
|
||||
}),
|
||||
],
|
||||
}),
|
||||
connections: [[Mpris, (self) => {
|
||||
setup: (self) => szelf.hook(Mpris, (self) => {
|
||||
const player = Mpris.getPlayer();
|
||||
if (!player)
|
||||
self.revealChild = false;
|
||||
else
|
||||
self.revealChild = true;
|
||||
}, 'notify::play-back-status']]
|
||||
}, 'notify::play-back-status'),
|
||||
});
|
||||
|
||||
const TrackSource = ({ player, ...rest }) => Widget.Revealer({
|
||||
@@ -230,19 +242,19 @@ const TrackSource = ({ player, ...rest }) => Widget.Revealer({
|
||||
hpack: 'fill',
|
||||
justification: 'center',
|
||||
className: 'icon-nerd',
|
||||
connections: [[player, (self) => {
|
||||
setup: (self) => self.hook(player, (self) => {
|
||||
self.label = detectMediaSource(player.trackCoverUrl);
|
||||
}, 'notify::cover-path']]
|
||||
}, 'notify::cover-path'),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
connections: [[Mpris, (self) => {
|
||||
setup: (self) => self.hook(Mpris, (self) => {
|
||||
const mpris = Mpris.getPlayer('');
|
||||
if (!mpris)
|
||||
self.revealChild = false;
|
||||
else
|
||||
self.revealChild = true;
|
||||
}]]
|
||||
}),
|
||||
});
|
||||
|
||||
const TrackTime = ({ player, ...rest }) => {
|
||||
@@ -256,28 +268,26 @@ const TrackTime = ({ player, ...rest }) => {
|
||||
className: 'osd-music-pill spacing-h-5',
|
||||
children: [
|
||||
Label({
|
||||
connections: [[1000, (self) => {
|
||||
setup: (self) => self.poll(1000, (self) => {
|
||||
const player = Mpris.getPlayer();
|
||||
if (!player) return;
|
||||
self.label = lengthStr(player.position);
|
||||
}]]
|
||||
}),
|
||||
}),
|
||||
Label({ label: '/' }),
|
||||
Label({
|
||||
connections: [[Mpris, (self) => {
|
||||
setup: (self) => self.hook(Mpris, (self) => {
|
||||
const player = Mpris.getPlayer();
|
||||
if (!player) return;
|
||||
self.label = lengthStr(player.length);
|
||||
}]]
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
connections: [[Mpris, (self) => {
|
||||
if (!player)
|
||||
self.revealChild = false;
|
||||
else
|
||||
self.revealChild = true;
|
||||
}]]
|
||||
setup: (self) => self.hook(Mpris, (self) => {
|
||||
if (!player) self.revealChild = false;
|
||||
else self.revealChild = true;
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -291,22 +301,17 @@ const PlayState = ({ player }) => {
|
||||
overlays: [
|
||||
Widget.Button({
|
||||
className: 'osd-music-playstate-btn',
|
||||
onClicked: () => {
|
||||
Mpris.getPlayer().playPause()
|
||||
},
|
||||
onClicked: () => execAsync('playerctl play-pause').catch(print),
|
||||
child: Widget.Label({
|
||||
justification: 'center',
|
||||
hpack: 'fill',
|
||||
vpack: 'center',
|
||||
connections: [[player, (label) => {
|
||||
setup: (self) => self.hook(player, (label) => {
|
||||
label.label = `${player.playBackStatus == 'Playing' ? 'pause' : 'play_arrow'}`;
|
||||
}, 'notify::play-back-status']],
|
||||
}, 'notify::play-back-status'),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
// setup: self => Utils.timeout(1, () => {
|
||||
// self.set_overlay_pass_through(self.get_children()[1], true);
|
||||
// }),
|
||||
passThrough: true,
|
||||
})
|
||||
});
|
||||
@@ -350,19 +355,17 @@ export default () => MarginRevealer({
|
||||
showClass: 'osd-show',
|
||||
hideClass: 'osd-hide',
|
||||
child: Box({
|
||||
connections: [[Mpris, box => {
|
||||
setup: (self) => self.hook(Mpris, box => {
|
||||
let foundPlayer = false;
|
||||
|
||||
Mpris.players.forEach((player, i) => {
|
||||
if (isRealPlayer(player)) {
|
||||
foundPlayer = true;
|
||||
box._player = player;
|
||||
box.children = [MusicControlsWidget(player)];
|
||||
}
|
||||
});
|
||||
|
||||
if (!foundPlayer) {
|
||||
box._player = null;
|
||||
const children = box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
@@ -370,12 +373,10 @@ export default () => MarginRevealer({
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, 'notify::players']],
|
||||
}, 'notify::players'),
|
||||
}),
|
||||
setup: (self) => self.hook(showMusicControls, (revealer) => {
|
||||
if (showMusicControls.value) revealer.attribute.show();
|
||||
else revealer.attribute.hide();
|
||||
}),
|
||||
connections: [
|
||||
[showMusicControls, (revealer) => {
|
||||
if(showMusicControls.value) revealer._show(revealer);
|
||||
else revealer._hide(revealer);
|
||||
}],
|
||||
],
|
||||
})
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// This file is for popup notifications
|
||||
const { GLib, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
||||
const { Box, EventBox, Icon, Scrollable, Label, Button, Revealer } = Widget;
|
||||
const { Box } = Widget;
|
||||
import Notification from '../../lib/notification.js';
|
||||
|
||||
const PopupNotification = (notifObject) => Widget.Box({
|
||||
@@ -39,41 +37,39 @@ const naiveNotifPopupList = Widget.Box({
|
||||
const notifPopupList = Box({
|
||||
vertical: true,
|
||||
className: 'osd-notifs spacing-v-5-revealer',
|
||||
properties: [
|
||||
['map', new Map()],
|
||||
|
||||
['dismiss', (box, id, force = false) => {
|
||||
if (!id || !box._map.has(id) || box._map.get(id)._hovered && !force)
|
||||
attribute: {
|
||||
'map': new Map(),
|
||||
'dismiss': (box, id, force = false) => {
|
||||
if (!id || !box.attribute.map.has(id) || box.attribute.map.get(id).attribute.hovered && !force)
|
||||
return;
|
||||
|
||||
const notif = box._map.get(id);
|
||||
const notif = box.attribute.map.get(id);
|
||||
notif.revealChild = false;
|
||||
notif._destroyWithAnims();
|
||||
}],
|
||||
|
||||
['notify', (box, id) => {
|
||||
// console.log('new notiffy', id, Notifications.getNotification(id))
|
||||
notif.attribute.destroyWithAnims();
|
||||
box.attribute.map.delete(id);
|
||||
},
|
||||
'notify': (box, id) => {
|
||||
if (!id || Notifications.dnd) return;
|
||||
if (!Notifications.getNotification(id)) return;
|
||||
|
||||
box._map.delete(id);
|
||||
box.attribute.map.delete(id);
|
||||
|
||||
const notif = Notifications.getNotification(id);
|
||||
const newNotif = Notification({
|
||||
notifObject: notif,
|
||||
isPopup: true,
|
||||
});
|
||||
box._map.set(id, newNotif);
|
||||
box.pack_end(box._map.get(id), false, false, 0);
|
||||
box.attribute.map.set(id, newNotif);
|
||||
box.pack_end(box.attribute.map.get(id), false, false, 0);
|
||||
box.show_all();
|
||||
|
||||
// box.children = Array.from(box._map.values()).reverse();
|
||||
}],
|
||||
],
|
||||
// box.children = Array.from(box.attribute.map.values()).reverse();
|
||||
},
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(Notifications, (box, id) => box._notify(box, id), 'notified')
|
||||
.hook(Notifications, (box, id) => box._dismiss(box, id), 'dismissed')
|
||||
.hook(Notifications, (box, id) => box._dismiss(box, id, true), 'closed')
|
||||
.hook(Notifications, (box, id) => box.attribute.notify(box, id), 'notified')
|
||||
.hook(Notifications, (box, id) => box.attribute.dismiss(box, id), 'dismissed')
|
||||
.hook(Notifications, (box, id) => box.attribute.dismiss(box, id, true), 'closed')
|
||||
,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
const { GLib, Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { Gtk } = imports.gi;
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
const { Box, EventBox, Button, Revealer } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
@@ -83,7 +87,7 @@ const keyboardItself = (kbJson) => {
|
||||
children: row.map(key => {
|
||||
return Button({
|
||||
className: `osk-key osk-key-${key.shape}`,
|
||||
hexpand: (key.shape == "space" || key.shape == "expand"),
|
||||
hexpand: ["space", "expand"].includes(key.shape),
|
||||
label: key.label,
|
||||
setup: (button) => {
|
||||
let pressed = false;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const { Gio, GLib } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
|
||||
function moveClientToWorkspace(address, workspace) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Widget } from '../../imports.js';
|
||||
import { SearchAndWindows } from "./overview.js";
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { SearchAndWindows } from "./windowcontent.js";
|
||||
|
||||
export default () => Widget.Window({
|
||||
name: 'overview',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const { Gio, GLib } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import Todo from "../../services/todo.js";
|
||||
|
||||
@@ -21,12 +22,12 @@ export function launchCustomCommand(command) {
|
||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/switchcolor.sh`, `&`]).catch(print);
|
||||
}
|
||||
else if (args[0] == '>light') { // Light mode
|
||||
execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "-l" > ~/.cache/ags/user/colormode.txt`])
|
||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "-l" > ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`])
|
||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
|
||||
.catch(print);
|
||||
}
|
||||
else if (args[0] == '>dark') { // Dark mode
|
||||
execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "" > ~/.cache/ags/user/colormode.txt`])
|
||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "" > ${GLib.get_user_cache_dir()}/ags/user/colormode.txt`])
|
||||
.then(execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/switchwall.sh --noswitch`]).catch(print))
|
||||
.catch(print);
|
||||
}
|
||||
@@ -34,10 +35,10 @@ export function launchCustomCommand(command) {
|
||||
execAsync([`bash`, `-c`, `${App.configDir}/scripts/color_generation/applycolor.sh --bad-apple`]).catch(print);
|
||||
}
|
||||
else if (args[0] == '>material') { // Light mode
|
||||
execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "material" > ~/.cache/ags/user/colorbackend.txt`]).catch(print);
|
||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "material" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print);
|
||||
}
|
||||
else if (args[0] == '>pywal') { // Dark mode
|
||||
execAsync([`bash`, `-c`, `mkdir -p ~/.cache/ags/user && echo "pywal" > ~/.cache/ags/user/colorbackend.txt`]).catch(print);
|
||||
execAsync([`bash`, `-c`, `mkdir -p ${GLib.get_user_cache_dir()}/ags/user && echo "pywal" > ${GLib.get_user_cache_dir()}/ags/user/colorbackend.txt`]).catch(print);
|
||||
}
|
||||
else if (args[0] == '>todo') { // Todo
|
||||
Todo.add(args.slice(1).join(' '));
|
||||
|
||||
@@ -1,547 +0,0 @@
|
||||
const { Gdk, Gio, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Variable, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import { setupCursorHover, setupCursorHoverGrab } from "../../lib/cursorhover.js";
|
||||
import { DoubleRevealer } from "../../lib/advancedrevealers.js";
|
||||
import { execAndClose, expandTilde, hasUnterminatedBackslash, startsWithNumber, launchCustomCommand, ls } from './miscfunctions.js';
|
||||
import {
|
||||
CalculationResultButton, CustomCommandButton, DirectoryButton,
|
||||
DesktopEntryButton, ExecuteCommandButton, SearchButton
|
||||
} from './searchbuttons.js';
|
||||
import { dumpToWorkspace, swapWorkspace } from "./actions.js";
|
||||
|
||||
// Add math funcs
|
||||
const { abs, sin, cos, tan, cot, asin, acos, atan, acot } = Math;
|
||||
const pi = Math.PI;
|
||||
// trigonometric funcs for deg
|
||||
const sind = x => sin(x * pi / 180);
|
||||
const cosd = x => cos(x * pi / 180);
|
||||
const tand = x => tan(x * pi / 180);
|
||||
const cotd = x => cot(x * pi / 180);
|
||||
const asind = x => asin(x) * 180 / pi;
|
||||
const acosd = x => acos(x) * 180 / pi;
|
||||
const atand = x => atan(x) * 180 / pi;
|
||||
const acotd = x => acot(x) * 180 / pi;
|
||||
|
||||
const MAX_RESULTS = 10;
|
||||
const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size
|
||||
const OVERVIEW_WS_NUM_SCALE = 0.09;
|
||||
const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07;
|
||||
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
||||
const searchPromptTexts = [
|
||||
'Try "~/.config"',
|
||||
'Try "Files"',
|
||||
'Try "6*cos(pi)"',
|
||||
'Try "sudo pacman -Syu"',
|
||||
'Try "How to basic"',
|
||||
'Drag n\' drop to move windows',
|
||||
'Type to search',
|
||||
]
|
||||
|
||||
const overviewTick = Variable(false);
|
||||
|
||||
function truncateTitle(str) {
|
||||
let lastDash = -1;
|
||||
let found = -1; // 0: em dash, 1: en dash, 2: minus, 3: vertical bar, 4: middle dot
|
||||
for (let i = str.length - 1; i >= 0; i--) {
|
||||
if (str[i] === '—') {
|
||||
found = 0;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '–' && found < 1) {
|
||||
found = 1;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '-' && found < 2) {
|
||||
found = 2;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '|' && found < 3) {
|
||||
found = 3;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '·' && found < 4) {
|
||||
found = 4;
|
||||
lastDash = i;
|
||||
}
|
||||
}
|
||||
if (lastDash === -1) return str;
|
||||
return str.substring(0, lastDash);
|
||||
}
|
||||
|
||||
function iconExists(iconName) {
|
||||
let iconTheme = Gtk.IconTheme.get_default();
|
||||
return iconTheme.has_icon(iconName);
|
||||
}
|
||||
|
||||
function substitute(str) {
|
||||
const subs = [
|
||||
{ from: 'code-url-handler', to: 'visual-studio-code' },
|
||||
{ from: 'Code', to: 'visual-studio-code' },
|
||||
{ from: 'GitHub Desktop', to: 'github-desktop' },
|
||||
{ from: 'wpsoffice', to: 'wps-office2019-kprometheus' },
|
||||
{ from: 'gnome-tweaks', to: 'org.gnome.tweaks' },
|
||||
{ from: 'Minecraft* 1.20.1', to: 'minecraft' },
|
||||
{ from: '', to: 'image-missing' },
|
||||
];
|
||||
|
||||
for (const { from, to } of subs) {
|
||||
if (from === str)
|
||||
return to;
|
||||
}
|
||||
|
||||
if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, '-'); // Turn into kebab-case
|
||||
return str;
|
||||
}
|
||||
|
||||
const ContextWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({
|
||||
label: `${label}`,
|
||||
setup: (menuItem) => {
|
||||
let submenu = new Gtk.Menu();
|
||||
submenu.className = 'menu';
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
let button = new Gtk.MenuItem({
|
||||
label: `Workspace ${i}`
|
||||
});
|
||||
button.connect("activate", () => {
|
||||
// execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print);
|
||||
actionFunc(thisWorkspace, i);
|
||||
overviewTick.value = !overviewTick.value;
|
||||
});
|
||||
submenu.append(button);
|
||||
}
|
||||
menuItem.set_reserve_indicator(true);
|
||||
menuItem.set_submenu(submenu);
|
||||
}
|
||||
})
|
||||
|
||||
const client = ({ address, size: [w, h], workspace: { id, name }, class: c, title, xwayland }) => {
|
||||
const revealInfoCondition = (Math.min(w, h) * OVERVIEW_SCALE > 70);
|
||||
if (w <= 0 || h <= 0) return null;
|
||||
title = truncateTitle(title);
|
||||
return Widget.Button({
|
||||
className: 'overview-tasks-window',
|
||||
hpack: 'center',
|
||||
vpack: 'center',
|
||||
onClicked: () => {
|
||||
execAsync([`bash`, `-c`, `hyprctl dispatch focuswindow address:${address}`, `&`]).catch(print);
|
||||
App.closeWindow('overview');
|
||||
},
|
||||
onMiddleClickRelease: () => execAsync([`bash`, `-c`, `hyprctl dispatch closewindow address:${address}`, `&`]).catch(print),
|
||||
onSecondaryClick: (button) => {
|
||||
button.toggleClassName('overview-tasks-window-selected', true);
|
||||
const menu = Widget.Menu({
|
||||
className: 'menu',
|
||||
children: [
|
||||
Widget.MenuItem({
|
||||
child: Widget.Label({
|
||||
xalign: 0,
|
||||
label: "Close (Middle-click)",
|
||||
}),
|
||||
onActivate: () => {
|
||||
execAsync([`bash`, `-c`, `hyprctl dispatch closewindow address:${address}`, `&`])
|
||||
.catch(print);
|
||||
}
|
||||
}),
|
||||
ContextWorkspaceArray({
|
||||
label: "Dump windows to workspace",
|
||||
actionFunc: dumpToWorkspace,
|
||||
thisWorkspace: Number(id)
|
||||
}),
|
||||
ContextWorkspaceArray({
|
||||
label: "Swap windows with workspace",
|
||||
actionFunc: swapWorkspace,
|
||||
thisWorkspace: Number(id)
|
||||
}),
|
||||
],
|
||||
});
|
||||
menu.connect("deactivate", () => {
|
||||
button.toggleClassName('overview-tasks-window-selected', false);
|
||||
})
|
||||
menu.connect("selection-done", () => {
|
||||
button.toggleClassName('overview-tasks-window-selected', false);
|
||||
})
|
||||
menu.popup_at_pointer(null); // Show the menu at the pointer's position
|
||||
},
|
||||
child: Widget.Box({
|
||||
css: `
|
||||
min-width: ${Math.max(w * OVERVIEW_SCALE - 4, 1)}px;
|
||||
min-height: ${Math.max(h * OVERVIEW_SCALE - 4, 1)}px;
|
||||
`,
|
||||
homogeneous: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
vpack: 'center',
|
||||
className: 'spacing-v-5',
|
||||
children: [
|
||||
Widget.Icon({
|
||||
icon: substitute(c),
|
||||
size: Math.min(w, h) * OVERVIEW_SCALE / 2.5,
|
||||
}),
|
||||
// TODO: Add xwayland tag instead of just having italics
|
||||
DoubleRevealer({
|
||||
transition1: 'slide_right',
|
||||
transition2: 'slide_down',
|
||||
revealChild: revealInfoCondition,
|
||||
child: Widget.Scrollable({
|
||||
hexpand: true,
|
||||
vscroll: 'never',
|
||||
hscroll: 'automatic',
|
||||
child: Widget.Label({
|
||||
truncate: 'end',
|
||||
className: `${xwayland ? 'txt txt-italic' : 'txt'}`,
|
||||
css: `
|
||||
font-size: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE / 14.6}px;
|
||||
margin: 0px ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE / 10}px;
|
||||
`,
|
||||
// If the title is too short, include the class
|
||||
label: (title.length <= 1 ? `${c}: ${title}` : title),
|
||||
})
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
}),
|
||||
tooltipText: `${c}: ${title}`,
|
||||
setup: (button) => {
|
||||
setupCursorHoverGrab(button);
|
||||
|
||||
button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.MOVE);
|
||||
button.drag_source_set_icon_name(substitute(c));
|
||||
// button.drag_source_set_icon_gicon(icon);
|
||||
|
||||
button.connect('drag-begin', (button) => { // On drag start, add the dragging class
|
||||
button.toggleClassName('overview-tasks-window-dragging', true);
|
||||
});
|
||||
button.connect('drag-data-get', (_w, _c, data) => { // On drag finish, give address
|
||||
data.set_text(address, address.length);
|
||||
button.toggleClassName('overview-tasks-window-dragging', false);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const workspace = index => {
|
||||
const fixed = Gtk.Fixed.new();
|
||||
const WorkspaceNumber = (index) => Widget.Label({
|
||||
className: 'overview-tasks-workspace-number',
|
||||
label: `${index}`,
|
||||
css: `
|
||||
margin: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE * OVERVIEW_WS_NUM_MARGIN_SCALE}px;
|
||||
font-size: ${SCREEN_HEIGHT * OVERVIEW_SCALE * OVERVIEW_WS_NUM_SCALE}px;
|
||||
`,
|
||||
})
|
||||
const widget = Widget.Box({
|
||||
className: 'overview-tasks-workspace',
|
||||
vpack: 'center',
|
||||
css: `
|
||||
min-width: ${SCREEN_WIDTH * OVERVIEW_SCALE}px;
|
||||
min-height: ${SCREEN_HEIGHT * OVERVIEW_SCALE}px;
|
||||
`,
|
||||
children: [Widget.EventBox({
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
onPrimaryClickRelease: () => {
|
||||
execAsync([`bash`, `-c`, `hyprctl dispatch workspace ${index}`, `&`]).catch(print);
|
||||
App.closeWindow('overview');
|
||||
},
|
||||
setup: eventbox => {
|
||||
eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
||||
eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => {
|
||||
overviewTick.value = !overviewTick.value;
|
||||
execAsync([`bash`, `-c`, `hyprctl dispatch movetoworkspacesilent ${index},address:${data.get_text()}`, `&`]).catch(print);
|
||||
});
|
||||
},
|
||||
child: fixed,
|
||||
})],
|
||||
});
|
||||
widget.update = (clients) => {
|
||||
clients = clients.filter(({ workspace: { id } }) => id === index);
|
||||
|
||||
// this is for my monitor layout
|
||||
// shifts clients back by SCREEN_WIDTHpx if necessary
|
||||
clients = clients.map(client => {
|
||||
const [x, y] = client.at;
|
||||
if (x > SCREEN_WIDTH)
|
||||
client.at = [x - SCREEN_WIDTH, y];
|
||||
return client;
|
||||
});
|
||||
|
||||
const children = fixed.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
child.destroy();
|
||||
}
|
||||
fixed.put(WorkspaceNumber(index), 0, 0);
|
||||
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
const c = clients[i];
|
||||
if (c.mapped) {
|
||||
fixed.put(client(c), c.at[0] * OVERVIEW_SCALE, c.at[1] * OVERVIEW_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fixed.show_all();
|
||||
};
|
||||
return widget;
|
||||
};
|
||||
|
||||
const arr = (s, n) => {
|
||||
const array = [];
|
||||
for (let i = 0; i < n; i++)
|
||||
array.push(s + i);
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => Widget.Box({
|
||||
children: arr(startWorkspace, workspaces).map(workspace),
|
||||
properties: [['update', box => {
|
||||
execAsync('hyprctl -j clients').then(clients => {
|
||||
const json = JSON.parse(clients);
|
||||
const children = box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const ch = children[i];
|
||||
ch.update(json)
|
||||
}
|
||||
|
||||
}).catch(print);
|
||||
}]],
|
||||
setup: (box) => box._update(box),
|
||||
connections: [
|
||||
// Update on change
|
||||
[overviewTick, box => { if (!App.getWindow(windowName).visible) return; Utils.timeout(2, () => box._update(box)); }],
|
||||
[Hyprland, box => { if (!App.getWindow(windowName).visible) return; box._update(box); }, 'client-added'],
|
||||
[Hyprland, box => { if (!App.getWindow(windowName).visible) return; box._update(box); }, 'client-removed'],
|
||||
// Update on show
|
||||
[App, (box, name, visible) => { // Update on open
|
||||
if (name == 'overview' && visible) {
|
||||
box._update(box);
|
||||
}
|
||||
}],
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
export const SearchAndWindows = () => {
|
||||
var _appSearchResults = [];
|
||||
|
||||
const ClickToClose = ({ ...props }) => Widget.EventBox({
|
||||
...props,
|
||||
onPrimaryClick: () => App.closeWindow('overview'),
|
||||
onSecondaryClick: () => App.closeWindow('overview'),
|
||||
onMiddleClick: () => App.closeWindow('overview'),
|
||||
});
|
||||
const resultsBox = Widget.Box({
|
||||
className: 'overview-search-results',
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
});
|
||||
const resultsRevealer = Widget.Revealer({
|
||||
transitionDuration: 200,
|
||||
revealChild: false,
|
||||
transition: 'slide_down',
|
||||
// duration: 200,
|
||||
hpack: 'center',
|
||||
child: resultsBox,
|
||||
});
|
||||
const overviewRevealer = Widget.Revealer({
|
||||
revealChild: true,
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 200,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
className: 'overview-tasks',
|
||||
children: [
|
||||
OverviewRow({ startWorkspace: 1, workspaces: 5 }),
|
||||
OverviewRow({ startWorkspace: 6, workspaces: 5 }),
|
||||
]
|
||||
}),
|
||||
});
|
||||
const entryPromptRevealer = Widget.Revealer({
|
||||
transition: 'crossfade',
|
||||
transitionDuration: 150,
|
||||
revealChild: true,
|
||||
hpack: 'center',
|
||||
child: Widget.Label({
|
||||
className: 'overview-search-prompt txt-small txt',
|
||||
label: searchPromptTexts[Math.floor(Math.random() * searchPromptTexts.length)],
|
||||
})
|
||||
});
|
||||
|
||||
const entryIconRevealer = Widget.Revealer({
|
||||
transition: 'crossfade',
|
||||
transitionDuration: 150,
|
||||
revealChild: false,
|
||||
hpack: 'end',
|
||||
child: Widget.Label({
|
||||
className: 'txt txt-large icon-material overview-search-icon',
|
||||
label: 'search',
|
||||
}),
|
||||
});
|
||||
|
||||
const entryIcon = Widget.Box({
|
||||
className: 'overview-search-prompt-box',
|
||||
setup: box => box.pack_start(entryIconRevealer, true, true, 0),
|
||||
});
|
||||
|
||||
const entry = Widget.Entry({
|
||||
className: 'overview-search-box txt-small txt',
|
||||
hpack: 'center',
|
||||
onAccept: (self) => { // This is when you hit Enter
|
||||
const text = self.text;
|
||||
if (text.length == 0) return;
|
||||
const isAction = text.startsWith('>');
|
||||
const isDir = (entry.text[0] == '/' || entry.text[0] == '~');
|
||||
|
||||
if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a workaround
|
||||
try {
|
||||
const fullResult = eval(text);
|
||||
// copy
|
||||
execAsync(['wl-copy', `${fullResult}`]).catch(print);
|
||||
App.closeWindow('overview');
|
||||
return;
|
||||
} catch (e) {
|
||||
// console.log(e);
|
||||
}
|
||||
}
|
||||
if (isDir) {
|
||||
App.closeWindow('overview');
|
||||
execAsync(['bash', '-c', `xdg-open "${expandTilde(text)}"`, `&`]).catch(print);
|
||||
return;
|
||||
}
|
||||
if (_appSearchResults.length > 0) {
|
||||
App.closeWindow('overview');
|
||||
_appSearchResults[0].launch();
|
||||
return;
|
||||
}
|
||||
else if (text[0] == '>') { // Custom commands
|
||||
App.closeWindow('overview');
|
||||
launchCustomCommand(text);
|
||||
return;
|
||||
}
|
||||
// Fallback: Execute command
|
||||
if (!isAction && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') {
|
||||
if (text.startsWith('sudo'))
|
||||
execAndClose(text, true);
|
||||
else
|
||||
execAndClose(text, false);
|
||||
}
|
||||
|
||||
else {
|
||||
App.closeWindow('overview');
|
||||
execAsync(['bash', '-c', `xdg-open 'https://www.google.com/search?q=${text} -site:quora.com' &`]).catch(print); // fuck quora
|
||||
}
|
||||
},
|
||||
// Actually onChange but this is ta workaround for a bug
|
||||
connections: [
|
||||
['notify::text', (entry) => { // This is when you type
|
||||
const isAction = entry.text[0] == '>';
|
||||
const isDir = (entry.text[0] == '/' || entry.text[0] == '~');
|
||||
const children = resultsBox.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
child.destroy();
|
||||
}
|
||||
// check empty if so then dont do stuff
|
||||
if (entry.text == '') {
|
||||
resultsRevealer.set_reveal_child(false);
|
||||
overviewRevealer.set_reveal_child(true);
|
||||
entryPromptRevealer.set_reveal_child(true);
|
||||
entryIconRevealer.set_reveal_child(false);
|
||||
entry.toggleClassName('overview-search-box-extended', false);
|
||||
}
|
||||
else {
|
||||
const text = entry.text;
|
||||
resultsRevealer.set_reveal_child(true);
|
||||
overviewRevealer.set_reveal_child(false);
|
||||
entryPromptRevealer.set_reveal_child(false);
|
||||
entryIconRevealer.set_reveal_child(true);
|
||||
entry.toggleClassName('overview-search-box-extended', true);
|
||||
_appSearchResults = Applications.query(text);
|
||||
|
||||
// Calculate
|
||||
if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a small workaround.
|
||||
try {
|
||||
const fullResult = eval(text);
|
||||
resultsBox.add(CalculationResultButton({ result: fullResult, text: text }));
|
||||
} catch (e) {
|
||||
// console.log(e);
|
||||
}
|
||||
}
|
||||
if (isDir) {
|
||||
var contents = [];
|
||||
contents = ls({ path: text, silent: true });
|
||||
contents.forEach((item) => {
|
||||
resultsBox.add(DirectoryButton(item));
|
||||
})
|
||||
}
|
||||
if (isAction) { // Eval on typing is dangerous, this is a workaround.
|
||||
resultsBox.add(CustomCommandButton({ text: entry.text }));
|
||||
}
|
||||
// Add application entries
|
||||
let appsToAdd = MAX_RESULTS;
|
||||
_appSearchResults.forEach(app => {
|
||||
if (appsToAdd == 0) return;
|
||||
resultsBox.add(DesktopEntryButton(app));
|
||||
appsToAdd--;
|
||||
});
|
||||
|
||||
// Fallbacks
|
||||
// if the first word is an actual command
|
||||
if (!isAction && !hasUnterminatedBackslash(text) && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') {
|
||||
resultsBox.add(ExecuteCommandButton({ command: entry.text, terminal: entry.text.startsWith('sudo') }));
|
||||
}
|
||||
|
||||
// Add fallback: search
|
||||
resultsBox.add(SearchButton({ text: entry.text }));
|
||||
resultsBox.show_all();
|
||||
}
|
||||
}]
|
||||
],
|
||||
});
|
||||
|
||||
return Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
ClickToClose({ // Top margin. Also works as a click-outside-to-close thing
|
||||
child: Widget.Box({
|
||||
className: 'bar-height',
|
||||
})
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: 'center',
|
||||
children: [
|
||||
entry,
|
||||
Widget.Box({
|
||||
className: 'overview-search-icon-box',
|
||||
setup: box => box.pack_start(entryPromptRevealer, true, true, 0),
|
||||
}),
|
||||
entryIcon,
|
||||
]
|
||||
}),
|
||||
overviewRevealer,
|
||||
resultsRevealer,
|
||||
],
|
||||
connections: [
|
||||
[App, (_b, name, visible) => {
|
||||
if (name == 'overview' && !visible) {
|
||||
entryPromptRevealer.child.label = searchPromptTexts[Math.floor(Math.random() * searchPromptTexts.length)];
|
||||
resultsBox.children = [];
|
||||
entry.set_text('');
|
||||
}
|
||||
}],
|
||||
['key-press-event', (widget, event) => { // Typing
|
||||
if (event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 && widget != entry) {
|
||||
Utils.timeout(1, () => entry.grab_focus());
|
||||
entry.set_text(entry.text + String.fromCharCode(event.get_keyval()[1]));
|
||||
entry.set_position(-1);
|
||||
}
|
||||
}],
|
||||
],
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,308 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||
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 Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import { setupCursorHoverGrab } from "../../lib/cursorhover.js";
|
||||
import { dumpToWorkspace, swapWorkspace } from "./actions.js";
|
||||
|
||||
const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size
|
||||
const OVERVIEW_WS_NUM_SCALE = 0.09;
|
||||
const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07;
|
||||
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
||||
|
||||
function truncateTitle(str) {
|
||||
let lastDash = -1;
|
||||
let found = -1; // 0: em dash, 1: en dash, 2: minus, 3: vertical bar, 4: middle dot
|
||||
for (let i = str.length - 1; i >= 0; i--) {
|
||||
if (str[i] === '—') {
|
||||
found = 0;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '–' && found < 1) {
|
||||
found = 1;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '-' && found < 2) {
|
||||
found = 2;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '|' && found < 3) {
|
||||
found = 3;
|
||||
lastDash = i;
|
||||
}
|
||||
else if (str[i] === '·' && found < 4) {
|
||||
found = 4;
|
||||
lastDash = i;
|
||||
}
|
||||
}
|
||||
if (lastDash === -1) return str;
|
||||
return str.substring(0, lastDash);
|
||||
}
|
||||
|
||||
function iconExists(iconName) {
|
||||
let iconTheme = Gtk.IconTheme.get_default();
|
||||
return iconTheme.has_icon(iconName);
|
||||
}
|
||||
|
||||
function substitute(str) {
|
||||
const subs = [
|
||||
{ from: 'code-url-handler', to: 'visual-studio-code' },
|
||||
{ from: 'Code', to: 'visual-studio-code' },
|
||||
{ from: 'GitHub Desktop', to: 'github-desktop' },
|
||||
{ from: 'wpsoffice', to: 'wps-office2019-kprometheus' },
|
||||
{ from: 'gnome-tweaks', to: 'org.gnome.tweaks' },
|
||||
{ from: 'Minecraft* 1.20.1', to: 'minecraft' },
|
||||
{ from: '', to: 'image-missing' },
|
||||
];
|
||||
|
||||
for (const { from, to } of subs) {
|
||||
if (from === str)
|
||||
return to;
|
||||
}
|
||||
|
||||
if (!iconExists(str)) str = str.toLowerCase().replace(/\s+/g, '-'); // Turn into kebab-case
|
||||
return str;
|
||||
}
|
||||
|
||||
const ContextMenuWorkspaceArray = ({ label, actionFunc, thisWorkspace }) => Widget.MenuItem({
|
||||
label: `${label}`,
|
||||
setup: (menuItem) => {
|
||||
let submenu = new Gtk.Menu();
|
||||
submenu.className = 'menu';
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
let button = new Gtk.MenuItem({
|
||||
label: `Workspace ${i}`
|
||||
});
|
||||
button.connect("activate", () => {
|
||||
// execAsync([`${onClickBinary}`, `${thisWorkspace}`, `${i}`]).catch(print);
|
||||
actionFunc(thisWorkspace, i);
|
||||
});
|
||||
submenu.append(button);
|
||||
}
|
||||
menuItem.set_reserve_indicator(true);
|
||||
menuItem.set_submenu(submenu);
|
||||
}
|
||||
})
|
||||
|
||||
const client = ({ address, size: [w, h], workspace: { id, name }, class: c, title, xwayland }) => {
|
||||
const revealInfoCondition = (Math.min(w, h) * OVERVIEW_SCALE > 70);
|
||||
if (w <= 0 || h <= 0) return null;
|
||||
// title = truncateTitle(title);
|
||||
return Widget.Button({
|
||||
className: 'overview-tasks-window',
|
||||
hpack: 'center',
|
||||
vpack: 'center',
|
||||
onClicked: () => {
|
||||
Hyprland.sendMessage(`dispatch focuswindow address:${address}`);
|
||||
App.closeWindow('overview');
|
||||
},
|
||||
onMiddleClickRelease: () => Hyprland.sendMessage(`dispatch closewindow address:${address}`),
|
||||
onSecondaryClick: (button) => {
|
||||
button.toggleClassName('overview-tasks-window-selected', true);
|
||||
const menu = Widget.Menu({
|
||||
className: 'menu',
|
||||
children: [
|
||||
Widget.MenuItem({
|
||||
child: Widget.Label({
|
||||
xalign: 0,
|
||||
label: "Close (Middle-click)",
|
||||
}),
|
||||
onActivate: () => Hyprland.sendMessage(`dispatch closewindow address:${address}`),
|
||||
}),
|
||||
ContextMenuWorkspaceArray({
|
||||
label: "Dump windows to workspace",
|
||||
actionFunc: dumpToWorkspace,
|
||||
thisWorkspace: Number(id)
|
||||
}),
|
||||
ContextMenuWorkspaceArray({
|
||||
label: "Swap windows with workspace",
|
||||
actionFunc: swapWorkspace,
|
||||
thisWorkspace: Number(id)
|
||||
}),
|
||||
],
|
||||
});
|
||||
menu.connect("deactivate", () => {
|
||||
button.toggleClassName('overview-tasks-window-selected', false);
|
||||
})
|
||||
menu.connect("selection-done", () => {
|
||||
button.toggleClassName('overview-tasks-window-selected', false);
|
||||
})
|
||||
menu.popup_at_pointer(null); // Show the menu at the pointer's position
|
||||
},
|
||||
child: Widget.Box({
|
||||
css: `
|
||||
min-width: ${Math.max(w * OVERVIEW_SCALE - 4, 1)}px;
|
||||
min-height: ${Math.max(h * OVERVIEW_SCALE - 4, 1)}px;
|
||||
`,
|
||||
homogeneous: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
vpack: 'center',
|
||||
className: 'spacing-v-5',
|
||||
children: [
|
||||
Widget.Icon({
|
||||
icon: substitute(c),
|
||||
size: Math.min(w, h) * OVERVIEW_SCALE / 2.5,
|
||||
}),
|
||||
// TODO: Add xwayland tag instead of just having italics
|
||||
Widget.Revealer({
|
||||
transition: 'slide_down',
|
||||
revealChild: revealInfoCondition,
|
||||
child: Widget.Label({
|
||||
truncate: 'end',
|
||||
className: `${xwayland ? 'txt txt-italic' : 'txt'}`,
|
||||
css: `
|
||||
font-size: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE / 14.6}px;
|
||||
margin: 0px ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE / 10}px;
|
||||
`,
|
||||
// If the title is too short, include the class
|
||||
label: (title.length <= 1 ? `${c}: ${title}` : title),
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
}),
|
||||
tooltipText: `${c}: ${title}`,
|
||||
setup: (button) => {
|
||||
setupCursorHoverGrab(button);
|
||||
|
||||
button.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.MOVE);
|
||||
button.drag_source_set_icon_name(substitute(c));
|
||||
// button.drag_source_set_icon_gicon(icon);
|
||||
|
||||
button.connect('drag-begin', (button) => { // On drag start, add the dragging class
|
||||
button.toggleClassName('overview-tasks-window-dragging', true);
|
||||
});
|
||||
button.connect('drag-data-get', (_w, _c, data) => { // On drag finish, give address
|
||||
data.set_text(address, address.length);
|
||||
button.toggleClassName('overview-tasks-window-dragging', false);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const workspace = index => {
|
||||
const fixed = Gtk.Fixed.new();
|
||||
const WorkspaceNumber = (index) => Widget.Label({
|
||||
className: 'overview-tasks-workspace-number',
|
||||
label: `${index}`,
|
||||
css: `
|
||||
margin: ${Math.min(SCREEN_WIDTH, SCREEN_HEIGHT) * OVERVIEW_SCALE * OVERVIEW_WS_NUM_MARGIN_SCALE}px;
|
||||
font-size: ${SCREEN_HEIGHT * OVERVIEW_SCALE * OVERVIEW_WS_NUM_SCALE}px;
|
||||
`,
|
||||
})
|
||||
const widget = Widget.Box({
|
||||
className: 'overview-tasks-workspace',
|
||||
vpack: 'center',
|
||||
css: `
|
||||
min-width: ${SCREEN_WIDTH * OVERVIEW_SCALE}px;
|
||||
min-height: ${SCREEN_HEIGHT * OVERVIEW_SCALE}px;
|
||||
`,
|
||||
children: [Widget.EventBox({
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
onPrimaryClick: () => {
|
||||
Hyprland.sendMessage(`dispatch workspace ${index}`)
|
||||
App.closeWindow('overview');
|
||||
},
|
||||
setup: (eventbox) => {
|
||||
eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY);
|
||||
eventbox.connect('drag-data-received', (_w, _c, _x, _y, data) => {
|
||||
Hyprland.sendMessage(`dispatch movetoworkspacesilent ${index},address:${data.get_text()}`)
|
||||
});
|
||||
},
|
||||
child: fixed,
|
||||
})],
|
||||
});
|
||||
widget.update = (clients) => {
|
||||
clients = clients.filter(({ workspace: { id } }) => id === index);
|
||||
|
||||
// this is for my monitor layout
|
||||
// shifts clients back by SCREEN_WIDTHpx if necessary
|
||||
clients = clients.map(client => {
|
||||
const [x, y] = client.at;
|
||||
if (x > SCREEN_WIDTH)
|
||||
client.at = [x - SCREEN_WIDTH, y];
|
||||
return client;
|
||||
});
|
||||
|
||||
const children = fixed.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
child.destroy();
|
||||
}
|
||||
fixed.put(WorkspaceNumber(index), 0, 0);
|
||||
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
const c = clients[i];
|
||||
if (c.mapped) {
|
||||
fixed.put(client(c), c.at[0] * OVERVIEW_SCALE, c.at[1] * OVERVIEW_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fixed.show_all();
|
||||
};
|
||||
return widget;
|
||||
};
|
||||
|
||||
const arr = (s, n) => {
|
||||
const array = [];
|
||||
for (let i = 0; i < n; i++)
|
||||
array.push(s + i);
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
const OverviewRow = ({ startWorkspace, workspaces, windowName = 'overview' }) => Widget.Box({
|
||||
children: arr(startWorkspace, workspaces).map(workspace),
|
||||
attribute: {
|
||||
'update': box => {
|
||||
if (!App.getWindow(windowName).visible) return;
|
||||
execAsync('hyprctl -j clients').then(clients => {
|
||||
const json = JSON.parse(clients);
|
||||
const children = box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const ch = children[i];
|
||||
ch.update(json)
|
||||
}
|
||||
|
||||
}).catch(print);
|
||||
}
|
||||
},
|
||||
setup: (box) => {
|
||||
box
|
||||
// .hook(Hyprland, (box, name, data) => { // idk, does this make it lag occasionally?
|
||||
// if (["changefloatingmode", "movewindow"].includes(name))
|
||||
// box.attribute.update(box);
|
||||
// }, 'event')
|
||||
.hook(Hyprland, (box) => box.attribute.update(box), 'client-added')
|
||||
.hook(Hyprland, (box) => box.attribute.update(box), 'client-removed')
|
||||
.hook(App, (box, name, visible) => { // Update on open
|
||||
if (name == 'overview' && visible) box.attribute.update(box);
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export default () => {
|
||||
const overviewRevealer = Widget.Revealer({
|
||||
revealChild: true,
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 200,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
className: 'overview-tasks',
|
||||
children: [
|
||||
OverviewRow({ startWorkspace: 1, workspaces: 5 }),
|
||||
OverviewRow({ startWorkspace: 6, workspaces: 5 }),
|
||||
]
|
||||
}),
|
||||
});
|
||||
return overviewRevealer;
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { Gtk } = imports.gi;
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import { searchItem } from './searchitem.js';
|
||||
import { execAndClose, startsWithNumber, launchCustomCommand } from './miscfunctions.js';
|
||||
@@ -54,16 +56,16 @@ export const DirectoryButton = ({ parentPath, name, type, icon }) => {
|
||||
})
|
||||
]
|
||||
}),
|
||||
connections: [
|
||||
['focus-in-event', (button) => {
|
||||
setup: (self) => self
|
||||
.on('focus-in-event', (button) => {
|
||||
actionText.revealChild = true;
|
||||
actionTextRevealer.revealChild = true;
|
||||
}],
|
||||
['focus-out-event', (button) => {
|
||||
})
|
||||
.on('focus-out-event', (button) => {
|
||||
actionText.revealChild = false;
|
||||
actionTextRevealer.revealChild = false;
|
||||
}],
|
||||
]
|
||||
})
|
||||
,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -128,16 +130,16 @@ export const DesktopEntryButton = (app) => {
|
||||
})
|
||||
]
|
||||
}),
|
||||
connections: [
|
||||
['focus-in-event', (button) => {
|
||||
setup: (self) => self
|
||||
.on('focus-in-event', (button) => {
|
||||
actionText.revealChild = true;
|
||||
actionTextRevealer.revealChild = true;
|
||||
}],
|
||||
['focus-out-event', (button) => {
|
||||
})
|
||||
.on('focus-out-event', (button) => {
|
||||
actionText.revealChild = false;
|
||||
actionTextRevealer.revealChild = false;
|
||||
}],
|
||||
]
|
||||
})
|
||||
,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -167,6 +169,6 @@ export const SearchButton = ({ text = '' }) => searchItem({
|
||||
content: `${text}`,
|
||||
onActivate: () => {
|
||||
App.closeWindow('overview');
|
||||
execAsync(['bash', '-c', `xdg-open 'https://www.google.com/search?q=${text} -site:quora.com' &`]).catch(print); // fuck quora
|
||||
execAsync(['bash', '-c', `xdg-open 'https://www.google.com/search?q=${text} -site:quora.com' &`]).catch(print); // quora is useless
|
||||
},
|
||||
});
|
||||
@@ -1,8 +1,4 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget } from '../../imports.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import { setupCursorHover, setupCursorHoverAim } from "../../lib/cursorhover.js";
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
|
||||
export const searchItem = ({ materialIconName, name, actionName, content, onActivate }) => {
|
||||
const actionText = Widget.Revealer({
|
||||
@@ -55,15 +51,15 @@ export const searchItem = ({ materialIconName, name, actionName, content, onActi
|
||||
})
|
||||
]
|
||||
}),
|
||||
connections: [
|
||||
['focus-in-event', (button) => {
|
||||
setup: (self) => self
|
||||
.on('focus-in-event', (button) => {
|
||||
actionText.revealChild = true;
|
||||
actionTextRevealer.revealChild = true;
|
||||
}],
|
||||
['focus-out-event', (button) => {
|
||||
})
|
||||
.on('focus-out-event', (button) => {
|
||||
actionText.revealChild = false;
|
||||
actionTextRevealer.revealChild = false;
|
||||
}],
|
||||
]
|
||||
})
|
||||
,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
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';
|
||||
|
||||
import Applications from 'resource:///com/github/Aylur/ags/service/applications.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import { execAndClose, expandTilde, hasUnterminatedBackslash, startsWithNumber, launchCustomCommand, ls } from './miscfunctions.js';
|
||||
import {
|
||||
CalculationResultButton, CustomCommandButton, DirectoryButton,
|
||||
DesktopEntryButton, ExecuteCommandButton, SearchButton
|
||||
} from './searchbuttons.js';
|
||||
|
||||
// Add math funcs
|
||||
const { abs, sin, cos, tan, cot, asin, acos, atan, acot } = Math;
|
||||
const pi = Math.PI;
|
||||
// trigonometric funcs for deg
|
||||
const sind = x => sin(x * pi / 180);
|
||||
const cosd = x => cos(x * pi / 180);
|
||||
const tand = x => tan(x * pi / 180);
|
||||
const cotd = x => cot(x * pi / 180);
|
||||
const asind = x => asin(x) * 180 / pi;
|
||||
const acosd = x => acos(x) * 180 / pi;
|
||||
const atand = x => atan(x) * 180 / pi;
|
||||
const acotd = x => acot(x) * 180 / pi;
|
||||
|
||||
const MAX_RESULTS = 10;
|
||||
const OVERVIEW_SCALE = 0.18; // = overview workspace box / screen size
|
||||
const OVERVIEW_WS_NUM_SCALE = 0.09;
|
||||
const OVERVIEW_WS_NUM_MARGIN_SCALE = 0.07;
|
||||
const TARGET = [Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags.SAME_APP, 0)];
|
||||
|
||||
function iconExists(iconName) {
|
||||
let iconTheme = Gtk.IconTheme.get_default();
|
||||
return iconTheme.has_icon(iconName);
|
||||
}
|
||||
|
||||
const OptionalOverview = async () => {
|
||||
try {
|
||||
return (await import('./overview_hyprland.js')).default();
|
||||
} catch {
|
||||
return null;
|
||||
// return (await import('./overview_hyprland.js')).default();
|
||||
}
|
||||
};
|
||||
|
||||
const overviewContent = await OptionalOverview();
|
||||
|
||||
export const SearchAndWindows = () => {
|
||||
var _appSearchResults = [];
|
||||
|
||||
const ClickToClose = ({ ...props }) => Widget.EventBox({
|
||||
...props,
|
||||
onPrimaryClick: () => App.closeWindow('overview'),
|
||||
onSecondaryClick: () => App.closeWindow('overview'),
|
||||
onMiddleClick: () => App.closeWindow('overview'),
|
||||
});
|
||||
const resultsBox = Widget.Box({
|
||||
className: 'overview-search-results',
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
});
|
||||
const resultsRevealer = Widget.Revealer({
|
||||
transitionDuration: 200,
|
||||
revealChild: false,
|
||||
transition: 'slide_down',
|
||||
// duration: 200,
|
||||
hpack: 'center',
|
||||
child: resultsBox,
|
||||
});
|
||||
const entryPromptRevealer = Widget.Revealer({
|
||||
transition: 'crossfade',
|
||||
transitionDuration: 150,
|
||||
revealChild: true,
|
||||
hpack: 'center',
|
||||
child: Widget.Label({
|
||||
className: 'overview-search-prompt txt-small txt',
|
||||
label: 'Type to search'
|
||||
})
|
||||
});
|
||||
|
||||
const entryIconRevealer = Widget.Revealer({
|
||||
transition: 'crossfade',
|
||||
transitionDuration: 150,
|
||||
revealChild: false,
|
||||
hpack: 'end',
|
||||
child: Widget.Label({
|
||||
className: 'txt txt-large icon-material overview-search-icon',
|
||||
label: 'search',
|
||||
}),
|
||||
});
|
||||
|
||||
const entryIcon = Widget.Box({
|
||||
className: 'overview-search-prompt-box',
|
||||
setup: box => box.pack_start(entryIconRevealer, true, true, 0),
|
||||
});
|
||||
|
||||
const entry = Widget.Entry({
|
||||
className: 'overview-search-box txt-small txt',
|
||||
hpack: 'center',
|
||||
onAccept: (self) => { // This is when you hit Enter
|
||||
const text = self.text;
|
||||
if (text.length == 0) return;
|
||||
const isAction = text.startsWith('>');
|
||||
const isDir = (['/', '~'].includes(entry.text[0]));
|
||||
|
||||
if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a workaround
|
||||
try {
|
||||
const fullResult = eval(text);
|
||||
// copy
|
||||
execAsync(['wl-copy', `${fullResult}`]).catch(print);
|
||||
App.closeWindow('overview');
|
||||
return;
|
||||
} catch (e) {
|
||||
// console.log(e);
|
||||
}
|
||||
}
|
||||
if (isDir) {
|
||||
App.closeWindow('overview');
|
||||
execAsync(['bash', '-c', `xdg-open "${expandTilde(text)}"`, `&`]).catch(print);
|
||||
return;
|
||||
}
|
||||
if (_appSearchResults.length > 0) {
|
||||
App.closeWindow('overview');
|
||||
_appSearchResults[0].launch();
|
||||
return;
|
||||
}
|
||||
else if (text[0] == '>') { // Custom commands
|
||||
App.closeWindow('overview');
|
||||
launchCustomCommand(text);
|
||||
return;
|
||||
}
|
||||
// Fallback: Execute command
|
||||
if (!isAction && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') {
|
||||
if (text.startsWith('sudo'))
|
||||
execAndClose(text, true);
|
||||
else
|
||||
execAndClose(text, false);
|
||||
}
|
||||
|
||||
else {
|
||||
App.closeWindow('overview');
|
||||
execAsync(['bash', '-c', `xdg-open 'https://www.google.com/search?q=${text} -site:quora.com' &`]).catch(print); // quora is useless
|
||||
}
|
||||
},
|
||||
onChange: (entry) => { // this is when you type
|
||||
const isAction = entry.text[0] == '>';
|
||||
const isDir = (['/', '~'].includes(entry.text[0]));
|
||||
resultsBox.get_children().forEach(ch => ch.destroy());
|
||||
|
||||
// check empty if so then dont do stuff
|
||||
if (entry.text == '') {
|
||||
resultsRevealer.revealChild = false;
|
||||
overviewContent.revealChild = true;
|
||||
entryPromptRevealer.revealChild = true;
|
||||
entryIconRevealer.revealChild = false;
|
||||
entry.toggleClassName('overview-search-box-extended', false);
|
||||
return;
|
||||
}
|
||||
const text = entry.text;
|
||||
resultsRevealer.revealChild = true;
|
||||
overviewContent.revealChild = false;
|
||||
entryPromptRevealer.revealChild = false;
|
||||
entryIconRevealer.revealChild = true;
|
||||
entry.toggleClassName('overview-search-box-extended', true);
|
||||
_appSearchResults = Applications.query(text);
|
||||
|
||||
// Calculate
|
||||
if (startsWithNumber(text)) { // Eval on typing is dangerous, this is a small workaround.
|
||||
try {
|
||||
const fullResult = eval(text);
|
||||
resultsBox.add(CalculationResultButton({ result: fullResult, text: text }));
|
||||
} catch (e) {
|
||||
// console.log(e);
|
||||
}
|
||||
}
|
||||
if (isDir) {
|
||||
var contents = [];
|
||||
contents = ls({ path: text, silent: true });
|
||||
contents.forEach((item) => {
|
||||
resultsBox.add(DirectoryButton(item));
|
||||
})
|
||||
}
|
||||
if (isAction) { // Eval on typing is dangerous, this is a workaround.
|
||||
resultsBox.add(CustomCommandButton({ text: entry.text }));
|
||||
}
|
||||
// Add application entries
|
||||
let appsToAdd = MAX_RESULTS;
|
||||
_appSearchResults.forEach(app => {
|
||||
if (appsToAdd == 0) return;
|
||||
resultsBox.add(DesktopEntryButton(app));
|
||||
appsToAdd--;
|
||||
});
|
||||
|
||||
// Fallbacks
|
||||
// if the first word is an actual command
|
||||
if (!isAction && !hasUnterminatedBackslash(text) && exec(`bash -c "command -v ${text.split(' ')[0]}"`) != '') {
|
||||
resultsBox.add(ExecuteCommandButton({ command: entry.text, terminal: entry.text.startsWith('sudo') }));
|
||||
}
|
||||
|
||||
// Add fallback: search
|
||||
resultsBox.add(SearchButton({ text: entry.text }));
|
||||
resultsBox.show_all();
|
||||
},
|
||||
});
|
||||
return Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
ClickToClose({ // Top margin. Also works as a click-outside-to-close thing
|
||||
child: Widget.Box({
|
||||
className: 'bar-height',
|
||||
})
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: 'center',
|
||||
children: [
|
||||
entry,
|
||||
Widget.Box({
|
||||
className: 'overview-search-icon-box',
|
||||
setup: box => box.pack_start(entryPromptRevealer, true, true, 0),
|
||||
}),
|
||||
entryIcon,
|
||||
]
|
||||
}),
|
||||
overviewContent,
|
||||
resultsRevealer,
|
||||
],
|
||||
setup: (self) => self
|
||||
.hook(App, (_b, name, visible) => {
|
||||
if (name == 'overview' && !visible) {
|
||||
resultsBox.children = [];
|
||||
entry.set_text('');
|
||||
}
|
||||
})
|
||||
.on('key-press-event', (widget, event) => { // Typing
|
||||
if (event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 && widget != entry) {
|
||||
Utils.timeout(1, () => entry.grab_focus());
|
||||
entry.set_text(entry.text + String.fromCharCode(event.get_keyval()[1]));
|
||||
entry.set_position(-1);
|
||||
}
|
||||
})
|
||||
,
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Widget } from '../../imports.js';
|
||||
import Cairo from 'gi://cairo?version=1.0';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { RoundedCorner } from "../../lib/roundedcorner.js";
|
||||
|
||||
const dummyRegion = new Cairo.Region();
|
||||
const enableClickthrough = (self) => self.input_shape_combine_region(dummyRegion);
|
||||
|
||||
export const CornerTopleft = () => Widget.Window({
|
||||
name: 'cornertl',
|
||||
layer: 'top',
|
||||
@@ -8,6 +12,7 @@ export const CornerTopleft = () => Widget.Window({
|
||||
exclusivity: 'normal',
|
||||
visible: true,
|
||||
child: RoundedCorner('topleft', { className: 'corner', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export const CornerTopright = () => Widget.Window({
|
||||
name: 'cornertr',
|
||||
@@ -16,6 +21,7 @@ export const CornerTopright = () => Widget.Window({
|
||||
exclusivity: 'normal',
|
||||
visible: true,
|
||||
child: RoundedCorner('topright', { className: 'corner', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export const CornerBottomleft = () => Widget.Window({
|
||||
name: 'cornerbl',
|
||||
@@ -24,6 +30,7 @@ export const CornerBottomleft = () => Widget.Window({
|
||||
exclusivity: 'ignore',
|
||||
visible: true,
|
||||
child: RoundedCorner('bottomleft', { className: 'corner-black', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
export const CornerBottomright = () => Widget.Window({
|
||||
name: 'cornerbr',
|
||||
@@ -32,5 +39,5 @@ export const CornerBottomright = () => Widget.Window({
|
||||
exclusivity: 'ignore',
|
||||
visible: true,
|
||||
child: RoundedCorner('bottomright', { className: 'corner-black', }),
|
||||
setup: enableClickthrough,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import SessionScreen from "./sessionscreen.js";
|
||||
|
||||
export default () => Widget.Window({ // On-screen keyboard
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// This is for the cool memory indicator on the sidebar
|
||||
// For the right pill of the bar, see system.js
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { App, Service, Utils, Widget, SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../../imports.js';
|
||||
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 { exec, execAsync } = Utils;
|
||||
|
||||
const SessionButton = (name, icon, command, props = {}) => {
|
||||
@@ -41,23 +45,24 @@ const SessionButton = (name, icon, command, props = {}) => {
|
||||
button.get_window().set_cursor(cursor);
|
||||
buttonDescription.revealChild = false;
|
||||
},
|
||||
connections: [
|
||||
['focus-in-event', (self) => {
|
||||
setup: (self) => self
|
||||
.on('focus-in-event', (self) => {
|
||||
buttonDescription.revealChild = true;
|
||||
self.toggleClassName('session-button-focused', true);
|
||||
}],
|
||||
['focus-out-event', (self) => {
|
||||
})
|
||||
.on('focus-out-event', (self) => {
|
||||
buttonDescription.revealChild = false;
|
||||
self.toggleClassName('session-button-focused', false);
|
||||
}],
|
||||
],
|
||||
})
|
||||
,
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
export default () => {
|
||||
// lock, logout, sleep
|
||||
const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('gtklock') });
|
||||
// const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('gtklock') });
|
||||
const lockButton = SessionButton('Lock', 'lock', () => { App.closeWindow('session'); execAsync('swaylock') });
|
||||
const logoutButton = SessionButton('Logout', 'logout', () => { App.closeWindow('session'); execAsync(['bash', '-c', 'loginctl terminate-user $USER']) });
|
||||
const sleepButton = SessionButton('Sleep', 'sleep', () => { App.closeWindow('session'); execAsync('systemctl suspend') });
|
||||
// hibernate, shutdown, reboot
|
||||
@@ -133,10 +138,10 @@ export default () => {
|
||||
]
|
||||
})
|
||||
],
|
||||
connections: [
|
||||
[App, (_b, name, visible) => {
|
||||
setup: (self) => self
|
||||
.hook(App, (_b, name, visible) => {
|
||||
if (visible) lockButton.grab_focus(); // Lock is the default option
|
||||
}],
|
||||
],
|
||||
})
|
||||
,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
const { Gdk, GLib, Gtk, Pango } = imports.gi;
|
||||
import { App, Utils, Widget } from '../../../imports.js';
|
||||
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 ChatGPT from '../../../services/chatgpt.js';
|
||||
@@ -8,7 +11,7 @@ import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover
|
||||
import { SystemMessage, ChatMessage } from "./chatgpt_chatmessage.js";
|
||||
import { ConfigToggle, ConfigSegmentedSelection, ConfigGap } from '../../../lib/configwidgets.js';
|
||||
import { markdownTest } from '../../../lib/md2pango.js';
|
||||
import { MarginRevealer } from '../../../lib/advancedrevealers.js';
|
||||
import { MarginRevealer } from '../../../lib/advancedwidgets.js';
|
||||
|
||||
export const chatGPTTabIcon = Box({
|
||||
hpack: 'center',
|
||||
@@ -64,14 +67,14 @@ const chatGPTInfo = Box({
|
||||
export const chatGPTSettings = MarginRevealer({
|
||||
transition: 'slide_down',
|
||||
revealChild: true,
|
||||
connections: [
|
||||
[ChatGPT, (self) => Utils.timeout(200, () => {
|
||||
self._hide(self);
|
||||
}), 'newMsg'],
|
||||
[ChatGPT, (self) => Utils.timeout(200, () => {
|
||||
self._show(self);
|
||||
}), 'clear'],
|
||||
],
|
||||
extraSetup: (self) => self
|
||||
.hook(ChatGPT, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.hide();
|
||||
}), 'newMsg')
|
||||
.hook(ChatGPT, (self) => Utils.timeout(200, () => {
|
||||
self.attribute.show();
|
||||
}), 'clear')
|
||||
,
|
||||
child: Box({
|
||||
vertical: true,
|
||||
className: 'sidebar-chat-settings',
|
||||
@@ -126,9 +129,11 @@ export const openaiApiKeyInstructions = Box({
|
||||
children: [Revealer({
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 150,
|
||||
connections: [[ChatGPT, (self, hasKey) => {
|
||||
self.revealChild = (ChatGPT.key.length == 0);
|
||||
}, 'hasKey']],
|
||||
setup: (self) => self
|
||||
.hook(ChatGPT, (self, hasKey) => {
|
||||
self.revealChild = (ChatGPT.key.length == 0);
|
||||
}, 'hasKey')
|
||||
,
|
||||
child: Button({
|
||||
child: Label({
|
||||
useMarkup: true,
|
||||
@@ -163,13 +168,13 @@ export const chatGPTWelcome = Box({
|
||||
export const chatContent = Box({
|
||||
className: 'spacing-v-15',
|
||||
vertical: true,
|
||||
connections: [
|
||||
[ChatGPT, (box, id) => {
|
||||
setup: (self) => self
|
||||
.hook(ChatGPT, (box, id) => {
|
||||
const message = ChatGPT.messages[id];
|
||||
if (!message) return;
|
||||
box.add(ChatMessage(message, chatGPTView))
|
||||
}, 'newMsg'],
|
||||
]
|
||||
}, 'newMsg')
|
||||
,
|
||||
});
|
||||
|
||||
const clearChat = () => {
|
||||
@@ -197,46 +202,36 @@ export const chatGPTView = Scrollable({
|
||||
const vScrollbar = scrolledWindow.get_vscrollbar();
|
||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
||||
// Avoid click-to-scroll-widget-to-view behavior
|
||||
Utils.timeout(1, () => {
|
||||
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 chatGPTCommands = Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
Box({ hexpand: true }),
|
||||
Button({
|
||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
||||
onClicked: () => chatContent.add(SystemMessage(
|
||||
`Key stored in:\n\`${ChatGPT.keyPath}\`\nTo update this key, type \`/key YOUR_API_KEY\``,
|
||||
'/key',
|
||||
chatGPTView)),
|
||||
setup: setupCursorHover,
|
||||
label: '/key',
|
||||
}),
|
||||
Button({
|
||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
||||
onClicked: () => chatContent.add(SystemMessage(
|
||||
`Currently using \`${ChatGPT.modelName}\``,
|
||||
'/model',
|
||||
chatGPTView
|
||||
)),
|
||||
setup: setupCursorHover,
|
||||
label: '/model',
|
||||
}),
|
||||
Button({
|
||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
||||
onClicked: () => clearChat(),
|
||||
setup: setupCursorHover,
|
||||
label: '/clear',
|
||||
}),
|
||||
CommandButton('/key'),
|
||||
CommandButton('/model'),
|
||||
CommandButton('/clear'),
|
||||
]
|
||||
});
|
||||
|
||||
export const chatGPTSendMessage = (text) => {
|
||||
export const sendMessage = (text) => {
|
||||
// Check if text or API key is empty
|
||||
if (text.length == 0) return;
|
||||
if (ChatGPT.key.length == 0) {
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
const { Gdk, Gio, GLib, Gtk, Pango } = imports.gi;
|
||||
import { App, Utils, Widget } from '../../../imports.js';
|
||||
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { Gdk, Gio, GLib, Gtk } = imports.gi;
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { Box, Button, Label, Scrollable } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { MaterialIcon } from "../../../lib/materialicon.js";
|
||||
import { convert } from "../../../lib/md2pango.js";
|
||||
import md2pango from "../../../lib/md2pango.js";
|
||||
import GtkSource from "gi://GtkSource?version=3.0";
|
||||
|
||||
const CUSTOM_SOURCEVIEW_SCHEME_PATH = `${App.configDir}/data/sourceviewtheme.xml`;
|
||||
const CUSTOM_SCHEME_ID = 'custom';
|
||||
const USERNAME = GLib.get_user_name();
|
||||
const CHATGPT_CURSOR = ' (o) ';
|
||||
const MESSAGE_SCROLL_DELAY = 13; // In milliseconds, the time before an updated message scrolls to bottom
|
||||
|
||||
/////////////////////// Custom source view colorscheme /////////////////////////
|
||||
|
||||
@@ -102,8 +103,8 @@ const CodeBlock = (content = '', lang = 'txt') => {
|
||||
]
|
||||
}),
|
||||
onClicked: (self) => {
|
||||
const copyContent = sourceView.get_buffer().get_text(0, 0, 0); // TODO: fix this
|
||||
console.log(copyContent);
|
||||
const buffer = sourceView.get_buffer();
|
||||
const copyContent = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), false); // TODO: fix this
|
||||
execAsync([`wl-copy`, `${copyContent}`]).catch(print);
|
||||
},
|
||||
}),
|
||||
@@ -113,11 +114,11 @@ const CodeBlock = (content = '', lang = 'txt') => {
|
||||
const sourceView = HighlightedCode(content, lang);
|
||||
|
||||
const codeBlock = Box({
|
||||
properties: [
|
||||
['updateText', (text) => {
|
||||
attribute: {
|
||||
'updateText': (text) => {
|
||||
sourceView.get_buffer().set_text(text, -1);
|
||||
}]
|
||||
],
|
||||
}
|
||||
},
|
||||
className: 'sidebar-chat-codeblock',
|
||||
vertical: true,
|
||||
children: [
|
||||
@@ -125,7 +126,11 @@ const CodeBlock = (content = '', lang = 'txt') => {
|
||||
Box({
|
||||
className: 'sidebar-chat-codeblock-code',
|
||||
homogeneous: true,
|
||||
children: [sourceView,],
|
||||
children: [Scrollable({
|
||||
vscroll: 'never',
|
||||
hscroll: 'automatic',
|
||||
child: sourceView,
|
||||
})],
|
||||
})
|
||||
]
|
||||
})
|
||||
@@ -146,8 +151,8 @@ const Divider = () => Box({
|
||||
const MessageContent = (content) => {
|
||||
const contentBox = Box({
|
||||
vertical: true,
|
||||
properties: [
|
||||
['fullUpdate', (self, content, useCursor = false) => {
|
||||
attribute: {
|
||||
'fullUpdate': (self, content, useCursor = false) => {
|
||||
// Clear and add first text widget
|
||||
const children = contentBox.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
@@ -164,16 +169,15 @@ const MessageContent = (content) => {
|
||||
// Code blocks
|
||||
const codeBlockRegex = /^\s*```([a-zA-Z0-9]+)?\n?/;
|
||||
if (codeBlockRegex.test(line)) {
|
||||
// console.log(`code at line ${index}`);
|
||||
const kids = self.get_children();
|
||||
const lastLabel = kids[kids.length - 1];
|
||||
const blockContent = lines.slice(lastProcessed, index).join('\n');
|
||||
if (!inCode) {
|
||||
lastLabel.label = convert(blockContent);
|
||||
lastLabel.label = md2pango(blockContent);
|
||||
contentBox.add(CodeBlock('', codeBlockRegex.exec(line)[1]));
|
||||
}
|
||||
else {
|
||||
lastLabel._updateText(blockContent);
|
||||
lastLabel.attribute.updateText(blockContent);
|
||||
contentBox.add(TextBlock());
|
||||
}
|
||||
|
||||
@@ -186,7 +190,7 @@ const MessageContent = (content) => {
|
||||
const kids = self.get_children();
|
||||
const lastLabel = kids[kids.length - 1];
|
||||
const blockContent = lines.slice(lastProcessed, index).join('\n');
|
||||
lastLabel.label = convert(blockContent);
|
||||
lastLabel.label = md2pango(blockContent);
|
||||
contentBox.add(Divider());
|
||||
contentBox.add(TextBlock());
|
||||
lastProcessed = index + 1;
|
||||
@@ -197,9 +201,9 @@ const MessageContent = (content) => {
|
||||
const lastLabel = kids[kids.length - 1];
|
||||
let blockContent = lines.slice(lastProcessed, lines.length).join('\n');
|
||||
if (!inCode)
|
||||
lastLabel.label = `${convert(blockContent)}${useCursor ? CHATGPT_CURSOR : ''}`;
|
||||
lastLabel.label = `${md2pango(blockContent)}${useCursor ? CHATGPT_CURSOR : ''}`;
|
||||
else
|
||||
lastLabel._updateText(blockContent);
|
||||
lastLabel.attribute.updateText(blockContent);
|
||||
}
|
||||
// Debug: plain text
|
||||
// contentBox.add(Label({
|
||||
@@ -209,13 +213,13 @@ const MessageContent = (content) => {
|
||||
// xalign: 0,
|
||||
// wrap: true,
|
||||
// selectable: true,
|
||||
// label: '------------------------------\n' + convert(content),
|
||||
// label: '------------------------------\n' + md2pango(content),
|
||||
// }))
|
||||
contentBox.show_all();
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
contentBox._fullUpdate(contentBox, content, false);
|
||||
contentBox.attribute.fullUpdate(contentBox, content, false);
|
||||
return contentBox;
|
||||
}
|
||||
|
||||
@@ -241,22 +245,17 @@ export const ChatMessage = (message, scrolledWindow) => {
|
||||
}),
|
||||
messageContentBox,
|
||||
],
|
||||
connections: [
|
||||
[message, (self, isThinking) => {
|
||||
setup: (self) => self
|
||||
.hook(message, (self, isThinking) => {
|
||||
messageContentBox.toggleClassName('thinking', message.thinking);
|
||||
}, 'notify::thinking'],
|
||||
[message, (self) => { // Message update
|
||||
messageContentBox._fullUpdate(messageContentBox, message.content, message.role != 'user');
|
||||
Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
|
||||
if (!scrolledWindow) return;
|
||||
var adjustment = scrolledWindow.get_vadjustment();
|
||||
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
|
||||
});
|
||||
}, 'notify::content'],
|
||||
[message, (label, isDone) => { // Remove the cursor
|
||||
messageContentBox._fullUpdate(messageContentBox, message.content, false);
|
||||
}, 'notify::done'],
|
||||
]
|
||||
}, 'notify::thinking')
|
||||
.hook(message, (self) => { // Message update
|
||||
messageContentBox.attribute.fullUpdate(messageContentBox, message.content, message.role != 'user');
|
||||
}, 'notify::content')
|
||||
.hook(message, (label, isDone) => { // Remove the cursor
|
||||
messageContentBox.attribute.fullUpdate(messageContentBox, message.content, false);
|
||||
}, 'notify::done')
|
||||
,
|
||||
})
|
||||
]
|
||||
});
|
||||
@@ -286,11 +285,6 @@ export const SystemMessage = (content, commandName, scrolledWindow) => {
|
||||
],
|
||||
})
|
||||
],
|
||||
setup: (self) => Utils.timeout(MESSAGE_SCROLL_DELAY, () => {
|
||||
if (!scrolledWindow) return;
|
||||
var adjustment = scrolledWindow.get_vadjustment();
|
||||
adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size());
|
||||
})
|
||||
});
|
||||
return thisMessage;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,41 @@
|
||||
const { Gdk, GLib, Gtk, Pango } = imports.gi;
|
||||
import { App, Utils, Widget } from '../../../imports.js';
|
||||
const { Box, Button, Entry, EventBox, Icon, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { 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';
|
||||
const { Box, Button, Label, Overlay, Revealer, Scrollable, Stack } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { MaterialIcon } from "../../../lib/materialicon.js";
|
||||
import { setupCursorHover, setupCursorHoverInfo } from "../../../lib/cursorhover.js";
|
||||
import { MarginRevealer } from '../../../lib/advancedwidgets.js';
|
||||
import { setupCursorHover } from "../../../lib/cursorhover.js";
|
||||
import WaifuService from '../../../services/waifus.js';
|
||||
|
||||
async function getImageViewerApp(preferredApp) {
|
||||
Utils.execAsync(['bash', '-c', `command -v ${preferredApp}`])
|
||||
.then((output) => {
|
||||
if (output != '') return preferredApp;
|
||||
else return 'xdg-open';
|
||||
});
|
||||
}
|
||||
|
||||
const IMAGE_REVEAL_DELAY = 13; // Some wait for inits n other weird stuff
|
||||
const IMAGE_VIEWER_APP = getImageViewerApp('loupe'); // Gnome's image viewer cuz very comfortable zooming
|
||||
const USER_CACHE_DIR = GLib.get_user_cache_dir();
|
||||
|
||||
// Create cache folder and clear pics from previous session
|
||||
Utils.exec(`bash -c 'mkdir -p ${USER_CACHE_DIR}/ags/media/waifus'`);
|
||||
Utils.exec(`bash -c 'rm ${USER_CACHE_DIR}/ags/media/waifus/*'`);
|
||||
|
||||
export function fileExists(filePath) {
|
||||
let file = Gio.File.new_for_path(filePath);
|
||||
return file.query_exists(null);
|
||||
}
|
||||
|
||||
const CommandButton = (command) => Button({
|
||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
||||
onClicked: () => sendMessage(command),
|
||||
setup: setupCursorHover,
|
||||
label: command,
|
||||
});
|
||||
|
||||
export const waifuTabIcon = Box({
|
||||
hpack: 'center',
|
||||
className: 'sidebar-chat-apiswitcher-icon',
|
||||
@@ -15,18 +45,204 @@ export const waifuTabIcon = Box({
|
||||
]
|
||||
});
|
||||
|
||||
const WaifuImage = (taglist) => {
|
||||
const ImageState = (icon, name) => Box({
|
||||
className: 'spacing-h-5 txt',
|
||||
children: [
|
||||
Box({ hexpand: true }),
|
||||
Label({
|
||||
className: 'sidebar-waifu-txt txt-smallie',
|
||||
xalign: 0,
|
||||
label: name,
|
||||
}),
|
||||
MaterialIcon(icon, 'norm'),
|
||||
]
|
||||
})
|
||||
const ImageAction = ({ name, icon, action }) => Button({
|
||||
className: 'sidebar-waifu-image-action txt-norm icon-material',
|
||||
tooltipText: name,
|
||||
label: icon,
|
||||
onClicked: action,
|
||||
setup: setupCursorHover,
|
||||
})
|
||||
const colorIndicator = Box({
|
||||
className: `sidebar-chat-indicator`,
|
||||
});
|
||||
const downloadState = Stack({
|
||||
homogeneous: false,
|
||||
transition: 'slide_up_down',
|
||||
transitionDuration: 150,
|
||||
items: [
|
||||
['api', ImageState('api', 'Calling API')],
|
||||
['download', ImageState('downloading', 'Downloading image')],
|
||||
['done', ImageState('done', 'Finished!')],
|
||||
['error', ImageState('error', 'Error')],
|
||||
]
|
||||
});
|
||||
const downloadIndicator = MarginRevealer({
|
||||
vpack: 'center',
|
||||
transition: 'slide_left',
|
||||
revealChild: true,
|
||||
child: downloadState,
|
||||
});
|
||||
const blockHeading = Box({
|
||||
hpack: 'fill',
|
||||
className: 'sidebar-waifu-content spacing-h-5',
|
||||
children: [
|
||||
...taglist.map((tag) => CommandButton(tag)),
|
||||
Box({ hexpand: true }),
|
||||
downloadIndicator,
|
||||
]
|
||||
});
|
||||
const blockImageActions = Revealer({
|
||||
transition: 'crossfade',
|
||||
revealChild: false,
|
||||
child: Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Box({
|
||||
className: 'sidebar-waifu-image-actions spacing-h-3',
|
||||
children: [
|
||||
Box({ hexpand: true }),
|
||||
ImageAction({
|
||||
name: 'Go to source',
|
||||
icon: 'link',
|
||||
action: () => execAsync(['xdg-open', `${thisBlock.attribute.imageData.source}`]).catch(print),
|
||||
}),
|
||||
ImageAction({
|
||||
name: 'Hoard',
|
||||
icon: 'save',
|
||||
action: () => execAsync(['bash', '-c', `mkdir -p ~/Pictures/homework${thisBlock.attribute.isNsfw ? '/🌶️' : ''} && cp ${thisBlock.attribute.imagePath} ~/Pictures/homework${thisBlock.attribute.isNsfw ? '/🌶️/' : ''}`]).catch(print),
|
||||
}),
|
||||
ImageAction({
|
||||
name: 'Open externally',
|
||||
icon: 'open_in_new',
|
||||
action: () => execAsync([IMAGE_VIEWER_APP, `${thisBlock.attribute.imagePath}`]).catch(print),
|
||||
}),
|
||||
]
|
||||
})
|
||||
],
|
||||
})
|
||||
})
|
||||
const blockImage = Widget.DrawingArea({
|
||||
className: 'sidebar-waifu-image',
|
||||
});
|
||||
const blockImageRevealer = Revealer({
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 150,
|
||||
revealChild: false,
|
||||
child: Overlay({
|
||||
child: Box({
|
||||
homogeneous: true,
|
||||
className: 'sidebar-waifu-image',
|
||||
children: [blockImage],
|
||||
}),
|
||||
overlays: [blockImageActions],
|
||||
}),
|
||||
});
|
||||
const thisBlock = Box({
|
||||
className: 'sidebar-chat-message',
|
||||
attribute: {
|
||||
'imagePath': '',
|
||||
'isNsfw': false,
|
||||
'imageData': '',
|
||||
'update': (imageData, force = false) => {
|
||||
thisBlock.attribute.imageData = imageData;
|
||||
const { status, signature, url, extension, source, dominant_color, is_nsfw, width, height, tags } = thisBlock.attribute.imageData;
|
||||
thisBlock.attribute.isNsfw = is_nsfw;
|
||||
if (status != 200) {
|
||||
downloadState.shown = 'error';
|
||||
return;
|
||||
}
|
||||
thisBlock.attribute.imagePath = `${USER_CACHE_DIR}/ags/media/waifus/${signature}${extension}`;
|
||||
downloadState.shown = 'download';
|
||||
// Width/height
|
||||
const widgetWidth = Math.min(Math.floor(waifuContent.get_allocated_width() * 0.85), width);
|
||||
const widgetHeight = Math.ceil(widgetWidth * height / width);
|
||||
blockImage.set_size_request(widgetWidth, widgetHeight);
|
||||
const showImage = () => {
|
||||
downloadState.shown = 'done';
|
||||
const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(thisBlock.attribute.imagePath, widgetWidth, widgetHeight, false);
|
||||
|
||||
blockImage.set_size_request(widgetWidth, widgetHeight);
|
||||
blockImage.connect("draw", (widget, cr) => {
|
||||
const borderRadius = widget.get_style_context().get_property('border-radius', Gtk.StateFlags.NORMAL);
|
||||
|
||||
// Draw a rounded rectangle
|
||||
cr.arc(borderRadius, borderRadius, borderRadius, Math.PI, 1.5 * Math.PI);
|
||||
cr.arc(widgetWidth - borderRadius, borderRadius, borderRadius, 1.5 * Math.PI, 2 * Math.PI);
|
||||
cr.arc(widgetWidth - borderRadius, widgetHeight - borderRadius, borderRadius, 0, 0.5 * Math.PI);
|
||||
cr.arc(borderRadius, widgetHeight - borderRadius, borderRadius, 0.5 * Math.PI, Math.PI);
|
||||
cr.closePath();
|
||||
cr.clip();
|
||||
|
||||
// Paint image as bg
|
||||
Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
|
||||
cr.paint();
|
||||
});
|
||||
|
||||
// Reveal stuff
|
||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => {
|
||||
blockImageRevealer.revealChild = true;
|
||||
})
|
||||
Utils.timeout(IMAGE_REVEAL_DELAY + blockImageRevealer.transitionDuration,
|
||||
() => blockImageActions.revealChild = true
|
||||
);
|
||||
downloadIndicator.attribute.hide();
|
||||
}
|
||||
// Show
|
||||
if (!force && fileExists(thisBlock.attribute.imagePath)) showImage();
|
||||
else Utils.execAsync(['bash', '-c', `wget -O '${thisBlock.attribute.imagePath}' '${url}'`])
|
||||
.then(showImage)
|
||||
.catch(print);
|
||||
blockHeading.get_children().forEach((child) => {
|
||||
child.setCss(`border-color: ${dominant_color};`);
|
||||
})
|
||||
colorIndicator.css = `background-color: ${dominant_color};`;
|
||||
},
|
||||
},
|
||||
children: [
|
||||
colorIndicator,
|
||||
Box({
|
||||
vertical: true,
|
||||
className: 'spacing-v-5',
|
||||
children: [
|
||||
blockHeading,
|
||||
Box({
|
||||
vertical: true,
|
||||
hpack: 'start',
|
||||
children: [blockImageRevealer],
|
||||
})
|
||||
]
|
||||
})
|
||||
],
|
||||
});
|
||||
return thisBlock;
|
||||
}
|
||||
|
||||
const waifuContent = Box({
|
||||
className: 'spacing-v-15',
|
||||
vertical: true,
|
||||
connections: [
|
||||
[WaifuService, (box, id) => {
|
||||
const message = WaifuService.responses[id];
|
||||
if (!message) return;
|
||||
box.add(Label({
|
||||
label: message,
|
||||
}))
|
||||
}, 'newResponse'],
|
||||
]
|
||||
vexpand: true,
|
||||
attribute: {
|
||||
'map': new Map(),
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(WaifuService, (box, id) => {
|
||||
if (id === undefined) return;
|
||||
const newImageBlock = WaifuImage(WaifuService.queries[id]);
|
||||
box.add(newImageBlock);
|
||||
box.show_all();
|
||||
box.attribute.map.set(id, newImageBlock);
|
||||
}, 'newResponse')
|
||||
.hook(WaifuService, (box, id) => {
|
||||
if (id === undefined) return;
|
||||
const data = WaifuService.responses[id];
|
||||
if (!data) return;
|
||||
const imageBlock = box.attribute.map.get(id);
|
||||
imageBlock.attribute.update(data);
|
||||
}, 'updateResponse')
|
||||
,
|
||||
});
|
||||
|
||||
export const waifuView = Scrollable({
|
||||
@@ -48,25 +264,91 @@ export const waifuView = Scrollable({
|
||||
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 waifuTags = Revealer({
|
||||
revealChild: false,
|
||||
transition: 'crossfade',
|
||||
transitionDuration: 150,
|
||||
child: Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
Scrollable({
|
||||
vscroll: 'never',
|
||||
hscroll: 'automatic',
|
||||
hexpand: true,
|
||||
child: Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
CommandButton('waifu'),
|
||||
CommandButton('maid'),
|
||||
CommandButton('uniform'),
|
||||
CommandButton('oppai'),
|
||||
CommandButton('selfies'),
|
||||
CommandButton('marin-kitagawa'),
|
||||
CommandButton('raiden-shogun'),
|
||||
CommandButton('mori-calliope'),
|
||||
]
|
||||
})
|
||||
}),
|
||||
Box({ className: 'separator-line' }),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
export const waifuCommands = Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
Box({ hexpand: true }),
|
||||
Button({
|
||||
className: 'sidebar-chat-chip sidebar-chat-chip-action txt txt-small',
|
||||
onClicked: () => {
|
||||
// command do something
|
||||
},
|
||||
setup: (self) => {
|
||||
self.pack_end(CommandButton('/clear'), false, false, 0);
|
||||
self.pack_start(Button({
|
||||
className: 'sidebar-chat-chip-toggle',
|
||||
setup: setupCursorHover,
|
||||
label: '/call',
|
||||
}),
|
||||
]
|
||||
label: 'Tags →',
|
||||
onClicked: () => {
|
||||
waifuTags.revealChild = !waifuTags.revealChild;
|
||||
}
|
||||
}), false, false, 0);
|
||||
self.pack_start(waifuTags, true, true, 0);
|
||||
}
|
||||
});
|
||||
|
||||
export const waifuCallAPI = (text) => {
|
||||
const clearChat = () => {
|
||||
waifuContent.attribute.map.clear();
|
||||
const kids = waifuContent.get_children();
|
||||
for (let i = 0; i < kids.length; i++) {
|
||||
const child = kids[i];
|
||||
if (child) child.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export const sendMessage = (text) => {
|
||||
// Do something on send
|
||||
WaifuService.fetch(text);
|
||||
// Commands
|
||||
if (text.startsWith('/')) {
|
||||
if (text.startsWith('/clear')) clearChat();
|
||||
else if (text.startsWith('/test')) {
|
||||
const newImage = WaifuImage(['/test']);
|
||||
waifuContent.add(newImage);
|
||||
Utils.timeout(IMAGE_REVEAL_DELAY, () => newImage.attribute.update({ // Needs timeout or inits won't make it
|
||||
// This is an image uploaded to my github repo
|
||||
status: 200,
|
||||
url: 'https://picsum.photos/400/600',
|
||||
extension: '',
|
||||
signature: 0,
|
||||
source: 'https://picsum.photos/400/600',
|
||||
dominant_color: '#9392A6',
|
||||
is_nsfw: false,
|
||||
width: 300,
|
||||
height: 200,
|
||||
tags: ['/test'],
|
||||
}, true));
|
||||
}
|
||||
}
|
||||
else WaifuService.fetch(text);
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
const { Gtk, Gdk } = imports.gi;
|
||||
import { App, Utils, Widget } from '../../imports.js';
|
||||
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 { setupCursorHover, setupCursorHoverInfo } from "../../lib/cursorhover.js";
|
||||
// APIs
|
||||
import ChatGPT from '../../services/chatgpt.js';
|
||||
import { chatGPTView, chatGPTCommands, chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
|
||||
import { waifuView, waifuCommands, waifuCallAPI, waifuTabIcon } from './apis/waifu.js';
|
||||
import { chatGPTView, chatGPTCommands, sendMessage as chatGPTSendMessage, chatGPTTabIcon } from './apis/chatgpt.js';
|
||||
import { waifuView, waifuCommands, sendMessage as waifuSendMessage, waifuTabIcon } from './apis/waifu.js';
|
||||
|
||||
const APIS = [
|
||||
{
|
||||
@@ -19,7 +20,7 @@ const APIS = [
|
||||
},
|
||||
{
|
||||
name: 'Waifus',
|
||||
sendCommand: waifuCallAPI,
|
||||
sendCommand: waifuSendMessage,
|
||||
contentWidget: waifuView,
|
||||
commandBar: waifuCommands,
|
||||
tabIcon: waifuTabIcon,
|
||||
@@ -32,12 +33,12 @@ APIS[currentApiId].tabIcon.toggleClassName('sidebar-chat-apiswitcher-icon-enable
|
||||
export const chatEntry = Entry({
|
||||
className: 'sidebar-chat-entry',
|
||||
hexpand: true,
|
||||
connections: [
|
||||
[ChatGPT, (self) => {
|
||||
setup: (self) => self
|
||||
.hook(ChatGPT, (self) => {
|
||||
if (APIS[currentApiId].name != 'ChatGPT') return;
|
||||
self.placeholderText = (ChatGPT.key.length > 0 ? 'Ask a question...' : 'Enter OpenAI API Key...');
|
||||
}, 'hasKey']
|
||||
],
|
||||
}, 'hasKey')
|
||||
,
|
||||
onChange: (entry) => {
|
||||
chatSendButton.toggleClassName('sidebar-chat-send-available', entry.text.length > 0);
|
||||
},
|
||||
@@ -83,7 +84,7 @@ function switchToTab(id) {
|
||||
apiContentStack.shown = APIS[id].name;
|
||||
apiCommandStack.shown = APIS[id].name;
|
||||
chatEntry.placeholderText = APIS[id].placeholderText,
|
||||
currentApiId = id;
|
||||
currentApiId = id;
|
||||
}
|
||||
const apiSwitcher = Box({
|
||||
homogeneous: true,
|
||||
@@ -104,10 +105,10 @@ const apiSwitcher = Box({
|
||||
})
|
||||
|
||||
export default Widget.Box({
|
||||
properties: [
|
||||
['nextTab', () => switchToTab(Math.min(currentApiId + 1, APIS.length - 1))],
|
||||
['prevTab', () => switchToTab(Math.max(0, currentApiId-1))],
|
||||
],
|
||||
attribute: {
|
||||
'nextTab': () => switchToTab(Math.min(currentApiId + 1, APIS.length - 1)),
|
||||
'prevTab': () => switchToTab(Math.max(0, currentApiId - 1)),
|
||||
},
|
||||
vertical: true,
|
||||
className: 'spacing-v-10',
|
||||
homogeneous: false,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { Utils, Widget } from '../../imports.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, Button, EventBox, Label, Scrollable } = Widget;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
const { Box, Button, Label } = Widget;
|
||||
|
||||
export const SidebarModule = ({
|
||||
name,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { Utils, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, Button, EventBox, Label, Scrollable } = Widget;
|
||||
import { SidebarModule } from './module.js';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { Utils, Widget } from '../../imports.js';
|
||||
const { Gdk } = 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, EventBox, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { MaterialIcon } from "../../lib/materialicon.js";
|
||||
@@ -93,20 +95,20 @@ const navBar = Box({
|
||||
});
|
||||
|
||||
const pinButton = Button({
|
||||
properties: [
|
||||
['enabled', false],
|
||||
['toggle', (self) => {
|
||||
self._enabled = !self._enabled;
|
||||
self.toggleClassName('sidebar-pin-enabled', self._enabled);
|
||||
attribute: {
|
||||
'enabled': false,
|
||||
'toggle': (self) => {
|
||||
self.attribute.enabled = !self.attribute.enabled;
|
||||
self.toggleClassName('sidebar-pin-enabled', self.attribute.enabled);
|
||||
|
||||
const sideleftWindow = App.getWindow('sideleft');
|
||||
const barWindow = App.getWindow('bar');
|
||||
const cornerTopLeftWindow = App.getWindow('cornertl');
|
||||
const sideleftContent = sideleftWindow.get_children()[0].get_children()[0].get_children()[1];
|
||||
|
||||
sideleftContent.toggleClassName('sidebar-pinned', self._enabled);
|
||||
sideleftContent.toggleClassName('sidebar-pinned', self.attribute.enabled);
|
||||
|
||||
if (self._enabled) {
|
||||
if (self.attribute.enabled) {
|
||||
sideleftWindow.layer = 'bottom';
|
||||
barWindow.layer = 'bottom';
|
||||
cornerTopLeftWindow.layer = 'bottom';
|
||||
@@ -118,19 +120,20 @@ const pinButton = Button({
|
||||
cornerTopLeftWindow.layer = 'top';
|
||||
sideleftWindow.exclusivity = 'normal';
|
||||
}
|
||||
}],
|
||||
],
|
||||
},
|
||||
},
|
||||
vpack: 'start',
|
||||
className: 'sidebar-pin',
|
||||
child: MaterialIcon('push_pin', 'larger'),
|
||||
tooltipText: 'Pin sidebar',
|
||||
onClicked: (self) => self._toggle(self),
|
||||
onClicked: (self) => self.attribute.toggle(self),
|
||||
// QoL: Focus Pin button on open. Hit keybind -> space/enter = toggle pin state
|
||||
connections: [[App, (self, currentName, visible) => {
|
||||
if (currentName === 'sideleft' && visible) {
|
||||
self.grab_focus();
|
||||
}
|
||||
}]]
|
||||
setup: (self) => self
|
||||
.hook(App, (self, currentName, visible) => {
|
||||
if (currentName === 'sideleft' && visible)
|
||||
self.grab_focus();
|
||||
})
|
||||
,
|
||||
})
|
||||
|
||||
export default () => Box({
|
||||
@@ -158,26 +161,27 @@ export default () => Box({
|
||||
}),
|
||||
contentStack,
|
||||
],
|
||||
connections: [[App, (self, currentName, visible) => {
|
||||
if (currentName === 'sideleft') {
|
||||
self.toggleClassName('sidebar-pinned', pinButton._enabled && visible);
|
||||
}
|
||||
}]]
|
||||
setup: (self) => self
|
||||
.hook(App, (self, currentName, visible) => {
|
||||
if (currentName === 'sideleft')
|
||||
self.toggleClassName('sidebar-pinned', pinButton.attribute.enabled && visible);
|
||||
})
|
||||
,
|
||||
}),
|
||||
],
|
||||
connections: [
|
||||
['key-press-event', (widget, event) => { // Handle keybinds
|
||||
setup: (self) => self
|
||||
.on('key-press-event', (widget, event) => { // Handle keybinds
|
||||
if (event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) {
|
||||
// Pin sidebar
|
||||
if (event.get_keyval()[1] == Gdk.KEY_p)
|
||||
pinButton._toggle(pinButton);
|
||||
pinButton.attribute.toggle(pinButton);
|
||||
// Switch sidebar tab
|
||||
else if (event.get_keyval()[1] === Gdk.KEY_Tab)
|
||||
switchToTab((currentTabId + 1) % contents.length);
|
||||
else if (event.get_keyval()[1] === Gdk.KEY_Page_Up)
|
||||
switchToTab(Math.max(currentTabId - 1), 0);
|
||||
switchToTab(Math.max(currentTabId - 1, 0));
|
||||
else if (event.get_keyval()[1] === Gdk.KEY_Page_Down)
|
||||
switchToTab(Math.min(currentTabId + 1), contents.length);
|
||||
switchToTab(Math.min(currentTabId + 1, contents.length - 1));
|
||||
}
|
||||
if (contentStack.shown == 'apis') { // If api tab is focused
|
||||
// Automatically focus entry when typing
|
||||
@@ -186,8 +190,8 @@ export default () => Box({
|
||||
event.get_keyval()[1] >= 32 && event.get_keyval()[1] <= 126 &&
|
||||
widget != chatEntry && event.get_keyval()[1] != Gdk.KEY_space)
|
||||
||
|
||||
((event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
|
||||
event.get_keyval()[1] === Gdk.KEY_v)
|
||||
((event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
|
||||
event.get_keyval()[1] === Gdk.KEY_v)
|
||||
) {
|
||||
chatEntry.grab_focus();
|
||||
chatEntry.set_text(chatEntry.text + String.fromCharCode(event.get_keyval()[1]));
|
||||
@@ -197,15 +201,15 @@ export default () => Box({
|
||||
else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
|
||||
event.get_keyval()[1] === Gdk.KEY_Page_Down) {
|
||||
const toSwitchTab = contentStack.get_visible_child();
|
||||
toSwitchTab._nextTab();
|
||||
toSwitchTab.attribute.nextTab();
|
||||
}
|
||||
else if (!(event.get_state()[1] & Gdk.ModifierType.CONTROL_MASK) &&
|
||||
event.get_keyval()[1] === Gdk.KEY_Page_Up) {
|
||||
const toSwitchTab = contentStack.get_visible_child();
|
||||
toSwitchTab._prevTab();
|
||||
toSwitchTab.attribute.prevTab();
|
||||
}
|
||||
}
|
||||
|
||||
}],
|
||||
],
|
||||
})
|
||||
,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { Gdk, Gtk } = imports.gi;
|
||||
import { Utils, Widget } from '../../imports.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, EventBox, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { QuickScripts } from './quickscripts.js';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { Gio, Gdk, GLib, Gtk } = imports.gi;
|
||||
import { App, Widget, Utils } from '../../imports.js';
|
||||
const { Box, Button, CenterBox, Label, Revealer } = Widget;
|
||||
const { Gio } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { Box, Button, Label } = Widget;
|
||||
import { MaterialIcon } from "../../lib/materialicon.js";
|
||||
import { getCalendarLayout } from "../../lib/calendarlayout.js";
|
||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||
|
||||
@@ -1,113 +1,137 @@
|
||||
// This file is for the notification widget on the sidebar
|
||||
// This file is for the notification list on the sidebar
|
||||
// For the popup notifications, see onscreendisplay.js
|
||||
// The actual widget for each single notification is in lib/notification.js
|
||||
|
||||
const { GLib, Gtk } = imports.gi;
|
||||
import { Service, Utils, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
|
||||
const { lookUpIcon, timeout } = Utils;
|
||||
const { Box, Icon, Scrollable, Label, Button, Revealer } = Widget;
|
||||
const { Box, Button, Label, Scrollable, Stack } = Widget;
|
||||
import { MaterialIcon } from "../../lib/materialicon.js";
|
||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||
import Notification from "../../lib/notification.js";
|
||||
|
||||
const NotificationList = Box({
|
||||
vertical: true,
|
||||
vpack: 'start',
|
||||
className: 'spacing-v-5-revealer',
|
||||
connections: [
|
||||
[Notifications, (box, id) => {
|
||||
if (box.children.length == 0) {
|
||||
Notifications.notifications
|
||||
.forEach(n => {
|
||||
box.pack_end(Notification({
|
||||
notifObject: n,
|
||||
isPopup: false,
|
||||
}), false, false, 0)
|
||||
});
|
||||
box.show_all();
|
||||
}
|
||||
else if (id) {
|
||||
export default (props) => {
|
||||
const notifEmptyContent = Box({
|
||||
homogeneous: true,
|
||||
children: [Box({
|
||||
vertical: true,
|
||||
vpack: 'center',
|
||||
className: 'txt spacing-v-10',
|
||||
children: [
|
||||
Box({
|
||||
vertical: true,
|
||||
className: 'spacing-v-5',
|
||||
children: [
|
||||
MaterialIcon('notifications_active', 'badonkers'),
|
||||
Label({ label: 'No notifications', className: 'txt-small' }),
|
||||
]
|
||||
}),
|
||||
]
|
||||
})]
|
||||
});
|
||||
const notificationList = Box({
|
||||
vertical: true,
|
||||
vpack: 'start',
|
||||
className: 'spacing-v-5-revealer',
|
||||
setup: (self) => self
|
||||
.hook(Notifications, (box, id) => {
|
||||
if (box.get_children().length == 0) { // On init there's no notif, or 1st notif
|
||||
Notifications.notifications
|
||||
.forEach(n => {
|
||||
box.pack_end(Notification({
|
||||
notifObject: n,
|
||||
isPopup: false,
|
||||
}), false, false, 0)
|
||||
});
|
||||
box.show_all();
|
||||
return;
|
||||
}
|
||||
// 2nd or later notif
|
||||
const notif = Notifications.getNotification(id);
|
||||
|
||||
const NewNotif = Notification({
|
||||
notifObject: notif,
|
||||
isPopup: false,
|
||||
});
|
||||
|
||||
if (NewNotif) {
|
||||
box.pack_end(NewNotif, false, false, 0);
|
||||
box.show_all();
|
||||
}
|
||||
}
|
||||
}, 'notified'],
|
||||
|
||||
[Notifications, (box, id) => {
|
||||
if (!id) return;
|
||||
for (const ch of box.children) {
|
||||
if (ch._id === id) {
|
||||
ch._destroyWithAnims();
|
||||
}, 'notified')
|
||||
.hook(Notifications, (box, id) => {
|
||||
if (!id) return;
|
||||
for (const ch of box.children) {
|
||||
if (ch._id === id) {
|
||||
ch.attribute.destroyWithAnims();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 'closed'],
|
||||
|
||||
[Notifications, box => box.visible = Notifications.notifications.length > 0],
|
||||
],
|
||||
});
|
||||
|
||||
export default (props) => {
|
||||
const listTitle = Revealer({
|
||||
revealChild: false,
|
||||
connections: [[Notifications, (revealer) => {
|
||||
revealer.revealChild = (Notifications.notifications.length > 0);
|
||||
}]],
|
||||
}, 'closed')
|
||||
,
|
||||
});
|
||||
const ListActionButton = (icon, name, action) => Button({
|
||||
className: 'notif-listaction-btn',
|
||||
onClicked: action,
|
||||
child: Box({
|
||||
vpack: 'start',
|
||||
className: 'sidebar-group-invisible txt',
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
MaterialIcon(icon, 'norm'),
|
||||
Label({
|
||||
hexpand: true,
|
||||
xalign: 0,
|
||||
className: 'txt-title-small',
|
||||
label: 'Notifications',
|
||||
}),
|
||||
Button({
|
||||
className: 'notif-closeall-btn',
|
||||
onClicked: () => {
|
||||
Notifications.clear();
|
||||
},
|
||||
child: Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
MaterialIcon('clear_all', 'norm'),
|
||||
Label({
|
||||
className: 'txt-small',
|
||||
label: 'Clear',
|
||||
})
|
||||
]
|
||||
}),
|
||||
setup: button => {
|
||||
setupCursorHover(button);
|
||||
},
|
||||
className: 'txt-small',
|
||||
label: name,
|
||||
})
|
||||
]
|
||||
})
|
||||
}),
|
||||
setup: setupCursorHover,
|
||||
});
|
||||
const listContents = Scrollable({
|
||||
const silenceButton = ListActionButton('notifications_paused', 'Silence', (self) => {
|
||||
Notifications.dnd = !Notifications.dnd;
|
||||
self.toggleClassName('notif-listaction-btn-enabled', Notifications.dnd);
|
||||
});
|
||||
const clearButton = ListActionButton('clear_all', 'Clear', () => {
|
||||
notificationList.get_children().forEach(ch => ch.destroy());
|
||||
Notifications.clear();
|
||||
});
|
||||
const listTitle = Box({
|
||||
vpack: 'start',
|
||||
className: 'sidebar-group-invisible txt spacing-h-5',
|
||||
children: [
|
||||
Label({
|
||||
hexpand: true,
|
||||
xalign: 0,
|
||||
className: 'txt-title-small margin-left-10',
|
||||
// ^ (extra margin on the left so that it looks similarly spaced
|
||||
// when compared to borderless "Clear" button on the right)
|
||||
label: 'Notifications',
|
||||
}),
|
||||
silenceButton,
|
||||
clearButton,
|
||||
]
|
||||
});
|
||||
const notifList = Scrollable({
|
||||
hexpand: true,
|
||||
hscroll: 'never',
|
||||
vscroll: 'automatic',
|
||||
child: Box({
|
||||
vexpand: true,
|
||||
children: [NotificationList],
|
||||
})
|
||||
// homogeneous: true,
|
||||
children: [notificationList],
|
||||
}),
|
||||
setup: (self) => {
|
||||
const vScrollbar = self.get_vscrollbar();
|
||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
||||
}
|
||||
});
|
||||
const listContents = Stack({
|
||||
transition: 'crossfade',
|
||||
transitionDuration: 150,
|
||||
items: [
|
||||
['empty', notifEmptyContent],
|
||||
['list', notifList]
|
||||
],
|
||||
setup: (self) => self
|
||||
.hook(Notifications, (self) => self.shown = (Notifications.notifications.length > 0 ? 'list' : 'empty'))
|
||||
,
|
||||
});
|
||||
listContents.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
const vScrollbar = listContents.get_vscrollbar();
|
||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
||||
return Box({
|
||||
...props,
|
||||
className: 'sidebar-group-invisible spacing-v-5',
|
||||
className: 'sidebar-group spacing-v-5',
|
||||
vertical: true,
|
||||
children: [
|
||||
listTitle,
|
||||
|
||||
@@ -1,28 +1,40 @@
|
||||
import { Widget, Utils, Service } from '../../imports.js';
|
||||
const { GLib } = imports.gi;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
|
||||
import Bluetooth from 'resource:///com/github/Aylur/ags/service/bluetooth.js';
|
||||
import Network from 'resource:///com/github/Aylur/ags/service/network.js';
|
||||
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
import { BluetoothIndicator, NetworkIndicator } from "../../lib/statusicons.js";
|
||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
|
||||
function expandTilde(path) {
|
||||
if (path.startsWith('~')) {
|
||||
console.log(GLib.get_home_dir() + path.slice(1));
|
||||
return GLib.get_home_dir() + path.slice(1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
export const ToggleIconWifi = (props = {}) => Widget.Button({
|
||||
className: 'txt-small sidebar-iconbutton',
|
||||
tooltipText: 'Wifi | Right-click to configure',
|
||||
onClicked: Network.toggleWifi,
|
||||
onClicked: () => Network.toggleWifi(),
|
||||
onSecondaryClickRelease: () => {
|
||||
execAsync(['bash', '-c', 'XDG_CURRENT_DESKTOP="gnome" gnome-control-center wifi', '&']);
|
||||
App.closeWindow('sideright');
|
||||
},
|
||||
child: NetworkIndicator(),
|
||||
connections: [
|
||||
[Network, button => {
|
||||
button.toggleClassName('sidebar-button-active', Network.wifi?.internet == 'connected' || Network.wired?.internet == 'connected')
|
||||
}],
|
||||
[Network, button => {
|
||||
setup: (self) => {
|
||||
setupCursorHover(self);
|
||||
self.hook(Network, button => {
|
||||
button.toggleClassName('sidebar-button-active', [Network.wifi?.internet, Network.wired?.internet].includes('connected'))
|
||||
button.tooltipText = (`${Network.wifi?.ssid} | Right-click to configure` || 'Unknown');
|
||||
}],
|
||||
],
|
||||
setup: setupCursorHover,
|
||||
});
|
||||
},
|
||||
...props,
|
||||
});
|
||||
|
||||
@@ -31,21 +43,22 @@ export const ToggleIconBluetooth = (props = {}) => Widget.Button({
|
||||
tooltipText: 'Bluetooth | Right-click to configure',
|
||||
onClicked: () => {
|
||||
const status = Bluetooth?.enabled;
|
||||
if (status)
|
||||
if (status)
|
||||
exec('rfkill block bluetooth');
|
||||
else
|
||||
else
|
||||
exec('rfkill unblock bluetooth');
|
||||
},
|
||||
onSecondaryClickRelease: () => {
|
||||
execAsync(['bash', '-c', 'blueberry &']);
|
||||
App.closeWindow('sideright');
|
||||
},
|
||||
child: BluetoothIndicator(),
|
||||
connections: [
|
||||
[Bluetooth, button => {
|
||||
setup: (self) => {
|
||||
setupCursorHover(self);
|
||||
self.hook(Bluetooth, button => {
|
||||
button.toggleClassName('sidebar-button-active', Bluetooth?.enabled)
|
||||
}],
|
||||
],
|
||||
setup: setupCursorHover,
|
||||
});
|
||||
},
|
||||
...props,
|
||||
});
|
||||
|
||||
@@ -69,24 +82,24 @@ export const HyprToggleIcon = (icon, name, hyprlandConfigValue, props = {}) => W
|
||||
})
|
||||
|
||||
export const ModuleNightLight = (props = {}) => Widget.Button({ // TODO: Make this work
|
||||
properties: [
|
||||
['enabled', false],
|
||||
['yellowlight', undefined],
|
||||
],
|
||||
attribute: {
|
||||
enabled: false,
|
||||
yellowlight: undefined,
|
||||
},
|
||||
className: 'txt-small sidebar-iconbutton',
|
||||
tooltipText: 'Night Light',
|
||||
onClicked: (self) => {
|
||||
self._enabled = !self._enabled;
|
||||
self.toggleClassName('sidebar-button-active', self._enabled);
|
||||
// if (self._enabled) Utils.execAsync(['bash', '-c', 'wlsunset & disown'])
|
||||
if (self._enabled) Utils.execAsync('wlsunset')
|
||||
self.attribute.enabled = !self.attribute.enabled;
|
||||
self.toggleClassName('sidebar-button-active', self.attribute.enabled);
|
||||
// if (self.attribute.enabled) Utils.execAsync(['bash', '-c', 'wlsunset & disown'])
|
||||
if (self.attribute.enabled) Utils.execAsync('wlsunset')
|
||||
else Utils.execAsync('pkill wlsunset');
|
||||
},
|
||||
child: MaterialIcon('nightlight', 'norm'),
|
||||
setup: (self) => {
|
||||
setupCursorHover(self);
|
||||
self._enabled = !!exec('pidof wlsunset');
|
||||
self.toggleClassName('sidebar-button-active', self._enabled);
|
||||
self.attribute.enabled = !!exec('pidof wlsunset');
|
||||
self.toggleClassName('sidebar-button-active', self.attribute.enabled);
|
||||
},
|
||||
...props,
|
||||
});
|
||||
@@ -95,15 +108,22 @@ export const ModuleInvertColors = (props = {}) => Widget.Button({
|
||||
className: 'txt-small sidebar-iconbutton',
|
||||
tooltipText: 'Color inversion',
|
||||
onClicked: (button) => {
|
||||
const shaderPath = JSON.parse(exec('hyprctl -j getoption decoration:screen_shader')).str;
|
||||
if (shaderPath != "[[EMPTY]]" && shaderPath != "") {
|
||||
execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader ''`]).catch(print);
|
||||
button.toggleClassName('sidebar-button-active', false);
|
||||
}
|
||||
else {
|
||||
execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader ~/.config/hypr/shaders/invert.frag`]).catch(print);
|
||||
button.toggleClassName('sidebar-button-active', true);
|
||||
}
|
||||
// const shaderPath = JSON.parse(exec('hyprctl -j getoption decoration:screen_shader')).str;
|
||||
Hyprland.sendMessage('j/getoption decoration:screen_shader')
|
||||
.then((output) => {
|
||||
const shaderPath = JSON.parse(output)["str"].trim();
|
||||
console.log(output)
|
||||
console.log(shaderPath)
|
||||
if (shaderPath != "[[EMPTY]]" && shaderPath != "") {
|
||||
execAsync(['bash', '-c', `hyprctl keyword decoration:screen_shader '[[EMPTY]]'`]).catch(print);
|
||||
button.toggleClassName('sidebar-button-active', false);
|
||||
}
|
||||
else {
|
||||
Hyprland.sendMessage(`j/keyword decoration:screen_shader ${expandTilde('~/.config/hypr/shaders/invert.frag')}`)
|
||||
.catch(print);
|
||||
button.toggleClassName('sidebar-button-active', true);
|
||||
}
|
||||
})
|
||||
},
|
||||
child: MaterialIcon('invert_colors', 'norm'),
|
||||
setup: setupCursorHover,
|
||||
@@ -111,17 +131,17 @@ export const ModuleInvertColors = (props = {}) => Widget.Button({
|
||||
})
|
||||
|
||||
export const ModuleIdleInhibitor = (props = {}) => Widget.Button({ // TODO: Make this work
|
||||
properties: [
|
||||
['enabled', false],
|
||||
['inhibitor', undefined],
|
||||
],
|
||||
attribute: {
|
||||
enabled: false,
|
||||
inhibitor: undefined,
|
||||
},
|
||||
className: 'txt-small sidebar-iconbutton',
|
||||
tooltipText: 'Keep system awake',
|
||||
onClicked: (self) => {
|
||||
self._enabled = !self._enabled;
|
||||
self.toggleClassName('sidebar-button-active', self._enabled);
|
||||
if (self._enabled) {
|
||||
self._inhibitor = Utils.subprocess(
|
||||
self.attribute.enabled = !self.attribute.enabled;
|
||||
self.toggleClassName('sidebar-button-active', self.attribute.enabled);
|
||||
if (self.attribute.enabled) {
|
||||
self.attribute.inhibitor = Utils.subprocess(
|
||||
['wayland-idle-inhibitor.py'],
|
||||
(output) => print(output),
|
||||
(err) => logError(err),
|
||||
@@ -129,7 +149,7 @@ export const ModuleIdleInhibitor = (props = {}) => Widget.Button({ // TODO: Make
|
||||
);
|
||||
}
|
||||
else {
|
||||
self._inhibitor.force_exit();
|
||||
self.attribute.inhibitor.force_exit();
|
||||
}
|
||||
},
|
||||
child: MaterialIcon('coffee', 'norm'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { GLib, Gdk, Gtk } = imports.gi;
|
||||
import { Utils, Widget } from '../../imports.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, EventBox } = Widget;
|
||||
import {
|
||||
@@ -17,44 +17,19 @@ import {
|
||||
import ModuleNotificationList from "./notificationlist.js";
|
||||
import { ModuleCalendar } from "./calendar.js";
|
||||
|
||||
// const NUM_OF_TOGGLES_PER_LINE = 5;
|
||||
// const togglesFlowBox = Widget.FlowBox({
|
||||
// className: 'sidebar-group spacing-h-10',
|
||||
// setup: (self) => {
|
||||
// self.set_max_children_per_line(NUM_OF_TOGGLES_PER_LINE);
|
||||
// self.add(ToggleIconWifi({ hexpand: true }));
|
||||
// self.add(ToggleIconBluetooth({ hexpand: true }));
|
||||
// self.add(HyprToggleIcon('mouse', 'Raw input', 'input:force_no_accel', { hexpand: true }));
|
||||
// self.add(HyprToggleIcon('front_hand', 'No touchpad while typing', 'input:touchpad:disable_while_typing', { hexpand: true }));
|
||||
// self.add(ModuleNightLight({ hexpand: true }));
|
||||
// // Setup flowbox rearrange
|
||||
// self.connect('child-activated', (self, child) => {
|
||||
// if (child.get_index() === 0) {
|
||||
// self.reorder_child(child, self.get_children().length - 1);
|
||||
// } else {
|
||||
// self.reorder_child(child, 0);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
|
||||
const timeRow = Box({
|
||||
className: 'spacing-h-5 sidebar-group-invisible-morehorizpad',
|
||||
children: [
|
||||
// Widget.Label({
|
||||
// className: 'txt-title txt',
|
||||
// connections: [[5000, label => {
|
||||
// label.label = GLib.DateTime.new_now_local().format("%H:%M");
|
||||
// }]],
|
||||
// }),
|
||||
Widget.Label({
|
||||
hpack: 'center',
|
||||
className: 'txt-small txt',
|
||||
connections: [[5000, label => {
|
||||
execAsync(['bash', '-c', `uptime -p | sed -e 's/up //;s/ hours,/h/;s/ minutes/m/'`]).then(upTimeString => {
|
||||
label.label = `System uptime: ${upTimeString}`;
|
||||
}).catch(print);
|
||||
}]],
|
||||
setup: (self) => self
|
||||
.poll(5000, label => {
|
||||
execAsync(['bash', '-c', `uptime -p | sed -e 's/up //;s/ hours,/h/;s/ minutes/m/'`]).then(upTimeString => {
|
||||
label.label = `Uptime: ${upTimeString}`;
|
||||
}).catch(print);
|
||||
})
|
||||
,
|
||||
}),
|
||||
Widget.Box({ hexpand: true }),
|
||||
// ModuleEditIcon({ hpack: 'end' }), // TODO: Make this work
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { Gio, Gdk, GLib, Gtk } = imports.gi;
|
||||
import { App, Widget, Utils } from '../../imports.js';
|
||||
const { Box, Button, CenterBox, Label, Revealer } = Widget;
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { Box, Button, Label, Revealer } = Widget;
|
||||
import { MaterialIcon } from "../../lib/materialicon.js";
|
||||
import Todo from "../../services/todo.js";
|
||||
import { setupCursorHover } from "../../lib/cursorhover.js";
|
||||
@@ -73,33 +73,36 @@ const todoListItem = (task, id, isDone, isEven = false) => {
|
||||
}
|
||||
|
||||
const todoItems = (isDone) => Widget.Scrollable({
|
||||
hscroll: 'never',
|
||||
vscroll: 'automatic',
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
connections: [[Todo, (self) => {
|
||||
self.children = Todo.todo_json.map((task, i) => {
|
||||
if (task.done != isDone) return null;
|
||||
return todoListItem(task, i, isDone);
|
||||
})
|
||||
if (self.children.length == 0) {
|
||||
self.homogeneous = true;
|
||||
self.children = [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
vpack: 'center',
|
||||
className: 'txt',
|
||||
children: [
|
||||
MaterialIcon(`${isDone ? 'checklist' : 'check_circle'}`, 'badonkers'),
|
||||
Label({ label: `${isDone ? 'Finished tasks will go here' : 'Nothing here!'}` })
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
else self.homogeneous = false;
|
||||
}, 'updated']]
|
||||
setup: (self) => self
|
||||
.hook(Todo, (self) => {
|
||||
self.children = Todo.todo_json.map((task, i) => {
|
||||
if (task.done != isDone) return null;
|
||||
return todoListItem(task, i, isDone);
|
||||
})
|
||||
if (self.children.length == 0) {
|
||||
self.homogeneous = true;
|
||||
self.children = [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
vpack: 'center',
|
||||
className: 'txt',
|
||||
children: [
|
||||
MaterialIcon(`${isDone ? 'checklist' : 'check_circle'}`, 'badonkers'),
|
||||
Label({ label: `${isDone ? 'Finished tasks will go here' : 'Nothing here!'}` })
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
else self.homogeneous = false;
|
||||
}, 'updated')
|
||||
,
|
||||
}),
|
||||
setup: (listContents) => {
|
||||
listContents.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
const vScrollbar = listContents.get_vscrollbar();
|
||||
vScrollbar.get_style_context().add_class('sidebar-scrollbar');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user