🔧 chore: start new cli development, use pnpm's built-in github repo feature for gnim-utils
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
[submodule "gnim-utils"]
|
|
||||||
path = src/utils
|
|
||||||
url = https://github.com/retrozinndev/gnim-utils.git
|
|
||||||
@@ -14,5 +14,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ags": "link:../../../../usr/share/ags/js"
|
"ags": "link:../../../../usr/share/ags/js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"gnim-utils": "github:retrozinndev/gnim-utils"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-7
@@ -1,4 +1,5 @@
|
|||||||
import "ags/overrides";
|
// thanks Aylur!!
|
||||||
|
import "../node_modules/ags/lib/overrides";
|
||||||
import "./config";
|
import "./config";
|
||||||
import {
|
import {
|
||||||
PluginApps,
|
PluginApps,
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
PluginWebSearch,
|
PluginWebSearch,
|
||||||
PluginKill
|
PluginKill
|
||||||
} from "./runner/plugins";
|
} from "./runner/plugins";
|
||||||
import { Wireplumber } from "./modules/volume";
|
|
||||||
import { handleArguments } from "./modules/arg-handler";
|
import { handleArguments } from "./modules/arg-handler";
|
||||||
import { Runner } from "./runner/Runner";
|
import { Runner } from "./runner/Runner";
|
||||||
import { Windows } from "./windows";
|
import { Windows } from "./windows";
|
||||||
@@ -32,6 +32,7 @@ import GObject, { register } from "ags/gobject";
|
|||||||
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 Adw from "gi://Adw?version=1";
|
import Adw from "gi://Adw?version=1";
|
||||||
|
import AstalWp from "gi://AstalWp";
|
||||||
|
|
||||||
|
|
||||||
const runnerPlugins: Array<Runner.Plugin> = [
|
const runnerPlugins: Array<Runner.Plugin> = [
|
||||||
@@ -284,11 +285,24 @@ you should use the socket in the XDG_RUNTIME_DIR/colorshell.sock for a faster re
|
|||||||
console.log("Adding runner plugins");
|
console.log("Adding runner plugins");
|
||||||
runnerPlugins.forEach(plugin => Runner.addPlugin(plugin));
|
runnerPlugins.forEach(plugin => Runner.addPlugin(plugin));
|
||||||
|
|
||||||
this.#connections.set(Wireplumber.getDefault(),
|
createSubscription(
|
||||||
Wireplumber.getDefault().getDefaultSink().connect("notify::volume", () =>
|
secureBaseBinding<AstalWp.Endpoint>(
|
||||||
!Windows.getDefault().isOpen("control-center") &&
|
createBinding(AstalWp.get_default(), "defaultSpeaker"),
|
||||||
triggerOSD(OSDModes.sink)
|
"volume",
|
||||||
)
|
null
|
||||||
|
),
|
||||||
|
() => !Windows.getDefault().isOpen("control-center") &&
|
||||||
|
triggerOSD(OSDModes.sink)
|
||||||
|
);
|
||||||
|
|
||||||
|
createSubscription(
|
||||||
|
secureBaseBinding<AstalWp.Endpoint>(
|
||||||
|
createBinding(AstalWp.get_default(), "defaultSpeaker"),
|
||||||
|
"mute",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
() => !Windows.getDefault().isOpen("control-center") &&
|
||||||
|
triggerOSD(OSDModes.sink)
|
||||||
);
|
);
|
||||||
|
|
||||||
createSubscription(
|
createSubscription(
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
import { Scope } from "ags";
|
||||||
|
import { createScopedConnection, encoder } from "../modules/utils";
|
||||||
|
|
||||||
|
import windows from "./modules/windows";
|
||||||
|
import volume from "./modules/volume";
|
||||||
|
import devel from "./modules/devel";
|
||||||
|
import media from "./modules/media";
|
||||||
|
import Gio from "gi://Gio?version=2.0";
|
||||||
|
import GLib from "gi://GLib?version=2.0";
|
||||||
|
|
||||||
|
|
||||||
|
export namespace Cli {
|
||||||
|
let rootScope: Scope;
|
||||||
|
let service: Gio.SocketService;
|
||||||
|
let initialized: boolean = false;
|
||||||
|
const modules: Array<Module> = [
|
||||||
|
// main module, no need for prefix
|
||||||
|
{
|
||||||
|
help: "manage colorshell windows and do more cool stuff.",
|
||||||
|
commands: [
|
||||||
|
...windows,
|
||||||
|
// others
|
||||||
|
{
|
||||||
|
name: "runner",
|
||||||
|
onCalled: (_, data) => {
|
||||||
|
return {
|
||||||
|
content: `Opening runner${data ? ` with "${data}"` : ""}...`,
|
||||||
|
type: "out"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: "version",
|
||||||
|
alias: "v",
|
||||||
|
help: "print the current colorshell version",
|
||||||
|
onCalled: () => `colorshell by retrozinndev, version ${COLORSHELL_VERSION
|
||||||
|
}${DEVEL ? "(devel)" : ""}`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
volume,
|
||||||
|
media
|
||||||
|
];
|
||||||
|
|
||||||
|
export type Output = {
|
||||||
|
type: "err"|"out";
|
||||||
|
content: string|Uint8Array;
|
||||||
|
} | string;
|
||||||
|
|
||||||
|
/** argument passed to the command / module.
|
||||||
|
* output of onCalled is passed to */
|
||||||
|
export type Argument = {
|
||||||
|
/** kebab-cased name for the argument(without the `--` prefix)
|
||||||
|
* @example help (turns into `--help` internally)*/
|
||||||
|
name: string;
|
||||||
|
/** alias for the name (without the `-` prefix).
|
||||||
|
* @example help -> h */
|
||||||
|
alias?: string;
|
||||||
|
/** whether the argument needs a value attribute.
|
||||||
|
* @example --file ~/a_nice_home_file.txt */
|
||||||
|
hasValue?: boolean;
|
||||||
|
/** runtime-set value for the argument(if enabled) */
|
||||||
|
value?: string;
|
||||||
|
/** help message for the argument */
|
||||||
|
help?: string;
|
||||||
|
onCalled: (value?: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Command = {
|
||||||
|
/** the command name to be called.
|
||||||
|
* @example `colorshell ${prefix?} ${command.name}`*/
|
||||||
|
name: string;
|
||||||
|
help?: string;
|
||||||
|
/** data passed to the command. (only works when arguments are disabled) */
|
||||||
|
data?: string;
|
||||||
|
arguments?: Array<Argument>;
|
||||||
|
onCalled: (args: Array<string>, data?: string) => Output;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Module = {
|
||||||
|
/** command to come after the cli call.
|
||||||
|
* @example `colorshell ${prefix?} ${command}`*/
|
||||||
|
prefix?: string;
|
||||||
|
commands?: Array<Command>;
|
||||||
|
arguments?: Array<Argument>;
|
||||||
|
help?: string;
|
||||||
|
/** called everytime the prefix is used, even when using module commands */
|
||||||
|
onPrefixCalled?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** initialize the cli */
|
||||||
|
export function init(scope: Scope, socketService: Gio.SocketService): void {
|
||||||
|
if(initialized) return;
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
rootScope = scope;
|
||||||
|
service = socketService;
|
||||||
|
DEVEL && modules.push(devel);
|
||||||
|
|
||||||
|
scope.run(() => {
|
||||||
|
createScopedConnection(
|
||||||
|
service, "incoming", (conn) => {
|
||||||
|
try {
|
||||||
|
return handleIncoming(conn);
|
||||||
|
} catch(_) {}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** handle incoming socket calls */
|
||||||
|
function handleIncoming(conn: Gio.SocketConnection): void {
|
||||||
|
const inputStream = Gio.DataInputStream.new(conn.inputStream);
|
||||||
|
|
||||||
|
inputStream.read_upto_async('\x00', -1, GLib.PRIORITY_DEFAULT, null, (_, res) => {
|
||||||
|
const [args, len] = inputStream.read_upto_finish(res);
|
||||||
|
inputStream.close(null);
|
||||||
|
conn.inputStream.close(null);
|
||||||
|
|
||||||
|
if(len < 1) {
|
||||||
|
console.error(`Colorshell: No args provided via socket call`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [success, parsedArgs] = GLib.shell_parse_argv(`colorshell ${args}`);
|
||||||
|
parsedArgs?.splice(0, 1); // remove the unnecessary `colorshell` part
|
||||||
|
|
||||||
|
if(success) {
|
||||||
|
handleArgs(parsedArgs!);
|
||||||
|
|
||||||
|
conn.outputStream.flush(null);
|
||||||
|
conn.close(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.outputStream.write_bytes(
|
||||||
|
encoder.encode("Error: Unexpected syntax error occurred"),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
conn.outputStream.flush(null);
|
||||||
|
conn.close(null);
|
||||||
|
} catch(_e) {
|
||||||
|
const e = _e as Error;
|
||||||
|
console.error(`Colorshell: An error occurred while writing to socket output. Stderr:\n${
|
||||||
|
e.message}\n${e.stack}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** translate app arguments to modules/commands */
|
||||||
|
function handleArgs(args: Array<string>): void {
|
||||||
|
let mod: Module;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Gtk } from "ags/gtk4";
|
||||||
|
import { Cli } from "..";
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
prefix: "dev",
|
||||||
|
help: "development tools to help debugging colorshell",
|
||||||
|
commands: [{
|
||||||
|
name: "inspector",
|
||||||
|
help: "open the gtk's visual inspector",
|
||||||
|
onCalled: () => {
|
||||||
|
Gtk.Window.set_interactive_debugging(true);
|
||||||
|
return "Opening GTK Inspector..."
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
} satisfies Cli.Module;
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Cli } from "..";
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
prefix: "media",
|
||||||
|
help: "manage colorshell's active player",
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
name: "play",
|
||||||
|
help: "resume/start active player's media",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "pause",
|
||||||
|
help: "pause the active player",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "play-pause",
|
||||||
|
help: "toggle pause/resume the active player",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "stop",
|
||||||
|
help: "stop the active player (if compatible)",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "previous",
|
||||||
|
help: "go back to previous media in the active player",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "next",
|
||||||
|
help: "jump to the next media in active player",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "bus-name",
|
||||||
|
help: "retrieve the active player's mpris bus name",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "list",
|
||||||
|
help: "list available players implementing mpris",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "select",
|
||||||
|
help: "resume/start active player's media",
|
||||||
|
onCalled: (_, busName) => "TODO"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} satisfies Cli.Module;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Cli } from "..";
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
prefix: "volume",
|
||||||
|
help: "manage audio device volume/sensitivity. available devices are sink(speaker) and source(microphone).\
|
||||||
|
example usage: `colorshell volume increase sink 5%`",
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
name: "increase",
|
||||||
|
help: "increase volume/sensitivity of a sink/source",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "decrease",
|
||||||
|
help: "decrease volume/sensitivity of a sink/source",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "set",
|
||||||
|
help: "set the volume/sensitivity of a sink/source",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}, {
|
||||||
|
name: "mute",
|
||||||
|
help: "toggle-mute a sink/source's audio",
|
||||||
|
onCalled: () => "TODO"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} satisfies Cli.Module;
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Cli } from "..";
|
||||||
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
name: "open",
|
||||||
|
onCalled: (_, data) => {
|
||||||
|
return {
|
||||||
|
type: "out",
|
||||||
|
content: "TODO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: "toggle",
|
||||||
|
onCalled: (_, data) => {
|
||||||
|
return {
|
||||||
|
type: "out",
|
||||||
|
content: "TODO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: "close",
|
||||||
|
onCalled: (_, data) => {
|
||||||
|
return {
|
||||||
|
type: "out",
|
||||||
|
content: "TODO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: "windows",
|
||||||
|
onCalled: () => {
|
||||||
|
return {
|
||||||
|
type: "out",
|
||||||
|
content: "TODO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: "reopen",
|
||||||
|
onCalled: () => {
|
||||||
|
return {
|
||||||
|
type: "out",
|
||||||
|
content: "TODO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] satisfies Array<Cli.Command>;
|
||||||
+13
-17
@@ -2,29 +2,29 @@ import { exec, execAsync } from "ags/process";
|
|||||||
import { register } from "ags/gobject";
|
import { register } from "ags/gobject";
|
||||||
import { AuthPopup } from "../widget/AuthPopup";
|
import { AuthPopup } from "../widget/AuthPopup";
|
||||||
|
|
||||||
import AstalAuth from "gi://AstalAuth";
|
import Polkit from "gi://Polkit?version=1.0";
|
||||||
import Polkit from "gi://Polkit";
|
import PolkitAgent from "gi://PolkitAgent?version=1.0";
|
||||||
import PolkitAgent from "gi://PolkitAgent";
|
|
||||||
import Gio from "gi://Gio?version=2.0";
|
import Gio from "gi://Gio?version=2.0";
|
||||||
import GLib from "gi://GLib?version=2.0";
|
import GLib from "gi://GLib?version=2.0";
|
||||||
|
import AstalAuth from "gi://AstalAuth?version=0.1";
|
||||||
|
|
||||||
|
|
||||||
@register({ GTypeName: "AuthAgent" })
|
@register({ GTypeName: "AuthAgent" })
|
||||||
export class Auth extends PolkitAgent.Listener {
|
export class Auth extends PolkitAgent.Listener {
|
||||||
private static instance: Auth;
|
private static instance: Auth;
|
||||||
#subject: Polkit.Subject;
|
|
||||||
#pam: AstalAuth.Pam;
|
|
||||||
#handle: any;
|
#handle: any;
|
||||||
|
#user: Polkit.UnixUser;
|
||||||
|
#pam: AstalAuth.Pam;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.#subject = Polkit.UnixSession.new("");
|
this.#user = Polkit.UnixUser.new(Number.parseInt(exec("id -u"))) as Polkit.UnixUser;
|
||||||
this.#pam = new AstalAuth.Pam();
|
this.#pam = new AstalAuth.Pam;
|
||||||
|
|
||||||
this.#handle = this.register(
|
this.register(
|
||||||
PolkitAgent.RegisterFlags.RUN_IN_THREAD,
|
PolkitAgent.RegisterFlags.RUN_IN_THREAD,
|
||||||
this.#subject,
|
Polkit.UnixSession.new(this.#user.get_uid().toString()),
|
||||||
"/io/github/retrozinndev/colorshell/PolicyKit/AuthAgent",
|
"/io/github/retrozinndev/colorshell/AuthAgent",
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -57,12 +57,8 @@ export class Auth extends PolkitAgent.Listener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initiate_authentication_finish(res: Gio.AsyncResult): boolean {
|
||||||
public static initAgent(): Auth {
|
|
||||||
if(!this.instance)
|
|
||||||
this.instance = new Auth();
|
|
||||||
|
|
||||||
return this.instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: support fingerprint/facial auth
|
// TODO: support fingerprint/facial auth
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export {
|
|||||||
createScopedConnection,
|
createScopedConnection,
|
||||||
createSecureBinding as secureBinding,
|
createSecureBinding as secureBinding,
|
||||||
createSecureAccessorBinding as secureBaseBinding,
|
createSecureAccessorBinding as secureBaseBinding,
|
||||||
} from "../utils";
|
} from "gnim-utils";
|
||||||
|
|
||||||
|
|
||||||
export const decoder = new TextDecoder("utf-8"),
|
export const decoder = new TextDecoder("utf-8"),
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import GObject, { register } from "ags/gobject";
|
|
||||||
import AstalWp from "gi://AstalWp";
|
import AstalWp from "gi://AstalWp";
|
||||||
|
|
||||||
export { Wireplumber };
|
|
||||||
|
|
||||||
@register({ GTypeName: "Wireplumber" })
|
export class Wireplumber {
|
||||||
class Wireplumber extends GObject.Object {
|
private static astalWireplumber: AstalWp.Wp|null = AstalWp.get_default();
|
||||||
private static astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default();
|
|
||||||
private static inst: Wireplumber;
|
private static inst: Wireplumber;
|
||||||
|
|
||||||
private defaultSink: AstalWp.Endpoint = Wireplumber.astalWireplumber!.get_default_speaker()!;
|
private defaultSink: AstalWp.Endpoint = Wireplumber.astalWireplumber!.get_default_speaker()!;
|
||||||
@@ -15,8 +12,6 @@ class Wireplumber extends GObject.Object {
|
|||||||
private maxSourceVolume: number = 100;
|
private maxSourceVolume: number = 100;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
|
||||||
|
|
||||||
if(!Wireplumber.astalWireplumber)
|
if(!Wireplumber.astalWireplumber)
|
||||||
throw new Error("Audio features will not work correctly! Please install wireplumber first", {
|
throw new Error("Audio features will not work correctly! Please install wireplumber first", {
|
||||||
cause: "Wireplumber library not found"
|
cause: "Wireplumber library not found"
|
||||||
|
|||||||
-1
Submodule src/utils deleted from bedadf7a22
Reference in New Issue
Block a user