🔧 chore: start new cli development, use pnpm's built-in github repo feature for gnim-utils
This commit is contained in:
+21
-7
@@ -1,4 +1,5 @@
|
||||
import "ags/overrides";
|
||||
// thanks Aylur!!
|
||||
import "../node_modules/ags/lib/overrides";
|
||||
import "./config";
|
||||
import {
|
||||
PluginApps,
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
PluginWebSearch,
|
||||
PluginKill
|
||||
} from "./runner/plugins";
|
||||
import { Wireplumber } from "./modules/volume";
|
||||
import { handleArguments } from "./modules/arg-handler";
|
||||
import { Runner } from "./runner/Runner";
|
||||
import { Windows } from "./windows";
|
||||
@@ -32,6 +32,7 @@ import GObject, { register } from "ags/gobject";
|
||||
import GLib from "gi://GLib?version=2.0";
|
||||
import Gio from "gi://Gio?version=2.0";
|
||||
import Adw from "gi://Adw?version=1";
|
||||
import AstalWp from "gi://AstalWp";
|
||||
|
||||
|
||||
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");
|
||||
runnerPlugins.forEach(plugin => Runner.addPlugin(plugin));
|
||||
|
||||
this.#connections.set(Wireplumber.getDefault(),
|
||||
Wireplumber.getDefault().getDefaultSink().connect("notify::volume", () =>
|
||||
!Windows.getDefault().isOpen("control-center") &&
|
||||
triggerOSD(OSDModes.sink)
|
||||
)
|
||||
createSubscription(
|
||||
secureBaseBinding<AstalWp.Endpoint>(
|
||||
createBinding(AstalWp.get_default(), "defaultSpeaker"),
|
||||
"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(
|
||||
|
||||
@@ -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 { AuthPopup } from "../widget/AuthPopup";
|
||||
|
||||
import AstalAuth from "gi://AstalAuth";
|
||||
import Polkit from "gi://Polkit";
|
||||
import PolkitAgent from "gi://PolkitAgent";
|
||||
import Polkit from "gi://Polkit?version=1.0";
|
||||
import PolkitAgent from "gi://PolkitAgent?version=1.0";
|
||||
import Gio from "gi://Gio?version=2.0";
|
||||
import GLib from "gi://GLib?version=2.0";
|
||||
import AstalAuth from "gi://AstalAuth?version=0.1";
|
||||
|
||||
|
||||
@register({ GTypeName: "AuthAgent" })
|
||||
export class Auth extends PolkitAgent.Listener {
|
||||
private static instance: Auth;
|
||||
#subject: Polkit.Subject;
|
||||
#pam: AstalAuth.Pam;
|
||||
#handle: any;
|
||||
#user: Polkit.UnixUser;
|
||||
#pam: AstalAuth.Pam;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#subject = Polkit.UnixSession.new("");
|
||||
this.#pam = new AstalAuth.Pam();
|
||||
this.#user = Polkit.UnixUser.new(Number.parseInt(exec("id -u"))) as Polkit.UnixUser;
|
||||
this.#pam = new AstalAuth.Pam;
|
||||
|
||||
this.#handle = this.register(
|
||||
PolkitAgent.RegisterFlags.RUN_IN_THREAD,
|
||||
this.#subject,
|
||||
"/io/github/retrozinndev/colorshell/PolicyKit/AuthAgent",
|
||||
this.register(
|
||||
PolkitAgent.RegisterFlags.RUN_IN_THREAD,
|
||||
Polkit.UnixSession.new(this.#user.get_uid().toString()),
|
||||
"/io/github/retrozinndev/colorshell/AuthAgent",
|
||||
null
|
||||
);
|
||||
}
|
||||
@@ -57,12 +57,8 @@ export class Auth extends PolkitAgent.Listener {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static initAgent(): Auth {
|
||||
if(!this.instance)
|
||||
this.instance = new Auth();
|
||||
|
||||
return this.instance;
|
||||
initiate_authentication_finish(res: Gio.AsyncResult): boolean {
|
||||
|
||||
}
|
||||
|
||||
// TODO: support fingerprint/facial auth
|
||||
|
||||
@@ -17,7 +17,7 @@ export {
|
||||
createScopedConnection,
|
||||
createSecureBinding as secureBinding,
|
||||
createSecureAccessorBinding as secureBaseBinding,
|
||||
} from "../utils";
|
||||
} from "gnim-utils";
|
||||
|
||||
|
||||
export const decoder = new TextDecoder("utf-8"),
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import GObject, { register } from "ags/gobject";
|
||||
import AstalWp from "gi://AstalWp";
|
||||
|
||||
export { Wireplumber };
|
||||
|
||||
@register({ GTypeName: "Wireplumber" })
|
||||
class Wireplumber extends GObject.Object {
|
||||
private static astalWireplumber: (AstalWp.Wp|null) = AstalWp.get_default();
|
||||
export class Wireplumber {
|
||||
private static astalWireplumber: AstalWp.Wp|null = AstalWp.get_default();
|
||||
private static inst: Wireplumber;
|
||||
|
||||
private defaultSink: AstalWp.Endpoint = Wireplumber.astalWireplumber!.get_default_speaker()!;
|
||||
@@ -15,8 +12,6 @@ class Wireplumber extends GObject.Object {
|
||||
private maxSourceVolume: number = 100;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(!Wireplumber.astalWireplumber)
|
||||
throw new Error("Audio features will not work correctly! Please install wireplumber first", {
|
||||
cause: "Wireplumber library not found"
|
||||
|
||||
-1
Submodule src/utils deleted from bedadf7a22
Reference in New Issue
Block a user