chore: migrate shell to ags v3 and gtk4

This commit is contained in:
retrozinndev
2025-07-06 19:57:28 -03:00
parent b90a799a89
commit 246698c642
10 changed files with 169 additions and 128 deletions
+22 -27
View File
@@ -1,13 +1,6 @@
import AstalNotifd from "gi://AstalNotifd";
import { App } from "astal/gtk3"
import { Wireplumber } from "./scripts/volume"; import { Wireplumber } from "./scripts/volume";
import { handleArguments } from "./scripts/arg-handler"; import { handleArguments } from "./scripts/arg-handler";
import { Time, timeout } from "astal/time"; import { Time, timeout } from "ags/time";
import { OSDModes, setOSDMode } from "./window/OSD";
import { Runner } from "./runner/Runner"; import { Runner } from "./runner/Runner";
import { PluginApps } from "./runner/plugins/apps"; import { PluginApps } from "./runner/plugins/apps";
import { PluginShell } from "./runner/plugins/shell"; import { PluginShell } from "./runner/plugins/shell";
@@ -15,7 +8,6 @@ import { PluginWebSearch } from "./runner/plugins/websearch";
import { PluginMedia } from "./runner/plugins/media"; import { PluginMedia } from "./runner/plugins/media";
import { Windows } from "./windows"; import { Windows } from "./windows";
import { Notifications } from "./scripts/notifications"; import { Notifications } from "./scripts/notifications";
import { GObject } from "astal";
import { PluginWallpapers } from "./runner/plugins/wallpapers"; import { PluginWallpapers } from "./runner/plugins/wallpapers";
import { Wallpaper } from "./scripts/wallpaper"; import { Wallpaper } from "./scripts/wallpaper";
import { Stylesheet } from "./scripts/stylesheet"; import { Stylesheet } from "./scripts/stylesheet";
@@ -23,17 +15,21 @@ import { Clipboard } from "./scripts/clipboard";
import { PluginClipboard } from "./runner/plugins/clipboard"; import { PluginClipboard } from "./runner/plugins/clipboard";
import { Config } from "./scripts/config"; import { Config } from "./scripts/config";
import App from "ags/gtk4/app"
import GObject from "ags/gobject";
import AstalNotifd from "gi://AstalNotifd";
let osdTimer: (Time|undefined);
let osdTimer: (Time|undefined), osdTimeout = 3500;
let connections = new Map<GObject.Object, (Array<number> | number)>(); let connections = new Map<GObject.Object, (Array<number> | number)>();
const defaultWindows: Array<keyof typeof Windows.windows> = [ "bar" ]; const defaultWindows: Array<keyof typeof Windows.prototype.windows> = [ "bar" ];
const runnerPlugins: Array<Runner.Plugin> = [ const runnerPlugins: Array<Runner.Plugin> = [
PluginApps, PluginApps,
PluginShell, PluginShell,
PluginWebSearch, PluginWebSearch,
PluginMedia, PluginMedia,
new PluginWallpapers(), PluginWallpapers,
PluginClipboard PluginClipboard
]; ];
@@ -63,15 +59,15 @@ App.start({
connections.set(Wireplumber.getDefault(), [ connections.set(Wireplumber.getDefault(), [
Wireplumber.getDefault().getDefaultSink().connect("notify::volume", () => Wireplumber.getDefault().getDefaultSink().connect("notify::volume", () =>
triggerOSD(OSDModes.SINK)) triggerOSD())
]); ]);
connections.set(Notifications.getDefault(), [ connections.set(Notifications.getDefault(), [
Notifications.getDefault().connect("notification-added", (_, _notif: AstalNotifd.Notification) => { Notifications.getDefault().connect("notification-added", (_, _notif: AstalNotifd.Notification) => {
Windows.open("floating-notifications"); Windows.getDefault().open("floating-notifications");
}), }),
Notifications.getDefault().connect("notification-removed", (_: Notifications, _id: number) => { Notifications.getDefault().connect("notification-removed", (_: Notifications, _id: number) => {
_.notifications.length === 0 && Windows.close("floating-notifications"); _.notifications.length === 0 && Windows.getDefault().close("floating-notifications");
}) })
]); ]);
@@ -82,32 +78,31 @@ App.start({
runnerPlugins.map(plugin => Runner.addPlugin(plugin)); runnerPlugins.map(plugin => Runner.addPlugin(plugin));
console.log("Opening default windows"); console.log("Opening default windows");
// Open openOnStart windows /* Open openOnStart windows
defaultWindows.map(name => { defaultWindows.map(name => {
if(Windows.isVisible(name)) return; if(Windows.getDefault().isVisible(name)) return;
Windows.open(name); Windows.getDefault().open(name);
}); });*/
} }
}); });
function triggerOSD(osdModeParam: OSDModes) { function triggerOSD() {
if(Windows.isVisible("control-center")) return; if(Windows.getDefault().isVisible("control-center")) return;
Windows.open("osd"); Windows.getDefault().open("osd");
if(!osdTimer) { if(!osdTimer) {
setOSDMode(osdModeParam); osdTimer = timeout(osdTimeout, () => {
osdTimer = timeout(3000, () => {
osdTimer = undefined; osdTimer = undefined;
Windows.close("osd"); Windows.getDefault().close("osd");
}); });
return; return;
} }
osdTimer.cancel(); osdTimer.cancel();
osdTimer = timeout(3000, () => { osdTimer = timeout(osdTimeout, () => {
Windows.close("osd"); Windows.getDefault().close("osd");
osdTimer = undefined; osdTimer = undefined;
}); });
} }
+1 -1
View File
@@ -1,4 +1,4 @@
import { GLib } from "astal"; import GLib from "gi://GLib?version=2.0";
const i18nKeys = { const i18nKeys = {
-21
View File
@@ -1,21 +0,0 @@
{
"name": "colorshell",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "colorshell",
"dependencies": {
"astal": "/usr/share/astal/gjs"
}
},
"../../../../usr/share/astal/gjs": {
"name": "astal",
"license": "LGPL-2.1"
},
"node_modules/astal": {
"resolved": "../../../../usr/share/astal/gjs",
"link": true
}
}
}
+9 -1
View File
@@ -1,6 +1,14 @@
{ {
"$schema": "https://www.schemastore.org/package.json",
"name": "colorshell", "name": "colorshell",
"packageManager": "pnpm@10.12.1",
"scripts": {
"start": "ags run",
"restart": "ags request reload",
"stop": "ags quit",
"bundle": "ags bundle"
},
"dependencies": { "dependencies": {
"astal": "/usr/share/astal/gjs" "ags": "link:../../.local/share/pnpm/global/5/node_modules/ags"
} }
} }
+10 -7
View File
@@ -8,7 +8,7 @@ import GLib from "gi://GLib?version=2.0";
import Gio from "gi://Gio?version=2.0"; import Gio from "gi://Gio?version=2.0";
import AstalIO from "gi://AstalIO"; import AstalIO from "gi://AstalIO";
import AstalNotifd from "gi://AstalNotifd"; import AstalNotifd from "gi://AstalNotifd";
import { Accessor, createConnection } from "ags"; import { Accessor } from "ags";
export { Config }; export { Config };
@@ -46,14 +46,15 @@ export type ConfigEntries = Partial<{
type ValueTypes = "string" | "boolean" | "object" | "number" | "undefined" | "any"; type ValueTypes = "string" | "boolean" | "object" | "number" | "undefined" | "any";
interface ConfigSignals extends GObject.Object.SignalSignatures {
"notify::entries": (entries: ConfigEntries) => void;
}
@register({ GTypeName: "Config" }) @register({ GTypeName: "Config" })
class Config extends GObject.Object { class Config extends GObject.Object {
private static instance: Config; private static instance: Config;
$signals = { declare $signals: ConfigSignals;
"notify": () => {},
"notify::entries": (_: ConfigEntries) => {}
};
private readonly defaultFile = Gio.File.new_for_path( private readonly defaultFile = Gio.File.new_for_path(
`${GLib.get_user_config_dir()}/colorshell/config.json`); `${GLib.get_user_config_dir()}/colorshell/config.json`);
@@ -196,8 +197,10 @@ class Config extends GObject.Object {
} }
public bindProperty(propertyPath: (keyof ConfigEntries|string), expectType?: ValueTypes): Accessor<any|undefined> { public bindProperty(propertyPath: (keyof ConfigEntries|string), expectType?: ValueTypes): Accessor<any|undefined> {
return createConnection(this.getProperty(propertyPath), [(this as typeof Config.instance), "notify::entries", () => return new Accessor<ConfigEntries>(() => this.getProperty(propertyPath, expectType), (callback: () => void) => {
this.getProperty(propertyPath, expectType)]); const id = this.connect("notify::entries", () => callback());
return () => this.disconnect(id);
});
} }
public getProperty(path: string, expectType?: ValueTypes): (any|undefined) { public getProperty(path: string, expectType?: ValueTypes): (any|undefined) {
+86 -2
View File
@@ -2,6 +2,13 @@ import { createPoll } from "ags/time";
import { exec, execAsync } from "ags/process"; import { exec, execAsync } from "ags/process";
import GLib from "gi://GLib?version=2.0"; import GLib from "gi://GLib?version=2.0";
import Gio from "gi://Gio?version=2.0"; import Gio from "gi://Gio?version=2.0";
import { Accessor, For, With } from "ags";
import GObject from "gi://GObject?version=2.0";
import { Astal, Gtk } from "ags/gtk4";
/** gnim doesn't export this, so we need to do it again */
export type WidgetNodeType = Array<GObject.Object> | GObject.Object | number | string | boolean | null | undefined;
export const decoder = new TextDecoder("utf-8"), export const decoder = new TextDecoder("utf-8"),
encoder = new TextEncoder(); encoder = new TextEncoder();
@@ -18,9 +25,63 @@ export function getHyprlandVersion(): string {
} }
export function omitObjectKeys<ObjT = object>(obj: ObjT, keys: keyof ObjT|Array<keyof ObjT>): ObjT { export function omitObjectKeys<ObjT = object>(obj: ObjT, keys: keyof ObjT|Array<keyof ObjT>): ObjT {
for(const objKey of Object.keys(obj)) { const finalObject = obj;
for(const omitKey of keys) {}
for(const objKey of Object.keys(obj as object)) {
if(!Array.isArray(keys)) {
if(objKey === keys) {
delete finalObject[keys as keyof typeof finalObject];
break;
}
continue;
}
for(const omitKey of keys) {
if(objKey === omitKey) {
delete finalObject[objKey as keyof typeof finalObject];
break;
}
}
} }
return finalObject;
}
export function variableToBoolean(variable: any|Accessor<any>|Accessor<Array<any>>): boolean|Accessor<boolean> {
return (variable instanceof Accessor) ?
variable.as(v => Array.isArray(v) ?
(v as Array<any>).length > 0
: Boolean(v))
: Boolean(variable);
}
export function transform<ValueType = any|Array<any>, RType = any>(
v: Accessor<ValueType>|ValueType, fn: (v: ValueType) => RType
): RType|Accessor<RType> {
return (v instanceof Accessor) ?
v.as(fn)
: fn(v);
}
export function transformWidget<ValueType = unknown>(
v: Accessor<ValueType|Array<ValueType>>|ValueType|Array<ValueType>,
fn: (v: ValueType, i?: Accessor<number>|number) => JSX.Element
): WidgetNodeType {
return (v instanceof Accessor) ?
Array.isArray(v.get()) ?
For({
each: v as Accessor<Array<ValueType>>,
children: (cval, i) => fn(cval, i)
})
: With({
value: v as Accessor<ValueType>,
children: fn
})
: (Array.isArray(v) ?
v.map(val => fn(val))
: fn(v));
} }
export function makeDirectory(dir: string): void { export function makeDirectory(dir: string): void {
@@ -41,3 +102,26 @@ export function isInstalled(commandName: string): boolean {
return false; return false;
} }
export function addSliderMarksFromMinMax(slider: Astal.Slider, amountOfMarks: number = 2, markup?: (string | null)) {
if(markup && !markup.includes("{}"))
markup = `${markup}{}`
slider.add_mark(slider.min, Gtk.PositionType.BOTTOM, markup ?
markup.replaceAll("{}", `${slider.min}`) : null);
const num = (amountOfMarks - 1);
for(let i = 1; i <= num; i++) {
const part = (slider.max / num) | 0;
if(i > num) {
slider.add_mark(slider.max, Gtk.PositionType.BOTTOM, `${slider.max}K`);
break;
}
slider.add_mark(part*i, Gtk.PositionType.BOTTOM, markup ?
markup.replaceAll("{}", `${part*i}`) : null);
}
return slider;
}
-24
View File
@@ -1,24 +0,0 @@
import { Gtk, Astal } from "ags/gtk4";
export function addSliderMarksFromMinMax(slider: Astal.Slider, amountOfMarks: number = 2, markup?: (string | null)) {
if(markup && !markup.includes("{}"))
markup = `${markup}{}`
slider.add_mark(slider.min, Gtk.PositionType.BOTTOM, markup ?
markup.replaceAll("{}", `${slider.min}`) : null);
const num = (amountOfMarks - 1);
for(let i = 1; i <= num; i++) {
const part = (slider.max / num) | 0;
if(i > num) {
slider.add_mark(slider.max, Gtk.PositionType.BOTTOM, `${slider.max}K`);
break;
}
slider.add_mark(part*i, Gtk.PositionType.BOTTOM, markup ?
markup.replaceAll("{}", `${part*i}`) : null);
}
return slider;
}
+20 -20
View File
@@ -1,26 +1,26 @@
// SCSS Variables // SCSS Variables
// Generated by 'wal' // Generated by 'wal'
$wallpaper: "/home/joaov/wallpapers/Frieren Ring.jpeg"; $wallpaper: "/home/joaov/wallpapers/Gumi Forest Sunlight.jpg";
// Special // Special
$background: #523c42; $background: #2a2825;
$foreground: #d3cecf; $foreground: #c9c9c8;
$cursor: #d3cecf; $cursor: #c9c9c8;
// Colors // Colors
$color0: #523c42; $color0: #2a2825;
$color1: #6c839d; $color1: #6a6a3b;
$color2: #7a84a4; $color2: #7b7b48;
$color3: #9f8a9d; $color3: #908a45;
$color4: #84a2b5; $color4: #7e876d;
$color5: #9f9cab; $color5: #8a9680;
$color6: #b7a1b2; $color6: #a5a679;
$color7: #b0a7a9; $color7: #a29f98;
$color8: #937b81; $color8: #7d7667;
$color9: #90AFD2; $color9: #8E8E4F;
$color10: #A3B0DB; $color10: #A5A560;
$color11: #D4B9D2; $color11: #C0B85C;
$color12: #B0D9F2; $color12: #A9B592;
$color13: #D5D0E5; $color13: #B9C8AB;
$color14: #F5D7EE; $color14: #DDDEA2;
$color15: #d3cecf; $color15: #c9c9c8;
+11 -12
View File
@@ -1,14 +1,13 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true, "allowJs": false,
"strict": true, "checkJs": true,
"target": "ES2022", "jsx": "react-jsx",
"module": "ES2022", "jsxImportSource": "ags/gtk4",
"moduleResolution": "Bundler", "module": "esnext",
"checkJs": true, "moduleResolution": "bundler",
"allowJs": false, "strict": true,
"jsx": "react-jsx", "target": "esnext"
"jsxImportSource": "astal/gtk3" }
}
} }
+10 -13
View File
@@ -1,14 +1,8 @@
import App from "ags/gtk4/app" import App from "ags/gtk4/app"
import { Bar } from "./window/Bar";
import { OSD } from "./window/OSD";
import { ControlCenter } from "./window/ControlCenter";
import { CenterWindow } from "./window/CenterWindow";
import { LogoutMenu } from "./window/LogoutMenu";
import { FloatingNotifications } from "./window/FloatingNotifications";
import { AppsWindow } from "./window/AppsWindow";
import AstalHyprland from "gi://AstalHyprland"; import AstalHyprland from "gi://AstalHyprland";
import GObject, { getter, register } from "ags/gobject"; import GObject, { getter, register, signal } from "ags/gobject";
import { Astal } from "ags/gtk4"; import { Astal } from "ags/gtk4";
import { ControlCenter } from "./window/ControlCenter";
export { Windows }; export { Windows };
@@ -28,15 +22,18 @@ class Windows extends GObject.Object {
#windowConnections: Record<string, (Array<number> | Array<Array<number>>)> = {}; #windowConnections: Record<string, (Array<number> | Array<Array<number>>)> = {};
#appConnections: Array<number> = []; #appConnections: Array<number> = [];
#windows: Record<string, (() => (Astal.Window | Array<Astal.Window>))> = { #windows: Record<string, (() => (Astal.Window | Array<Astal.Window>))> = {
"bar": this.createWindowForMonitors(Bar), //"bar": this.createWindowForMonitors(Bar),
"osd": this.createWindowForFocusedMonitor(OSD), //"osd": this.createWindowForFocusedMonitor(OSD),
"control-center": this.createWindowForFocusedMonitor(ControlCenter), "control-center": this.createWindowForFocusedMonitor(ControlCenter),
"center-window": this.createWindowForFocusedMonitor(CenterWindow), /*"center-window": this.createWindowForFocusedMonitor(CenterWindow),
"logout-menu": this.createWindowForFocusedMonitor(LogoutMenu), "logout-menu": this.createWindowForFocusedMonitor(LogoutMenu),
"floating-notifications": this.createWindowForFocusedMonitor(FloatingNotifications), "floating-notifications": this.createWindowForFocusedMonitor(FloatingNotifications),
"apps-window": this.createWindowForFocusedMonitor(AppsWindow) "apps-window": this.createWindowForFocusedMonitor(AppsWindow)*/
}; };
@signal(String) opened(_name: string) {}
@signal(String) closed(_name: string) {}
get windows() { return this.#windows; } get windows() { return this.#windows; }
@getter(Object) @getter(Object)
@@ -187,7 +184,7 @@ class Windows extends GObject.Object {
cause: `No focused monitor found (${typeof focusedMonitor})` cause: `No focused monitor found (${typeof focusedMonitor})`
}); });
return () => windowFun(focusedMonitor.id) as Astal.Window; return () => (windowFun(focusedMonitor.id) as Astal.Window);
} }
public addWindow(name: string, window: (() => (Astal.Window | Array<Astal.Window>))): void { public addWindow(name: string, window: (() => (Astal.Window | Array<Astal.Window>))): void {