From 246698c642a3b671b0bd71d733be15794ee0fcfd Mon Sep 17 00:00:00 2001 From: retrozinndev Date: Sun, 6 Jul 2025 19:57:28 -0300 Subject: [PATCH] :sparkles: chore: migrate shell to ags v3 and gtk4 --- ags/app.ts | 49 ++++++++++----------- ags/i18n/intl.ts | 2 +- ags/package-lock.json | 21 --------- ags/package.json | 10 ++++- ags/scripts/config.ts | 17 ++++--- ags/scripts/utils.ts | 88 ++++++++++++++++++++++++++++++++++++- ags/scripts/widget-utils.ts | 24 ---------- ags/style/_wal.scss | 40 ++++++++--------- ags/tsconfig.json | 23 +++++----- ags/windows.ts | 23 +++++----- 10 files changed, 169 insertions(+), 128 deletions(-) delete mode 100644 ags/package-lock.json delete mode 100644 ags/scripts/widget-utils.ts diff --git a/ags/app.ts b/ags/app.ts index 7df09e1..c640ab7 100644 --- a/ags/app.ts +++ b/ags/app.ts @@ -1,13 +1,6 @@ -import AstalNotifd from "gi://AstalNotifd"; - -import { App } from "astal/gtk3" import { Wireplumber } from "./scripts/volume"; - import { handleArguments } from "./scripts/arg-handler"; -import { Time, timeout } from "astal/time"; - -import { OSDModes, setOSDMode } from "./window/OSD"; - +import { Time, timeout } from "ags/time"; import { Runner } from "./runner/Runner"; import { PluginApps } from "./runner/plugins/apps"; import { PluginShell } from "./runner/plugins/shell"; @@ -15,7 +8,6 @@ import { PluginWebSearch } from "./runner/plugins/websearch"; import { PluginMedia } from "./runner/plugins/media"; import { Windows } from "./windows"; import { Notifications } from "./scripts/notifications"; -import { GObject } from "astal"; import { PluginWallpapers } from "./runner/plugins/wallpapers"; import { Wallpaper } from "./scripts/wallpaper"; import { Stylesheet } from "./scripts/stylesheet"; @@ -23,17 +15,21 @@ import { Clipboard } from "./scripts/clipboard"; import { PluginClipboard } from "./runner/plugins/clipboard"; 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 | number)>(); -const defaultWindows: Array = [ "bar" ]; +const defaultWindows: Array = [ "bar" ]; const runnerPlugins: Array = [ PluginApps, PluginShell, PluginWebSearch, PluginMedia, - new PluginWallpapers(), + PluginWallpapers, PluginClipboard ]; @@ -63,15 +59,15 @@ App.start({ connections.set(Wireplumber.getDefault(), [ Wireplumber.getDefault().getDefaultSink().connect("notify::volume", () => - triggerOSD(OSDModes.SINK)) + triggerOSD()) ]); connections.set(Notifications.getDefault(), [ 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.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)); console.log("Opening default windows"); - // Open openOnStart windows + /* Open openOnStart windows defaultWindows.map(name => { - if(Windows.isVisible(name)) return; - Windows.open(name); - }); + if(Windows.getDefault().isVisible(name)) return; + Windows.getDefault().open(name); + });*/ } }); -function triggerOSD(osdModeParam: OSDModes) { - if(Windows.isVisible("control-center")) return; +function triggerOSD() { + if(Windows.getDefault().isVisible("control-center")) return; - Windows.open("osd"); + Windows.getDefault().open("osd"); if(!osdTimer) { - setOSDMode(osdModeParam); - osdTimer = timeout(3000, () => { + osdTimer = timeout(osdTimeout, () => { osdTimer = undefined; - Windows.close("osd"); + Windows.getDefault().close("osd"); }); return; } osdTimer.cancel(); - osdTimer = timeout(3000, () => { - Windows.close("osd"); + osdTimer = timeout(osdTimeout, () => { + Windows.getDefault().close("osd"); osdTimer = undefined; }); } diff --git a/ags/i18n/intl.ts b/ags/i18n/intl.ts index 32f96c6..86e7f25 100644 --- a/ags/i18n/intl.ts +++ b/ags/i18n/intl.ts @@ -1,4 +1,4 @@ -import { GLib } from "astal"; +import GLib from "gi://GLib?version=2.0"; const i18nKeys = { diff --git a/ags/package-lock.json b/ags/package-lock.json deleted file mode 100644 index a31381c..0000000 --- a/ags/package-lock.json +++ /dev/null @@ -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 - } - } -} diff --git a/ags/package.json b/ags/package.json index 02db176..a4b17ba 100644 --- a/ags/package.json +++ b/ags/package.json @@ -1,6 +1,14 @@ { + "$schema": "https://www.schemastore.org/package.json", "name": "colorshell", + "packageManager": "pnpm@10.12.1", + "scripts": { + "start": "ags run", + "restart": "ags request reload", + "stop": "ags quit", + "bundle": "ags bundle" + }, "dependencies": { - "astal": "/usr/share/astal/gjs" + "ags": "link:../../.local/share/pnpm/global/5/node_modules/ags" } } diff --git a/ags/scripts/config.ts b/ags/scripts/config.ts index c13a604..26eb125 100644 --- a/ags/scripts/config.ts +++ b/ags/scripts/config.ts @@ -8,7 +8,7 @@ import GLib from "gi://GLib?version=2.0"; import Gio from "gi://Gio?version=2.0"; import AstalIO from "gi://AstalIO"; import AstalNotifd from "gi://AstalNotifd"; -import { Accessor, createConnection } from "ags"; +import { Accessor } from "ags"; export { Config }; @@ -46,14 +46,15 @@ export type ConfigEntries = Partial<{ type ValueTypes = "string" | "boolean" | "object" | "number" | "undefined" | "any"; +interface ConfigSignals extends GObject.Object.SignalSignatures { + "notify::entries": (entries: ConfigEntries) => void; +} + @register({ GTypeName: "Config" }) class Config extends GObject.Object { private static instance: Config; - $signals = { - "notify": () => {}, - "notify::entries": (_: ConfigEntries) => {} - }; + declare $signals: ConfigSignals; private readonly defaultFile = Gio.File.new_for_path( `${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 { - return createConnection(this.getProperty(propertyPath), [(this as typeof Config.instance), "notify::entries", () => - this.getProperty(propertyPath, expectType)]); + return new Accessor(() => this.getProperty(propertyPath, expectType), (callback: () => void) => { + const id = this.connect("notify::entries", () => callback()); + return () => this.disconnect(id); + }); } public getProperty(path: string, expectType?: ValueTypes): (any|undefined) { diff --git a/ags/scripts/utils.ts b/ags/scripts/utils.ts index 6be64ac..918be90 100644 --- a/ags/scripts/utils.ts +++ b/ags/scripts/utils.ts @@ -2,6 +2,13 @@ import { createPoll } from "ags/time"; import { exec, execAsync } from "ags/process"; import GLib from "gi://GLib?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 | number | string | boolean | null | undefined; export const decoder = new TextDecoder("utf-8"), encoder = new TextEncoder(); @@ -18,9 +25,63 @@ export function getHyprlandVersion(): string { } export function omitObjectKeys(obj: ObjT, keys: keyof ObjT|Array): ObjT { - for(const objKey of Object.keys(obj)) { - for(const omitKey of keys) {} + const finalObject = obj; + + 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|Accessor>): boolean|Accessor { + return (variable instanceof Accessor) ? + variable.as(v => Array.isArray(v) ? + (v as Array).length > 0 + : Boolean(v)) + : Boolean(variable); +} + +export function transform, RType = any>( + v: Accessor|ValueType, fn: (v: ValueType) => RType +): RType|Accessor { + + return (v instanceof Accessor) ? + v.as(fn) + : fn(v); +} + +export function transformWidget( + v: Accessor>|ValueType|Array, + fn: (v: ValueType, i?: Accessor|number) => JSX.Element +): WidgetNodeType { + + return (v instanceof Accessor) ? + Array.isArray(v.get()) ? + For({ + each: v as Accessor>, + children: (cval, i) => fn(cval, i) + }) + : With({ + value: v as Accessor, + children: fn + }) + : (Array.isArray(v) ? + v.map(val => fn(val)) + : fn(v)); } export function makeDirectory(dir: string): void { @@ -41,3 +102,26 @@ export function isInstalled(commandName: string): boolean { 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; +} diff --git a/ags/scripts/widget-utils.ts b/ags/scripts/widget-utils.ts deleted file mode 100644 index 2b3ca41..0000000 --- a/ags/scripts/widget-utils.ts +++ /dev/null @@ -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; -} diff --git a/ags/style/_wal.scss b/ags/style/_wal.scss index a94b042..0c75ac5 100644 --- a/ags/style/_wal.scss +++ b/ags/style/_wal.scss @@ -1,26 +1,26 @@ // SCSS Variables // Generated by 'wal' -$wallpaper: "/home/joaov/wallpapers/Frieren Ring.jpeg"; +$wallpaper: "/home/joaov/wallpapers/Gumi Forest Sunlight.jpg"; // Special -$background: #523c42; -$foreground: #d3cecf; -$cursor: #d3cecf; +$background: #2a2825; +$foreground: #c9c9c8; +$cursor: #c9c9c8; // Colors -$color0: #523c42; -$color1: #6c839d; -$color2: #7a84a4; -$color3: #9f8a9d; -$color4: #84a2b5; -$color5: #9f9cab; -$color6: #b7a1b2; -$color7: #b0a7a9; -$color8: #937b81; -$color9: #90AFD2; -$color10: #A3B0DB; -$color11: #D4B9D2; -$color12: #B0D9F2; -$color13: #D5D0E5; -$color14: #F5D7EE; -$color15: #d3cecf; +$color0: #2a2825; +$color1: #6a6a3b; +$color2: #7b7b48; +$color3: #908a45; +$color4: #7e876d; +$color5: #8a9680; +$color6: #a5a679; +$color7: #a29f98; +$color8: #7d7667; +$color9: #8E8E4F; +$color10: #A5A560; +$color11: #C0B85C; +$color12: #A9B592; +$color13: #B9C8AB; +$color14: #DDDEA2; +$color15: #c9c9c8; diff --git a/ags/tsconfig.json b/ags/tsconfig.json index d79ca1e..47ca203 100644 --- a/ags/tsconfig.json +++ b/ags/tsconfig.json @@ -1,14 +1,13 @@ { - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - "experimentalDecorators": true, - "strict": true, - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "Bundler", - "checkJs": true, - "allowJs": false, - "jsx": "react-jsx", - "jsxImportSource": "astal/gtk3" - } + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowJs": false, + "checkJs": true, + "jsx": "react-jsx", + "jsxImportSource": "ags/gtk4", + "module": "esnext", + "moduleResolution": "bundler", + "strict": true, + "target": "esnext" + } } diff --git a/ags/windows.ts b/ags/windows.ts index 1faa2d5..ea21a4c 100644 --- a/ags/windows.ts +++ b/ags/windows.ts @@ -1,14 +1,8 @@ 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 GObject, { getter, register } from "ags/gobject"; +import GObject, { getter, register, signal } from "ags/gobject"; import { Astal } from "ags/gtk4"; +import { ControlCenter } from "./window/ControlCenter"; export { Windows }; @@ -28,15 +22,18 @@ class Windows extends GObject.Object { #windowConnections: Record | Array>)> = {}; #appConnections: Array = []; #windows: Record (Astal.Window | Array))> = { - "bar": this.createWindowForMonitors(Bar), - "osd": this.createWindowForFocusedMonitor(OSD), + //"bar": this.createWindowForMonitors(Bar), + //"osd": this.createWindowForFocusedMonitor(OSD), "control-center": this.createWindowForFocusedMonitor(ControlCenter), - "center-window": this.createWindowForFocusedMonitor(CenterWindow), + /*"center-window": this.createWindowForFocusedMonitor(CenterWindow), "logout-menu": this.createWindowForFocusedMonitor(LogoutMenu), "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; } @getter(Object) @@ -187,7 +184,7 @@ class Windows extends GObject.Object { 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))): void {