From 6a8c41cd1d5e218d0dacffb836fdd7d4ec6333dd Mon Sep 17 00:00:00 2001 From: Aylur Date: Mon, 14 Oct 2024 16:01:36 +0000 Subject: feat: astal-io --- lib/astal/io/application.vala | 161 +++++++++++++++++++++++++++++++++++ lib/astal/io/cli.vala | 87 +++++++++++++++++++ lib/astal/io/config.vala.in | 6 ++ lib/astal/io/file.vala | 81 ++++++++++++++++++ lib/astal/io/meson.build | 90 ++++++++++++++++++++ lib/astal/io/process.vala | 119 ++++++++++++++++++++++++++ lib/astal/io/time.vala | 71 ++++++++++++++++ lib/astal/io/variable.vala | 194 ++++++++++++++++++++++++++++++++++++++++++ lib/astal/io/version | 1 + 9 files changed, 810 insertions(+) create mode 100644 lib/astal/io/application.vala create mode 100644 lib/astal/io/cli.vala create mode 100644 lib/astal/io/config.vala.in create mode 100644 lib/astal/io/file.vala create mode 100644 lib/astal/io/meson.build create mode 100644 lib/astal/io/process.vala create mode 100644 lib/astal/io/time.vala create mode 100644 lib/astal/io/variable.vala create mode 100644 lib/astal/io/version diff --git a/lib/astal/io/application.vala b/lib/astal/io/application.vala new file mode 100644 index 0000000..00bef57 --- /dev/null +++ b/lib/astal/io/application.vala @@ -0,0 +1,161 @@ +namespace AstalIO { +public errordomain AppError { + NAME_OCCUPIED, + TAKEOVER_FAILED, +} + +public interface Application : Object { + public abstract void quit() throws Error; + public abstract void inspector() throws Error; + public abstract void toggle_window(string window) throws Error; + + public abstract string instance_name { owned get; construct set; } + public abstract void acquire_socket() throws Error; + public abstract void request(string msg, SocketConnection conn) throws Error; +} + +public SocketService acquire_socket(Application app) throws Error { + var name = app.instance_name; + foreach (var instance in get_instances()) { + if (instance == name) { + throw new AppError.NAME_OCCUPIED(@"$name is occupied"); + } + } + + var rundir = Environment.get_user_runtime_dir(); + var path = @"$rundir/$name.sock"; + + if (FileUtils.test(path, FileTest.EXISTS)) { + try { + File.new_for_path(path).delete(null); + } catch (Error err) { + throw new AppError.TAKEOVER_FAILED("could not delete previous socket"); + } + } + + var service = new SocketService(); + service.add_address( + new UnixSocketAddress(path), + SocketType.STREAM, + SocketProtocol.DEFAULT, + null, + null + ); + + service.incoming.connect((conn) => { + read_sock.begin(conn, (_, res) => { + try { + string message = read_sock.end(res); + app.request(message != null ? message.strip() : "", conn); + } catch (Error err) { + critical(err.message); + } + }); + return false; + }); + + return service; +} + +public static List get_instances() { + var list = new List(); + var prefix = "io.Astal."; + + try { + DBusImpl dbus = Bus.get_proxy_sync( + BusType.SESSION, + "org.freedesktop.DBus", + "/org/freedesktop/DBus" + ); + + foreach (var busname in dbus.list_names()) { + if (busname.has_prefix(prefix)) + list.append(busname.replace(prefix, "")); + } + } catch (Error err) { + critical(err.message); + } + + return list; +} + +public static void quit_instance(string instance) { + try { + IApplication proxy = Bus.get_proxy_sync( + BusType.SESSION, + "io.Astal." + instance, + "/io/Astal/Application" + ); + + proxy.quit(); + } catch (Error err) { + critical(err.message); + } +} + +public static void open_inspector(string instance) { + try { + IApplication proxy = Bus.get_proxy_sync( + BusType.SESSION, + "io.Astal." + instance, + "/io/Astal/Application" + ); + + proxy.inspector(); + } catch (Error err) { + critical(err.message); + } +} + +public static void toggle_window_by_name(string instance, string window) { + try { + IApplication proxy = Bus.get_proxy_sync( + BusType.SESSION, + "io.Astal." + instance, + "/io/Astal/Application" + ); + + proxy.toggle_window(window); + } catch (Error err) { + critical(err.message); + } +} + +public static string send_message(string instance_name, string msg) { + var rundir = Environment.get_user_runtime_dir(); + var socket_path = @"$rundir/$instance_name.sock"; + var client = new SocketClient(); + + try { + var conn = client.connect(new UnixSocketAddress(socket_path), null); + conn.output_stream.write(msg.concat("\x04").data); + + var stream = new DataInputStream(conn.input_stream); + return stream.read_upto("\x04", -1, null, null); + } catch (Error err) { + printerr(err.message); + return ""; + } +} + +public async string read_sock(SocketConnection conn) throws IOError { + var stream = new DataInputStream(conn.input_stream); + return yield stream.read_upto_async("\x04", -1, Priority.DEFAULT, null, null); +} + +public async void write_sock(SocketConnection conn, string response) throws IOError { + yield conn.output_stream.write_async(response.concat("\x04").data, Priority.DEFAULT); +} + +[DBus (name="io.Astal.Application")] +private interface IApplication : DBusProxy { + public abstract void quit() throws GLib.Error; + public abstract void inspector() throws GLib.Error; + public abstract void toggle_window(string window) throws GLib.Error; +} + +[DBus (name="org.freedesktop.DBus")] +private interface DBusImpl : DBusProxy { + public abstract string[] list_names() throws Error; +} +} diff --git a/lib/astal/io/cli.vala b/lib/astal/io/cli.vala new file mode 100644 index 0000000..1db0b2e --- /dev/null +++ b/lib/astal/io/cli.vala @@ -0,0 +1,87 @@ +private static bool version; +private static bool help; +private static bool list; +private static bool quit; +private static bool inspector; +private static string? toggle_window; +private static string? instance_name; + +private const OptionEntry[] options = { + { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null }, + { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null }, + { "list", 'l', OptionFlags.NONE, OptionArg.NONE, ref list, null, null }, + { "quit", 'q', OptionFlags.NONE, OptionArg.NONE, ref quit, null, null }, + { "quit", 'q', OptionFlags.NONE, OptionArg.NONE, ref quit, null, null }, + { "inspector", 'I', OptionFlags.NONE, OptionArg.NONE, ref inspector, null, null }, + { "toggle-window", 't', OptionFlags.NONE, OptionArg.STRING, ref toggle_window, null, null }, + { "instance", 'i', OptionFlags.NONE, OptionArg.STRING, ref instance_name, null, null }, + { null }, +}; + +int main(string[] argv) { + try { + var opts = new OptionContext(); + opts.add_main_entries(options, null); + opts.set_help_enabled(false); + opts.set_ignore_unknown_options(false); + opts.parse(ref argv); + } catch (OptionError err) { + printerr (err.message); + return 1; + } + + if (help) { + print("Client for Astal.Application instances\n\n"); + print("Usage:\n"); + print(" %s [flags] message\n\n", argv[0]); + print("Flags:\n"); + print(" -h, --help Print this help and exit\n"); + print(" -v, --version Print version number and exit\n"); + print(" -l, --list List running Astal instances and exit\n"); + print(" -q, --quit Quit an Astal.Application instance\n"); + print(" -i, --instance Instance name of the Astal instance\n"); + print(" -I, --inspector Open up Gtk debug tool\n"); + print(" -t, --toggle-window Show or hide a window\n"); + return 0; + } + + if (version) { + print(AstalIO.VERSION); + return 0; + } + + if (instance_name == null) + instance_name = "astal"; + + if (list) { + foreach (var name in AstalIO.get_instances()) + stdout.printf("%s\n", name); + + return 0; + } + + if (quit) { + AstalIO.quit_instance(instance_name); + return 0; + } + + if (inspector) { + AstalIO.open_inspector(instance_name); + return 0; + } + + if (toggle_window != null) { + AstalIO.toggle_window_by_name(instance_name, toggle_window); + return 0; + } + + var request = ""; + for (var i = 1; i < argv.length; ++i) { + request = request.concat(" ", argv[i]); + } + + var reply = AstalIO.send_message(instance_name, request); + print("%s\n", reply); + + return 0; +} diff --git a/lib/astal/io/config.vala.in b/lib/astal/io/config.vala.in new file mode 100644 index 0000000..fe1e450 --- /dev/null +++ b/lib/astal/io/config.vala.in @@ -0,0 +1,6 @@ +namespace AstalIO { + public const int MAJOR_VERSION = @MAJOR_VERSION@; + public const int MINOR_VERSION = @MINOR_VERSION@; + public const int MICRO_VERSION = @MICRO_VERSION@; + public const string VERSION = "@VERSION@"; +} diff --git a/lib/astal/io/file.vala b/lib/astal/io/file.vala new file mode 100644 index 0000000..b2d480c --- /dev/null +++ b/lib/astal/io/file.vala @@ -0,0 +1,81 @@ +namespace AstalIO { +public string read_file(string path) { + var str = ""; + try { + FileUtils.get_contents(path, out str, null); + } catch (Error error) { + critical(error.message); + } + return str; +} + +public async string read_file_async(string path) throws Error { + uint8[] content; + yield File.new_for_path(path).load_contents_async(null, out content, null); + return (string)content; +} + +public void write_file(string path, string content) { + try { + FileUtils.set_contents(path, content); + } catch (Error error) { + critical(error.message); + } +} + +public async void write_file_async(string path, string content) throws Error { + yield File.new_for_path(path).replace_contents_async( + content.data, + null, + false, + FileCreateFlags.REPLACE_DESTINATION, + null, + null); +} + +public FileMonitor? monitor_file(string path, Closure callback) { + try { + var file = File.new_for_path(path); + var mon = file.monitor(FileMonitorFlags.NONE); + + mon.changed.connect((file, _file, event) => { + var f = Value(Type.STRING); + var e = Value(Type.INT); + var ret = Value(Type.POINTER); + + f.set_string(file.get_path()); + e.set_int(event); + + callback.invoke(ref ret, { f, e }); + }); + + if (FileUtils.test(path, FileTest.IS_DIR)) { + var enumerator = file.enumerate_children("standard::*", + FileQueryInfoFlags.NONE, null); + + var i = enumerator.next_file(null); + while (i != null) { + if (i.get_file_type() == FileType.DIRECTORY) { + var filepath = file.get_child(i.get_name()).get_path(); + if (filepath != null) { + var m = monitor_file(path, callback); + mon.notify["cancelled"].connect(() => { + m.cancel(); + }); + } + } + i = enumerator.next_file(null); + } + } + + mon.ref(); + mon.notify["cancelled"].connect(() => { + mon.unref(); + }); + return mon; + } catch (Error error) { + critical(error.message); + return null; + } +} +} diff --git a/lib/astal/io/meson.build b/lib/astal/io/meson.build new file mode 100644 index 0000000..426a6d6 --- /dev/null +++ b/lib/astal/io/meson.build @@ -0,0 +1,90 @@ +project( + 'astal-io', + 'vala', + 'c', + version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(), + meson_version: '>= 0.62.0', + default_options: [ + 'warning_level=2', + 'werror=false', + 'c_std=gnu11', + ], +) + +version_split = meson.project_version().split('.') +api_version = version_split[0] + '.' + version_split[1] +gir = 'AstalIO-' + api_version + '.gir' +typelib = 'AstalIO-' + api_version + '.typelib' +libdir = get_option('prefix') / get_option('libdir') +pkgdatadir = get_option('prefix') / get_option('datadir') / 'astal' + +config = configure_file( + input: 'config.vala.in', + output: 'config.vala', + configuration: { + 'VERSION': meson.project_version(), + 'MAJOR_VERSION': version_split[0], + 'MINOR_VERSION': version_split[1], + 'MICRO_VERSION': version_split[2], + }, +) + +deps = [ + dependency('glib-2.0'), + dependency('gio-unix-2.0'), + dependency('gobject-2.0'), + dependency('gio-2.0'), +] + +sources = [ + config, + 'application.vala', + 'file.vala', + 'process.vala', + 'time.vala', + 'variable.vala', +] + +lib = library( + meson.project_name(), + sources, + dependencies: deps, + vala_header: meson.project_name() + '.h', + vala_vapi: meson.project_name() + '-' + api_version + '.vapi', + vala_gir: gir, + version: meson.project_version(), + install: true, + install_dir: [true, true, true, true], +) + +import('pkgconfig').generate( + lib, + name: meson.project_name(), + filebase: meson.project_name() + '-' + api_version, + version: meson.project_version(), + subdirs: meson.project_name(), + requires: deps, + install_dir: libdir / 'pkgconfig', +) + +custom_target( + typelib, + command: [ + find_program('g-ir-compiler'), + '--output', '@OUTPUT@', + '--shared-library', libdir / '@PLAINNAME@', + meson.current_build_dir() / gir, + ], + input: lib, + output: typelib, + depends: lib, + install: true, + install_dir: libdir / 'girepository-1.0', +) + +executable( + 'astal', + ['cli.vala', sources], + dependencies: deps, + install: true, +) diff --git a/lib/astal/io/process.vala b/lib/astal/io/process.vala new file mode 100644 index 0000000..e8637ab --- /dev/null +++ b/lib/astal/io/process.vala @@ -0,0 +1,119 @@ +public class AstalIO.Process : Object { + private void read_stream(DataInputStream stream, bool err) { + stream.read_line_utf8_async.begin(Priority.DEFAULT, null, (_, res) => { + try { + var output = stream.read_line_utf8_async.end(res); + if (output != null) { + if (err) + stdout(output.strip()); + else + stderr(output.strip()); + + read_stream(stream, err); + } + } catch (Error err) { + printerr("%s\n", err.message); + } + }); + } + + private DataInputStream out_stream; + private DataInputStream err_stream; + private DataOutputStream in_stream; + private Subprocess process; + public string[] argv { construct; get; } + + public signal void stdout (string out); + public signal void stderr (string err); + + public void kill() { + process.force_exit(); + } + + public void signal(int signal_num) { + process.send_signal(signal_num); + } + + public void write(string in) throws Error { + in_stream.put_string(in); + } + + public void write_async(string in) { + in_stream.write_all_async.begin( + in.data, + Priority.DEFAULT, null, (_, res) => { + try { + in_stream.write_all_async.end(res, null); + } catch (Error err) { + printerr("%s\n", err.message); + } + } + ); + } + + public Process.subprocessv(string[] cmd) throws Error { + Object(argv: cmd); + process = new Subprocess.newv(cmd, + SubprocessFlags.STDIN_PIPE | + SubprocessFlags.STDERR_PIPE | + SubprocessFlags.STDOUT_PIPE + ); + out_stream = new DataInputStream(process.get_stdout_pipe()); + err_stream = new DataInputStream(process.get_stderr_pipe()); + in_stream = new DataOutputStream(process.get_stdin_pipe()); + read_stream(out_stream, true); + read_stream(err_stream, false); + } + + public static Process subprocess(string cmd) throws Error { + string[] argv; + Shell.parse_argv(cmd, out argv); + return new Process.subprocessv(argv); + } + + public static string execv(string[] cmd) throws Error { + var process = new Subprocess.newv( + cmd, + SubprocessFlags.STDERR_PIPE | + SubprocessFlags.STDOUT_PIPE + ); + + string err_str, out_str; + process.communicate_utf8(null, null, out out_str, out err_str); + var success = process.get_successful(); + process.dispose(); + if (success) + return out_str.strip(); + else + throw new IOError.FAILED(err_str.strip()); + } + + public static string exec(string cmd) throws Error { + string[] argv; + Shell.parse_argv(cmd, out argv); + return Process.execv(argv); + } + + public static async string exec_asyncv(string[] cmd) throws Error { + var process = new Subprocess.newv( + cmd, + SubprocessFlags.STDERR_PIPE | + SubprocessFlags.STDOUT_PIPE + ); + + string err_str, out_str; + yield process.communicate_utf8_async(null, null, out out_str, out err_str); + var success = process.get_successful(); + process.dispose(); + if (success) + return out_str.strip(); + else + throw new IOError.FAILED(err_str.strip()); + } + + public static async string exec_async(string cmd) throws Error { + string[] argv; + Shell.parse_argv(cmd, out argv); + return yield exec_asyncv(argv); + } +} diff --git a/lib/astal/io/time.vala b/lib/astal/io/time.vala new file mode 100644 index 0000000..1446441 --- /dev/null +++ b/lib/astal/io/time.vala @@ -0,0 +1,71 @@ +public class AstalIO.Time : Object { + public signal void now (); + public signal void cancelled (); + private Cancellable cancellable; + private uint timeout_id; + private bool fulfilled = false; + + construct { + cancellable = new Cancellable(); + cancellable.cancelled.connect(() => { + if (!fulfilled) { + Source.remove(timeout_id); + cancelled(); + dispose(); + } + }); + } + + private void connect_closure(Closure? closure) { + if (closure == null) + return; + + now.connect(() => { + Value ret = Value(Type.POINTER); // void + closure.invoke(ref ret, {}); + }); + } + + public Time.interval_prio(uint interval, int prio = Priority.DEFAULT, Closure? fn) { + connect_closure(fn); + Idle.add_once(() => now()); + timeout_id = Timeout.add(interval, () => { + now(); + return Source.CONTINUE; + }, prio); + } + + public Time.timeout_prio(uint timeout, int prio = Priority.DEFAULT, Closure? fn) { + connect_closure(fn); + timeout_id = Timeout.add(timeout, () => { + now(); + fulfilled = true; + return Source.REMOVE; + }, prio); + } + + public Time.idle_prio(int prio = Priority.DEFAULT_IDLE, Closure? fn) { + connect_closure(fn); + timeout_id = Idle.add(() => { + now(); + fulfilled = true; + return Source.REMOVE; + }, prio); + } + + public static Time interval(uint interval, Closure? fn) { + return new Time.interval_prio(interval, Priority.DEFAULT, fn); + } + + public static Time timeout(uint timeout, Closure? fn) { + return new Time.timeout_prio(timeout, Priority.DEFAULT, fn); + } + + public static Time idle(Closure? fn) { + return new Time.idle_prio(Priority.DEFAULT_IDLE, fn); + } + + public void cancel() { + cancellable.cancel(); + } +} diff --git a/lib/astal/io/variable.vala b/lib/astal/io/variable.vala new file mode 100644 index 0000000..2a395b4 --- /dev/null +++ b/lib/astal/io/variable.vala @@ -0,0 +1,194 @@ +public class AstalIO.VariableBase : Object { + public signal void changed (); + public signal void dropped (); + public signal void error (string err); + + // lua-lgi crashes when using its emitting mechanism + public void emit_changed() { changed(); } + public void emit_dropped() { dropped(); } + public void emit_error(string err) { this.error(err); } + + ~VariableBase() { + dropped(); + } +} + +public class AstalIO.Variable : VariableBase { + public Value value { owned get; set; } + + private uint poll_id = 0; + private Process? watch_proc; + + private uint poll_interval { get; set; default = 1000; } + private string[] poll_exec { get; set; } + private Closure? poll_transform { get; set; } + private Closure? poll_fn { get; set; } + + private Closure? watch_transform { get; set; } + private string[] watch_exec { get; set; } + + public Variable(Value init) { + Object(value: init); + } + + public Variable poll( + uint interval, + string exec, + Closure? transform + ) throws Error { + string[] argv; + Shell.parse_argv(exec, out argv); + return pollv(interval, argv, transform); + } + + public Variable pollv( + uint interval, + string[] execv, + Closure? transform + ) throws Error { + if (is_polling()) + stop_poll(); + + poll_interval = interval; + poll_exec = execv; + poll_transform = transform; + poll_fn = null; + start_poll(); + return this; + } + + public Variable pollfn( + uint interval, + Closure fn + ) throws Error { + if (is_polling()) + stop_poll(); + + poll_interval = interval; + poll_fn = fn; + poll_exec = null; + start_poll(); + return this; + } + + public Variable watch( + string exec, + Closure? transform + ) throws Error { + string[] argv; + Shell.parse_argv(exec, out argv); + return watchv(argv, transform); + } + + public Variable watchv( + string[] execv, + Closure? transform + ) throws Error { + if (is_watching()) + stop_watch(); + + watch_exec = execv; + watch_transform = transform; + start_watch(); + return this; + } + + construct { + notify["value"].connect(() => changed()); + dropped.connect(() => { + if (is_polling()) + stop_poll(); + + if (is_watching()) + stop_watch(); + }); + } + + private void set_closure(string val, Closure? transform) { + if (transform != null) { + var str = Value(typeof(string)); + str.set_string(val); + + var ret_val = Value(this.value.type()); + transform.invoke(ref ret_val, { str, this.value }); + this.value = ret_val; + } + else { + if (this.value.type() == Type.STRING && this.value.get_string() == val) + return; + + var str = Value(typeof(string)); + str.set_string(val); + this.value = str; + } + } + + private void set_fn() { + var ret_val = Value(this.value.type()); + poll_fn.invoke(ref ret_val, { this.value }); + this.value = ret_val; + } + + public void start_poll() throws Error { + return_if_fail(poll_id == 0); + + if (poll_fn != null) { + set_fn(); + poll_id = Timeout.add(poll_interval, () => { + set_fn(); + return Source.CONTINUE; + }, Priority.DEFAULT); + } + if (poll_exec != null) { + Process.exec_asyncv.begin(poll_exec, (_, res) => { + try { + var str = Process.exec_asyncv.end(res); + set_closure(str, poll_transform); + } catch (Error err) { + this.error(err.message); + } + }); + poll_id = Timeout.add(poll_interval, () => { + Process.exec_asyncv.begin(poll_exec, (_, res) => { + try { + var str = Process.exec_asyncv.end(res); + set_closure(str, poll_transform); + } catch (Error err) { + this.error(err.message); + Source.remove(poll_id); + poll_id = 0; + } + }); + return Source.CONTINUE; + }, Priority.DEFAULT); + } + } + + public void start_watch() throws Error { + return_if_fail(watch_proc == null); + return_if_fail(watch_exec != null); + + watch_proc = new Process.subprocessv(watch_exec); + watch_proc.stdout.connect((str) => set_closure(str, watch_transform)); + watch_proc.stderr.connect((str) => this.error(str)); + } + + public void stop_poll() { + return_if_fail(poll_id != 0); + Source.remove(poll_id); + poll_id = 0; + } + + public void stop_watch() { + return_if_fail(watch_proc != null); + watch_proc.kill(); + watch_proc = null; + } + + public bool is_polling() { return poll_id > 0; } + public bool is_watching() { return watch_proc != null; } + + ~Variable() { + dropped(); + } +} diff --git a/lib/astal/io/version b/lib/astal/io/version new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/lib/astal/io/version @@ -0,0 +1 @@ +0.1.0 -- cgit v1.2.3 From 9fab13452a26ed55c01047d4225f699f43bba20d Mon Sep 17 00:00:00 2001 From: Aylur Date: Mon, 14 Oct 2024 16:45:03 +0000 Subject: feat: Astal3 --- lib/astal/gtk3/meson.build | 17 ++ lib/astal/gtk3/src/application.vala | 217 ++++++++++++++++++++ lib/astal/gtk3/src/config.vala.in | 6 + lib/astal/gtk3/src/idle-inhibit.c | 114 +++++++++++ lib/astal/gtk3/src/idle-inhibit.h | 22 ++ lib/astal/gtk3/src/meson.build | 120 +++++++++++ lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi | 13 ++ lib/astal/gtk3/src/widget/box.vala | 50 +++++ lib/astal/gtk3/src/widget/button.vala | 99 +++++++++ lib/astal/gtk3/src/widget/centerbox.vala | 52 +++++ lib/astal/gtk3/src/widget/circularprogress.vala | 180 +++++++++++++++++ lib/astal/gtk3/src/widget/eventbox.vala | 64 ++++++ lib/astal/gtk3/src/widget/icon.vala | 105 ++++++++++ lib/astal/gtk3/src/widget/label.vala | 18 ++ lib/astal/gtk3/src/widget/levelbar.vala | 13 ++ lib/astal/gtk3/src/widget/overlay.vala | 57 ++++++ lib/astal/gtk3/src/widget/scrollable.vala | 40 ++++ lib/astal/gtk3/src/widget/slider.vala | 71 +++++++ lib/astal/gtk3/src/widget/stack.vala | 26 +++ lib/astal/gtk3/src/widget/widget.vala | 157 +++++++++++++++ lib/astal/gtk3/src/widget/window.vala | 246 +++++++++++++++++++++++ lib/astal/gtk3/version | 1 + 22 files changed, 1688 insertions(+) create mode 100644 lib/astal/gtk3/meson.build create mode 100644 lib/astal/gtk3/src/application.vala create mode 100644 lib/astal/gtk3/src/config.vala.in create mode 100644 lib/astal/gtk3/src/idle-inhibit.c create mode 100644 lib/astal/gtk3/src/idle-inhibit.h create mode 100644 lib/astal/gtk3/src/meson.build create mode 100644 lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi create mode 100644 lib/astal/gtk3/src/widget/box.vala create mode 100644 lib/astal/gtk3/src/widget/button.vala create mode 100644 lib/astal/gtk3/src/widget/centerbox.vala create mode 100644 lib/astal/gtk3/src/widget/circularprogress.vala create mode 100644 lib/astal/gtk3/src/widget/eventbox.vala create mode 100644 lib/astal/gtk3/src/widget/icon.vala create mode 100644 lib/astal/gtk3/src/widget/label.vala create mode 100644 lib/astal/gtk3/src/widget/levelbar.vala create mode 100644 lib/astal/gtk3/src/widget/overlay.vala create mode 100644 lib/astal/gtk3/src/widget/scrollable.vala create mode 100644 lib/astal/gtk3/src/widget/slider.vala create mode 100644 lib/astal/gtk3/src/widget/stack.vala create mode 100644 lib/astal/gtk3/src/widget/widget.vala create mode 100644 lib/astal/gtk3/src/widget/window.vala create mode 100644 lib/astal/gtk3/version diff --git a/lib/astal/gtk3/meson.build b/lib/astal/gtk3/meson.build new file mode 100644 index 0000000..b1a0b43 --- /dev/null +++ b/lib/astal/gtk3/meson.build @@ -0,0 +1,17 @@ +project( + 'astal', + 'vala', + 'c', + version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(), + meson_version: '>= 0.62.0', + default_options: [ + 'warning_level=2', + 'werror=false', + 'c_std=gnu11', + ], +) + +libdir = get_option('prefix') / get_option('libdir') +pkgdatadir = get_option('prefix') / get_option('datadir') / 'astal' + +subdir('src') diff --git a/lib/astal/gtk3/src/application.vala b/lib/astal/gtk3/src/application.vala new file mode 100644 index 0000000..8539aa0 --- /dev/null +++ b/lib/astal/gtk3/src/application.vala @@ -0,0 +1,217 @@ +[DBus (name="io.Astal.Application")] +public class Astal.Application : Gtk.Application, AstalIO.Application { + private List css_providers = new List(); + private SocketService service; + private DBusConnection conn; + private string socket_path { get; set; } + private string _instance_name; + + [DBus (visible=false)] + public signal void monitor_added(Gdk.Monitor monitor); + + [DBus (visible=false)] + public signal void monitor_removed(Gdk.Monitor monitor); + + [DBus (visible=false)] + public signal void window_toggled(Gtk.Window window); + + [DBus (visible=false)] + public List monitors { + owned get { + var display = Gdk.Display.get_default(); + var list = new List(); + for (var i = 0; i <= display.get_n_monitors(); ++i) { + var mon = display.get_monitor(i); + if (mon != null) { + list.append(mon); + } + } + return list; + } + } + + [DBus (visible=false)] + public string instance_name { + owned get { return _instance_name; } + construct set { + application_id = "io.Astal." + value; + _instance_name = value; + } + } + + [DBus (visible=false)] + public List windows { + get { return get_windows(); } + } + + [DBus (visible=false)] + public Gtk.Settings settings { + get { return Gtk.Settings.get_default(); } + } + + [DBus (visible=false)] + public Gdk.Screen screen { + get { return Gdk.Screen.get_default(); } + } + + [DBus (visible=false)] + public string gtk_theme { + owned get { return settings.gtk_theme_name; } + set { settings.gtk_theme_name = value; } + } + + [DBus (visible=false)] + public string icon_theme { + owned get { return settings.gtk_icon_theme_name; } + set { settings.gtk_icon_theme_name = value; } + } + + [DBus (visible=false)] + public string cursor_theme { + owned get { return settings.gtk_cursor_theme_name; } + set { settings.gtk_cursor_theme_name = value; } + } + + [DBus (visible=false)] + public void reset_css() { + foreach(var provider in css_providers) { + Gtk.StyleContext.remove_provider_for_screen(screen, provider); + } + css_providers = new List(); + } + + public void inspector() throws DBusError, IOError { + Gtk.Window.set_interactive_debugging(true); + } + + [DBus (visible=false)] + public Gtk.Window? get_window(string name) { + foreach(var win in windows) { + if (win.name == name) + return win; + } + + critical("no window with name \"%s\"".printf(name)); + return null; + } + + public void toggle_window(string window) throws Error { + var win = get_window(window); + if (win != null) { + win.visible = !win.visible; + } else { + throw new IOError.FAILED("window not found"); + } + } + + [DBus (visible=false)] + public void apply_css(string style, bool reset = false) { + var provider = new Gtk.CssProvider(); + + if (reset) + reset_css(); + + try { + if (FileUtils.test(style, FileTest.EXISTS)) + provider.load_from_path(style); + else + provider.load_from_data(style); + } catch (Error err) { + critical(err.message); + } + + Gtk.StyleContext.add_provider_for_screen( + screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_USER); + + css_providers.append(provider); + } + + [DBus (visible=false)] + public void add_icons(string? path) { + if (path != null) { + Gtk.IconTheme.get_default().prepend_search_path(path); + } + } + + [DBus (visible=false)] + public virtual void request(string msg, SocketConnection conn) { + AstalIO.write_sock.begin(conn, @"missing response implementation on $application_id"); + } + + /** + * should be called before `run()` + * the return value indicates if instance is already running + */ + [DBus (visible=false)] + public void acquire_socket() { + try { + service = AstalIO.acquire_socket(this); + + Bus.own_name( + BusType.SESSION, + "io.Astal." + instance_name, + BusNameOwnerFlags.NONE, + (conn) => { + try { + this.conn = conn; + conn.register_object("/io/Astal/Application", this); + } catch (Error err) { + critical(err.message); + } + }, + () => {}, + () => {} + ); + } catch (Error err) { + critical("could not acquire socket %s\n", application_id); + critical(err.message); + } + } + + public new void quit() throws DBusError, IOError { + if (service != null) { + if (FileUtils.test(socket_path, GLib.FileTest.EXISTS)){ + try { + File.new_for_path(socket_path).delete(null); + } catch (Error err) { + warning(err.message); + } + } + } + + base.quit(); + } + + construct { + if (instance_name == null) + instance_name = "astal"; + + activate.connect(() => { + var display = Gdk.Display.get_default(); + display.monitor_added.connect((mon) => { + monitor_added(mon); + notify_property("monitors"); + }); + display.monitor_removed.connect((mon) => { + monitor_removed(mon); + notify_property("monitors"); + }); + }); + + window_added.connect((window) => { + ulong id1, id2; + id1 = window.notify["visible"].connect(() => window_toggled(window)); + id2 = window_removed.connect((removed) => { + if (removed == window) { + window.disconnect(id1); + this.disconnect(id2); + } + }); + }); + + shutdown.connect(() => { try { quit(); } catch(Error err) {} }); + Unix.signal_add(1, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + Unix.signal_add(2, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + Unix.signal_add(15, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + } +} diff --git a/lib/astal/gtk3/src/config.vala.in b/lib/astal/gtk3/src/config.vala.in new file mode 100644 index 0000000..88bfe9c --- /dev/null +++ b/lib/astal/gtk3/src/config.vala.in @@ -0,0 +1,6 @@ +namespace Astal { + public const int MAJOR_VERSION = @MAJOR_VERSION@; + public const int MINOR_VERSION = @MINOR_VERSION@; + public const int MICRO_VERSION = @MICRO_VERSION@; + public const string VERSION = "@VERSION@"; +} diff --git a/lib/astal/gtk3/src/idle-inhibit.c b/lib/astal/gtk3/src/idle-inhibit.c new file mode 100644 index 0000000..48f2471 --- /dev/null +++ b/lib/astal/gtk3/src/idle-inhibit.c @@ -0,0 +1,114 @@ +#include "idle-inhibit.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "idle-inhibit-unstable-v1-client.h" + +struct _AstalInhibitManager { + GObject parent_instance; +}; + +typedef struct { + gboolean init; + struct wl_registry* wl_registry; + struct wl_display* display; + struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager; +} AstalInhibitManagerPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(AstalInhibitManager, astal_inhibit_manager, G_TYPE_OBJECT) + +AstalInhibitor* astal_inhibit_manager_inhibit(AstalInhibitManager* self, GtkWindow* window) { + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + g_assert_true(priv->init); + GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); + struct wl_surface* surface = gdk_wayland_window_get_wl_surface(gdk_window); + return zwp_idle_inhibit_manager_v1_create_inhibitor(priv->idle_inhibit_manager, surface); +} + +static void global_registry_handler(void* data, struct wl_registry* registry, uint32_t id, + const char* interface, uint32_t version) { + AstalInhibitManager* self = ASTAL_INHIBIT_MANAGER(data); + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + + if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { + priv->idle_inhibit_manager = + wl_registry_bind(registry, id, &zwp_idle_inhibit_manager_v1_interface, 1); + } +} + +static void global_registry_remover(void* data, struct wl_registry* registry, uint32_t id) { + // neither inhibit_manager nor inhibitor is going to be removed by the compositor, so we don't + // need do anything here. +} + +static const struct wl_registry_listener registry_listener = {global_registry_handler, + global_registry_remover}; + +static gboolean astal_inhibit_manager_wayland_init(AstalInhibitManager* self) { + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + + if (priv->init) return TRUE; + + GdkDisplay* gdk_display = gdk_display_get_default(); + priv->display = gdk_wayland_display_get_wl_display(gdk_display); + + priv->wl_registry = wl_display_get_registry(priv->display); + wl_registry_add_listener(priv->wl_registry, ®istry_listener, self); + + wl_display_roundtrip(priv->display); + + if (priv->idle_inhibit_manager == NULL) { + g_critical("Can not connect idle inhibitor protocol"); + return FALSE; + } + + priv->init = TRUE; + return TRUE; +} + +AstalInhibitManager* astal_inhibit_manager_get_default() { + static AstalInhibitManager* self = NULL; + + if (self == NULL) { + self = g_object_new(ASTAL_TYPE_INHIBIT_MANAGER, NULL); + if (!astal_inhibit_manager_wayland_init(self)) { + g_object_unref(self); + self = NULL; + } + } + + return self; +} + +static void astal_inhibit_manager_init(AstalInhibitManager* self) { + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + priv->init = FALSE; + priv->display = NULL; + priv->wl_registry = NULL; + priv->idle_inhibit_manager = NULL; +} + +static void astal_inhibit_manager_finalize(GObject* object) { + AstalInhibitManager* self = ASTAL_INHIBIT_MANAGER(object); + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + + if (priv->display != NULL) wl_display_roundtrip(priv->display); + + if (priv->wl_registry != NULL) wl_registry_destroy(priv->wl_registry); + if (priv->idle_inhibit_manager != NULL) + zwp_idle_inhibit_manager_v1_destroy(priv->idle_inhibit_manager); + + G_OBJECT_CLASS(astal_inhibit_manager_parent_class)->finalize(object); +} + +static void astal_inhibit_manager_class_init(AstalInhibitManagerClass* class) { + GObjectClass* object_class = G_OBJECT_CLASS(class); + object_class->finalize = astal_inhibit_manager_finalize; +} diff --git a/lib/astal/gtk3/src/idle-inhibit.h b/lib/astal/gtk3/src/idle-inhibit.h new file mode 100644 index 0000000..5e9a3ab --- /dev/null +++ b/lib/astal/gtk3/src/idle-inhibit.h @@ -0,0 +1,22 @@ +#ifndef ASTAL_IDLE_INHIBITOR_H +#define ASTAL_IDLE_INHIBITOR_H + +#include +#include + +#include "idle-inhibit-unstable-v1-client.h" + +G_BEGIN_DECLS + +#define ASTAL_TYPE_INHIBIT_MANAGER (astal_inhibit_manager_get_type()) + +G_DECLARE_FINAL_TYPE(AstalInhibitManager, astal_inhibit_manager, ASTAL, INHIBIT_MANAGER, GObject) + +typedef struct zwp_idle_inhibitor_v1 AstalInhibitor; + +AstalInhibitManager* astal_inhibit_manager_get_default(); +AstalInhibitor* astal_inhibit_manager_inhibit(AstalInhibitManager* self, GtkWindow* window); + +G_END_DECLS + +#endif // !ASTAL_IDLE_INHIBITOR_H diff --git a/lib/astal/gtk3/src/meson.build b/lib/astal/gtk3/src/meson.build new file mode 100644 index 0000000..c8c7df2 --- /dev/null +++ b/lib/astal/gtk3/src/meson.build @@ -0,0 +1,120 @@ +version_split = meson.project_version().split('.') +api_version = version_split[0] + '.' + version_split[1] +gir = 'Astal-' + api_version + '.gir' +typelib = 'Astal-' + api_version + '.typelib' + +vapi_dir = meson.current_source_dir() / 'vapi' +add_project_arguments(['--vapidir', vapi_dir], language: 'vala') + +config = configure_file( + input: 'config.vala.in', + output: 'config.vala', + configuration: { + 'VERSION': meson.project_version(), + 'MAJOR_VERSION': version_split[0], + 'MINOR_VERSION': version_split[1], + 'MICRO_VERSION': version_split[2], + }, +) + +pkgconfig_deps = [ + dependency('astal-io-0.1'), + dependency('glib-2.0'), + dependency('gio-unix-2.0'), + dependency('gobject-2.0'), + dependency('gio-2.0'), + dependency('gtk+-3.0'), + dependency('gdk-pixbuf-2.0'), + dependency('gtk-layer-shell-0'), + dependency('wayland-client'), +] + +deps = pkgconfig_deps + meson.get_compiler('c').find_library('m') + +wayland_protos = dependency('wayland-protocols') +wayland_scanner = find_program('wayland-scanner') + +wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir') + +gen_client_header = generator( + wayland_scanner, + output: ['@BASENAME@-client.h'], + arguments: ['-c', 'client-header', '@INPUT@', '@BUILD_DIR@/@BASENAME@-client.h'], +) + +gen_private_code = generator( + wayland_scanner, + output: ['@BASENAME@.c'], + arguments: ['-c', 'private-code', '@INPUT@', '@BUILD_DIR@/@BASENAME@.c'], +) + +protocols = [ + join_paths(wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'), +] + +client_protocol_srcs = [] + +foreach protocol : protocols + client_header = gen_client_header.process(protocol) + code = gen_private_code.process(protocol) + client_protocol_srcs += [client_header, code] +endforeach + +sources = [ + config, + 'widget/box.vala', + 'widget/button.vala', + 'widget/centerbox.vala', + 'widget/circularprogress.vala', + 'widget/eventbox.vala', + 'widget/icon.vala', + 'widget/label.vala', + 'widget/levelbar.vala', + 'widget/overlay.vala', + 'widget/scrollable.vala', + 'widget/slider.vala', + 'widget/stack.vala', + 'widget/widget.vala', + 'widget/window.vala', + 'application.vala', + 'idle-inhibit.h', + 'idle-inhibit.c', +] + client_protocol_srcs + +lib = library( + meson.project_name(), + sources, + dependencies: deps, + vala_args: ['--pkg', 'AstalInhibitManager'], + vala_header: meson.project_name() + '.h', + vala_vapi: meson.project_name() + '-' + api_version + '.vapi', + vala_gir: gir, + version: meson.project_version(), + install: true, + install_dir: [true, true, true, true], +) + +import('pkgconfig').generate( + lib, + name: meson.project_name(), + filebase: meson.project_name() + '-' + api_version, + version: meson.project_version(), + subdirs: meson.project_name(), + requires: pkgconfig_deps, + install_dir: libdir / 'pkgconfig', +) + +custom_target( + typelib, + command: [ + find_program('g-ir-compiler'), + '--output', '@OUTPUT@', + '--shared-library', libdir / '@PLAINNAME@', + meson.current_build_dir() / gir, + ], + input: lib, + output: typelib, + depends: lib, + install: true, + install_dir: libdir / 'girepository-1.0', +) diff --git a/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi b/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi new file mode 100644 index 0000000..6232a3c --- /dev/null +++ b/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi @@ -0,0 +1,13 @@ +[CCode (cprefix = "Astal", gir_namespace = "Astal", lower_case_cprefix = "astal_")] +namespace Astal { + [CCode (cheader_filename = "idle-inhibit.h", type_id = "astal_idle_inhibit_manager_get_type()")] + public class InhibitManager : GLib.Object { + public static unowned InhibitManager? get_default(); + public Inhibitor inhibit (Gtk.Window window); + } + + [CCode (cheader_filename = "idle-inhibit.h", free_function = "zwp_idle_inhibitor_v1_destroy")] + [Compact] + public class Inhibitor { + } +} diff --git a/lib/astal/gtk3/src/widget/box.vala b/lib/astal/gtk3/src/widget/box.vala new file mode 100644 index 0000000..d23a799 --- /dev/null +++ b/lib/astal/gtk3/src/widget/box.vala @@ -0,0 +1,50 @@ +public class Astal.Box : Gtk.Box { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + public List children { + set { _set_children(value); } + owned get { return get_children(); } + } + + public new Gtk.Widget child { + owned get { return _get_child(); } + set { _set_child(value); } + } + + construct { + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + } + + private void _set_child(Gtk.Widget child) { + var list = new List(); + list.append(child); + _set_children(list); + } + + private Gtk.Widget? _get_child() { + foreach(var child in get_children()) + return child; + + return null; + } + + private void _set_children(List arr) { + foreach(var child in get_children()) { + remove(child); + } + + foreach(var child in arr) + add(child); + } + + public Box(bool vertical, List children) { + this.vertical = vertical; + _set_children(children); + } +} diff --git a/lib/astal/gtk3/src/widget/button.vala b/lib/astal/gtk3/src/widget/button.vala new file mode 100644 index 0000000..bc10577 --- /dev/null +++ b/lib/astal/gtk3/src/widget/button.vala @@ -0,0 +1,99 @@ +public class Astal.Button : Gtk.Button { + public signal void hover (HoverEvent event); + public signal void hover_lost (HoverEvent event); + public signal void click (ClickEvent event); + public signal void click_release (ClickEvent event); + public signal void scroll (ScrollEvent event); + + construct { + add_events(Gdk.EventMask.SCROLL_MASK); + add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); + + enter_notify_event.connect((self, event) => { + hover(HoverEvent(event) { lost = false }); + }); + + leave_notify_event.connect((self, event) => { + hover_lost(HoverEvent(event) { lost = true }); + }); + + button_press_event.connect((event) => { + click(ClickEvent(event) { release = false }); + }); + + button_release_event.connect((event) => { + click_release(ClickEvent(event) { release = true }); + }); + + scroll_event.connect((event) => { + scroll(ScrollEvent(event)); + }); + } +} + +public enum Astal.MouseButton { + PRIMARY = 1, + MIDDLE = 2, + SECONDARY = 3, + BACK = 4, + FORWARD = 5, +} + +// these structs are here because gjs converts every event +// into a union Gdk.Event, which cannot be destructured +// and are not as convinent to work with as a struct +public struct Astal.ClickEvent { + bool release; + uint time; + double x; + double y; + Gdk.ModifierType modifier; + MouseButton button; + + public ClickEvent(Gdk.EventButton event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.button = (MouseButton)event.button; + this.modifier = event.state; + } +} + +public struct Astal.HoverEvent { + bool lost; + uint time; + double x; + double y; + Gdk.ModifierType modifier; + Gdk.CrossingMode mode; + Gdk.NotifyType detail; + + public HoverEvent(Gdk.EventCrossing event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.modifier = event.state; + this.mode = event.mode; + this.detail = event.detail; + } +} + +public struct Astal.ScrollEvent { + uint time; + double x; + double y; + Gdk.ModifierType modifier; + Gdk.ScrollDirection direction; + double delta_x; + double delta_y; + + public ScrollEvent(Gdk.EventScroll event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.modifier = event.state; + this.direction = event.direction; + this.delta_x = event.delta_x; + this.delta_y = event.delta_y; + } +} diff --git a/lib/astal/gtk3/src/widget/centerbox.vala b/lib/astal/gtk3/src/widget/centerbox.vala new file mode 100644 index 0000000..89bf50b --- /dev/null +++ b/lib/astal/gtk3/src/widget/centerbox.vala @@ -0,0 +1,52 @@ +public class Astal.CenterBox : Gtk.Box { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + construct { + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + } + + static construct { + set_css_name("centerbox"); + } + + private Gtk.Widget _start_widget; + public Gtk.Widget start_widget { + get { return _start_widget; } + set { + if (_start_widget != null) + remove(_start_widget); + + if (value != null) + pack_start(value, true, true, 0); + } + } + + private Gtk.Widget _end_widget; + public Gtk.Widget end_widget { + get { return _end_widget; } + set { + if (_end_widget != null) + remove(_end_widget); + + if (value != null) + pack_end(value, true, true, 0); + } + } + + public Gtk.Widget center_widget { + get { return get_center_widget(); } + set { + if (center_widget != null) + remove(center_widget); + + if (value != null) + set_center_widget(value); + } + } +} diff --git a/lib/astal/gtk3/src/widget/circularprogress.vala b/lib/astal/gtk3/src/widget/circularprogress.vala new file mode 100644 index 0000000..dd7c97b --- /dev/null +++ b/lib/astal/gtk3/src/widget/circularprogress.vala @@ -0,0 +1,180 @@ +public class Astal.CircularProgress : Gtk.Bin { + public double start_at { get; set; } + public double end_at { get; set; } + public double value { get; set; } + public bool inverted { get; set; } + public bool rounded { get; set; } + + construct { + notify["start-at"].connect(queue_draw); + notify["end-at"].connect(queue_draw); + notify["value"].connect(queue_draw); + notify["inverted"].connect(queue_draw); + notify["rounded"].connect(queue_draw); + notify["child"].connect(queue_draw); + } + + static construct { + set_css_name("circular-progress"); + } + + public override void get_preferred_height(out int minh, out int nath) { + var val = get_style_context().get_property("min-height", Gtk.StateFlags.NORMAL); + if (val.get_int() <= 0) { + minh = 40; + nath = 40; + } + + minh = val.get_int(); + nath = val.get_int(); + } + + public override void get_preferred_width(out int minw, out int natw) { + var val = get_style_context().get_property("min-width", Gtk.StateFlags.NORMAL); + if (val.get_int() <= 0) { + minw = 40; + natw = 40; + } + + minw = val.get_int(); + natw = val.get_int(); + } + + private double to_radian(double percentage) { + percentage = Math.floor(percentage * 100); + return (percentage / 100) * (2 * Math.PI); + } + + private bool is_full_circle(double start, double end, double epsilon = 1e-10) { + // Ensure that start and end are between 0 and 1 + start = (start % 1 + 1) % 1; + end = (end % 1 + 1) % 1; + + // Check if the difference between start and end is close to 1 + return Math.fabs(start - end) <= epsilon; + } + + private double scale_arc_value(double start, double end, double value) { + // Ensure that start and end are between 0 and 1 + start = (start % 1 + 1) % 1; + end = (end % 1 + 1) % 1; + + // Calculate the length of the arc + var arc_length = end - start; + if (arc_length < 0) + arc_length += 1; // Adjust for circular representation + + // Calculate the position on the arc based on the percentage value + var scaled = arc_length + value; + + // Ensure the position is between 0 and 1 + return (scaled % 1 + 1) % 1; + } + + private double min(double[] arr) { + double min = arr[0]; + foreach(var i in arr) + if (min > i) min = i; + return min; + } + + private double max(double[] arr) { + double max = arr[0]; + foreach(var i in arr) + if (max < i) max = i; + return max; + } + + public override bool draw(Cairo.Context cr) { + Gtk.Allocation allocation; + get_allocation(out allocation); + + var styles = get_style_context(); + var width = allocation.width; + var height = allocation.height; + var thickness = styles.get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); + var margin = styles.get_margin(Gtk.StateFlags.NORMAL); + var fg = styles.get_color(Gtk.StateFlags.NORMAL); + var bg = styles.get_background_color(Gtk.StateFlags.NORMAL); + + var bg_stroke = thickness + min({margin.bottom, margin.top, margin.left, margin.right}); + var fg_stroke = thickness; + var radius = min({width, height}) / 2.0 - max({bg_stroke, fg_stroke}) / 2.0; + var center_x = width / 2; + var center_y = height / 2; + + var start_background = to_radian(start_at); + var end_background = to_radian(end_at); + var ranged_value = value + start_at; + + var is_circle = is_full_circle(this.start_at, this.end_at); + + if (is_circle) { + // Redefine end_draw in radius to create an accurate full circle + end_background = start_background + 2 * Math.PI; + ranged_value = to_radian(value); + } else { + // Range the value for the arc shape + ranged_value = to_radian(scale_arc_value( + start_at, + end_at, + value + )); + } + + double start_progress, end_progress; + + if (inverted) { + start_progress = end_background - ranged_value; + end_progress = end_background; + } else { + start_progress = start_background; + end_progress = start_background + ranged_value; + } + + // Draw background + cr.set_source_rgba(bg.red, bg.green, bg.blue, bg.alpha); + cr.arc(center_x, center_y, radius, start_background, end_background); + cr.set_line_width(bg_stroke); + cr.stroke(); + + // Draw rounded background ends + if (rounded) { + var start_x = center_x + Math.cos(start_background) * radius; + var start_y = center_y + Math.sin(start_background) * radius; + var end_x = center_x + Math.cos(end_background) * radius; + var end_y = center_y + Math.sin(end_background) * radius; + cr.set_line_width(0); + cr.arc(start_x, start_y, bg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + cr.arc(end_x, end_y, bg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + } + + // Draw progress + cr.set_source_rgba(fg.red, fg.green, fg.blue, fg.alpha); + cr.arc(center_x, center_y, radius, start_progress, end_progress); + cr.set_line_width(fg_stroke); + cr.stroke(); + + // Draw rounded progress ends + if (rounded) { + var start_x = center_x + Math.cos(start_progress) * radius; + var start_y = center_y + Math.sin(start_progress) * radius; + var end_x = center_x + Math.cos(end_progress) * radius; + var end_y = center_y + Math.sin(end_progress) * radius; + cr.set_line_width(0); + cr.arc(start_x, start_y, fg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + cr.arc(end_x, end_y, fg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + } + + if (get_child() != null) { + get_child().size_allocate(allocation); + propagate_draw(get_child(), cr); + } + + return true; + } +} diff --git a/lib/astal/gtk3/src/widget/eventbox.vala b/lib/astal/gtk3/src/widget/eventbox.vala new file mode 100644 index 0000000..611da2a --- /dev/null +++ b/lib/astal/gtk3/src/widget/eventbox.vala @@ -0,0 +1,64 @@ +public class Astal.EventBox : Gtk.EventBox { + public signal void hover (HoverEvent event); + public signal void hover_lost (HoverEvent event); + public signal void click (ClickEvent event); + public signal void click_release (ClickEvent event); + public signal void scroll (ScrollEvent event); + public signal void motion (MotionEvent event); + + static construct { + set_css_name("eventbox"); + } + + construct { + add_events(Gdk.EventMask.SCROLL_MASK); + add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); + add_events(Gdk.EventMask.POINTER_MOTION_MASK); + + enter_notify_event.connect((self, event) => { + if (event.window == self.get_window() && + event.detail != Gdk.NotifyType.INFERIOR) { + this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); + hover(HoverEvent(event) { lost = false }); + } + }); + + leave_notify_event.connect((self, event) => { + if (event.window == self.get_window() && + event.detail != Gdk.NotifyType.INFERIOR) { + this.unset_state_flags(Gtk.StateFlags.PRELIGHT); + hover_lost(HoverEvent(event) { lost = true }); + } + }); + + button_press_event.connect((event) => { + click(ClickEvent(event) { release = false }); + }); + + button_release_event.connect((event) => { + click_release(ClickEvent(event) { release = true }); + }); + + scroll_event.connect((event) => { + scroll(ScrollEvent(event)); + }); + + motion_notify_event.connect((event) => { + motion(MotionEvent(event)); + }); + } +} + +public struct Astal.MotionEvent { + uint time; + double x; + double y; + Gdk.ModifierType modifier; + + public MotionEvent(Gdk.EventMotion event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.modifier = event.state; + } +} diff --git a/lib/astal/gtk3/src/widget/icon.vala b/lib/astal/gtk3/src/widget/icon.vala new file mode 100644 index 0000000..f2d59a2 --- /dev/null +++ b/lib/astal/gtk3/src/widget/icon.vala @@ -0,0 +1,105 @@ +public class Astal.Icon : Gtk.Image { + private IconType type = IconType.NAMED; + private double size { get; set; default = 14; } + + public new Gdk.Pixbuf pixbuf { get; set; } + public string icon { get; set; default = ""; } + public GLib.Icon g_icon {get; set;} + + public static Gtk.IconInfo? lookup_icon(string icon) { + var theme = Gtk.IconTheme.get_default(); + return theme.lookup_icon(icon, 16, Gtk.IconLookupFlags.USE_BUILTIN); + } + + private async void display_icon() { + switch(type) { + case IconType.NAMED: + icon_name = icon; + pixel_size = (int)size; + break; + case IconType.FILE: + try { + var file = File.new_for_path(icon); + var stream = yield file.read_async(); + var pb = yield new Gdk.Pixbuf.from_stream_at_scale_async( + stream, + (int)size * scale_factor, + (int)size * scale_factor, + true, + null + ); + var cs = Gdk.cairo_surface_create_from_pixbuf(pb, 0, this.get_window()); + set_from_surface(cs); + } catch (Error err) { + printerr(err.message); + } + break; + case IconType.PIXBUF: + var pb_scaled = pixbuf.scale_simple( + (int)size * scale_factor, + (int)size * scale_factor, + Gdk.InterpType.BILINEAR + ); + if (pb_scaled != null) { + var cs = Gdk.cairo_surface_create_from_pixbuf(pb_scaled, 0, this.get_window()); + set_from_surface(cs); + } + break; + case IconType.GICON: + pixel_size = (int)size; + gicon = g_icon; + break; + + } + } + + static construct { + set_css_name("icon"); + } + + construct { + notify["icon"].connect(() => { + if(FileUtils.test(icon, GLib.FileTest.EXISTS)) + type = IconType.FILE; + else if (lookup_icon(icon) != null) + type = IconType.NAMED; + else { + type = IconType.NAMED; + warning("cannot assign %s as icon, "+ + "it is not a file nor a named icon", icon); + } + display_icon.begin(); + }); + + notify["pixbuf"].connect(() => { + type = IconType.PIXBUF; + display_icon.begin(); + }); + + notify["g-icon"].connect(() => { + type = IconType.GICON; + display_icon.begin(); + }); + + size_allocate.connect(() => { + size = get_style_context() + .get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); + + display_icon.begin(); + }); + + get_style_context().changed.connect(() => { + size = get_style_context() + .get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); + + display_icon.begin(); + }); + } +} + +private enum Astal.IconType { + NAMED, + FILE, + PIXBUF, + GICON, +} diff --git a/lib/astal/gtk3/src/widget/label.vala b/lib/astal/gtk3/src/widget/label.vala new file mode 100644 index 0000000..4063b6f --- /dev/null +++ b/lib/astal/gtk3/src/widget/label.vala @@ -0,0 +1,18 @@ +using Pango; + +public class Astal.Label : Gtk.Label { + public bool truncate { + set { ellipsize = value ? EllipsizeMode.END : EllipsizeMode.NONE; } + get { return ellipsize == EllipsizeMode.END; } + } + + public new bool justify_fill { + set { justify = value ? Gtk.Justification.FILL : Gtk.Justification.LEFT; } + get { return justify == Gtk.Justification.FILL; } + } + + construct { + notify["ellipsize"].connect(() => notify_property("truncate")); + notify["justify"].connect(() => notify_property("justify_fill")); + } +} diff --git a/lib/astal/gtk3/src/widget/levelbar.vala b/lib/astal/gtk3/src/widget/levelbar.vala new file mode 100644 index 0000000..9b61957 --- /dev/null +++ b/lib/astal/gtk3/src/widget/levelbar.vala @@ -0,0 +1,13 @@ +public class Astal.LevelBar : Gtk.LevelBar { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + construct { + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + } +} diff --git a/lib/astal/gtk3/src/widget/overlay.vala b/lib/astal/gtk3/src/widget/overlay.vala new file mode 100644 index 0000000..603ee66 --- /dev/null +++ b/lib/astal/gtk3/src/widget/overlay.vala @@ -0,0 +1,57 @@ +public class Astal.Overlay : Gtk.Overlay { + public bool pass_through { get; set; } + + public Gtk.Widget? overlay { + get { return overlays.nth_data(0); } + set { + foreach (var ch in get_children()) { + if (ch != child) + remove(ch); + } + + if (value != null) + add_overlay(value); + } + } + + public List overlays { + owned get { return get_children(); } + set { + foreach (var ch in get_children()) { + if (ch != child) + remove(ch); + } + + foreach (var ch in value) + add_overlay(ch); + } + } + + public new Gtk.Widget? child { + get { return get_child(); } + set { + var ch = get_child(); + if (ch != null) + remove(ch); + + if (value != null) + add(value); + } + } + + construct { + notify["pass-through"].connect(() => { + update_pass_through(); + }); + } + + private void update_pass_through() { + foreach (var child in get_children()) + set_overlay_pass_through(child, pass_through); + } + + public new void add_overlay(Gtk.Widget widget) { + base.add_overlay(widget); + set_overlay_pass_through(widget, pass_through); + } +} diff --git a/lib/astal/gtk3/src/widget/scrollable.vala b/lib/astal/gtk3/src/widget/scrollable.vala new file mode 100644 index 0000000..57afb6e --- /dev/null +++ b/lib/astal/gtk3/src/widget/scrollable.vala @@ -0,0 +1,40 @@ +public class Astal.Scrollable : Gtk.ScrolledWindow { + private Gtk.PolicyType _hscroll = Gtk.PolicyType.AUTOMATIC; + private Gtk.PolicyType _vscroll = Gtk.PolicyType.AUTOMATIC; + + public Gtk.PolicyType hscroll { + get { return _hscroll; } + set { + _hscroll = value; + set_policy(value, vscroll); + } + } + + public Gtk.PolicyType vscroll { + get { return _vscroll; } + set { + _vscroll = value; + set_policy(hscroll, value); + } + } + + static construct { + set_css_name("scrollable"); + } + + construct { + if (hadjustment != null) + hadjustment = new Gtk.Adjustment(0,0,0,0,0,0); + + if (vadjustment != null) + vadjustment = new Gtk.Adjustment(0,0,0,0,0,0); + } + + public new Gtk.Widget get_child() { + var ch = base.get_child(); + if (ch is Gtk.Viewport) { + return ch.get_child(); + } + return ch; + } +} diff --git a/lib/astal/gtk3/src/widget/slider.vala b/lib/astal/gtk3/src/widget/slider.vala new file mode 100644 index 0000000..466275b --- /dev/null +++ b/lib/astal/gtk3/src/widget/slider.vala @@ -0,0 +1,71 @@ +public class Astal.Slider : Gtk.Scale { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + // emitted when the user drags the slider + public signal void dragged (); + + construct { + draw_value = false; + + if (adjustment == null) + adjustment = new Gtk.Adjustment(0,0,0,0,0,0); + + if (max == 0 && min == 0) { + max = 1; + } + + if (step == 0) { + step = 0.05; + } + + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + + button_press_event.connect(() => { dragging = true; }); + key_press_event.connect(() => { dragging = true; }); + button_release_event.connect(() => { dragging = false; }); + key_release_event.connect(() => { dragging = false; }); + scroll_event.connect((event) => { + dragging = true; + if (event.delta_y > 0) + value -= step; + else + value += step; + dragging = false; + }); + + value_changed.connect(() => { + if (dragging) + dragged(); + }); + } + + public bool dragging { get; private set; } + + public double value { + get { return adjustment.value; } + set { if (!dragging) adjustment.value = value; } + } + + public double min { + get { return adjustment.lower; } + set { adjustment.lower = value; } + } + + public double max { + get { return adjustment.upper; } + set { adjustment.upper = value; } + } + + public double step { + get { return adjustment.step_increment; } + set { adjustment.step_increment = value; } + } + + // TODO: marks +} diff --git a/lib/astal/gtk3/src/widget/stack.vala b/lib/astal/gtk3/src/widget/stack.vala new file mode 100644 index 0000000..02f9959 --- /dev/null +++ b/lib/astal/gtk3/src/widget/stack.vala @@ -0,0 +1,26 @@ +public class Astal.Stack : Gtk.Stack { + public string shown { + get { return visible_child_name; } + set { visible_child_name = value; } + } + + public List children { + set { _set_children(value); } + owned get { return get_children(); } + } + + private void _set_children(List arr) { + foreach(var child in get_children()) { + remove(child); + } + + var i = 0; + foreach(var child in arr) { + if (child.name != null) { + add_named(child, child.name); + } else { + add_named(child, (++i).to_string()); + } + } + } +} diff --git a/lib/astal/gtk3/src/widget/widget.vala b/lib/astal/gtk3/src/widget/widget.vala new file mode 100644 index 0000000..2506bc8 --- /dev/null +++ b/lib/astal/gtk3/src/widget/widget.vala @@ -0,0 +1,157 @@ +namespace Astal { +private class Css { + private static HashTable _providers; + public static HashTable providers { + get { + if (_providers == null) { + _providers = new HashTable( + (w) => (uint)w, + (a, b) => a == b); + } + + return _providers; + } + } +} + +private void remove_provider(Gtk.Widget widget) { + var providers = Css.providers; + + if (providers.contains(widget)) { + var p = providers.get(widget); + widget.get_style_context().remove_provider(p); + providers.remove(widget); + p.dispose(); + } +} + +public void widget_set_css(Gtk.Widget widget, string css) { + var providers = Css.providers; + + if (providers.contains(widget)) { + remove_provider(widget); + } else { + widget.destroy.connect(() => { + remove_provider(widget); + }); + } + + var style = !css.contains("{") || !css.contains("}") + ? "* { ".concat(css, "}") : css; + + var p = new Gtk.CssProvider(); + widget.get_style_context() + .add_provider(p, Gtk.STYLE_PROVIDER_PRIORITY_USER); + + try { + p.load_from_data(style, style.length); + providers.set(widget, p); + } catch (Error err) { + warning(err.message); + } +} + +public string widget_get_css(Gtk.Widget widget) { + var providers = Css.providers; + + if (providers.contains(widget)) + return providers.get(widget).to_string(); + + return ""; +} + +public void widget_set_class_names(Gtk.Widget widget, string[] class_names) { + foreach (var name in widget_get_class_names(widget)) + widget_toggle_class_name(widget, name, false); + + foreach (var name in class_names) + widget_toggle_class_name(widget, name, true); +} + +public List widget_get_class_names(Gtk.Widget widget) { + return widget.get_style_context().list_classes(); +} + +public void widget_toggle_class_name( + Gtk.Widget widget, + string class_name, + bool condition = true +) { + var c = widget.get_style_context(); + if (condition) + c.add_class(class_name); + else + c.remove_class(class_name); +} + +private class Cursor { + private static HashTable _cursors; + public static HashTable cursors { + get { + if (_cursors == null) { + _cursors = new HashTable( + (w) => (uint)w, + (a, b) => a == b); + } + return _cursors; + } + } +} + +private void widget_setup_cursor(Gtk.Widget widget) { + widget.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK); + widget.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); + widget.enter_notify_event.connect(() => { + widget.get_window().set_cursor( + new Gdk.Cursor.from_name( + Gdk.Display.get_default(), + Cursor.cursors.get(widget))); + return false; + }); + widget.leave_notify_event.connect(() => { + widget.get_window().set_cursor( + new Gdk.Cursor.from_name( + Gdk.Display.get_default(), + "default")); + return false; + }); + widget.destroy.connect(() => { + if (Cursor.cursors.contains(widget)) + Cursor.cursors.remove(widget); + }); +} + +public void widget_set_cursor(Gtk.Widget widget, string cursor) { + if (!Cursor.cursors.contains(widget)) + widget_setup_cursor(widget); + + Cursor.cursors.set(widget, cursor); +} + +public string widget_get_cursor(Gtk.Widget widget) { + return Cursor.cursors.get(widget); +} + +private class ClickThrough { + private static HashTable _click_through; + public static HashTable click_through { + get { + if (_click_through == null) { + _click_through = new HashTable( + (w) => (uint)w, + (a, b) => a == b); + } + return _click_through; + } + } +} + +public void widget_set_click_through(Gtk.Widget widget, bool click_through) { + ClickThrough.click_through.set(widget, click_through); + widget.input_shape_combine_region(click_through ? new Cairo.Region() : null); +} + +public bool widget_get_click_through(Gtk.Widget widget) { + return ClickThrough.click_through.get(widget); +} +} diff --git a/lib/astal/gtk3/src/widget/window.vala b/lib/astal/gtk3/src/widget/window.vala new file mode 100644 index 0000000..946e766 --- /dev/null +++ b/lib/astal/gtk3/src/widget/window.vala @@ -0,0 +1,246 @@ +using GtkLayerShell; + +public enum Astal.WindowAnchor { + NONE = 0, + TOP = 1, + RIGHT = 2, + LEFT = 4, + BOTTOM = 8, +} + +public enum Astal.Exclusivity { + NORMAL, + EXCLUSIVE, + IGNORE, +} + +public enum Astal.Layer { + BACKGROUND = 0, // GtkLayerShell.Layer.BACKGROUND + BOTTOM = 1, // GtkLayerShell.Layer.BOTTOM + TOP = 2, // GtkLayerShell.Layer.TOP + OVERLAY = 3, // GtkLayerShell.Layer.OVERLAY +} + +public enum Astal.Keymode { + NONE = 0, // GtkLayerShell.KeyboardMode.NONE + ON_DEMAND = 1, // GtkLayerShell.KeyboardMode.ON_DEMAND + EXCLUSIVE = 2, // GtkLayerShell.KeyboardMode.EXCLUSIVE +} + +public class Astal.Window : Gtk.Window { + private static bool check(string action) { + if (!is_supported()) { + critical(@"can not $action on window: layer shell not supported"); + print("tip: running from an xwayland terminal can cause this, for example VsCode"); + return true; + } + return false; + } + + private InhibitManager? inhibit_manager; + private Inhibitor? inhibitor; + + construct { + if (check("initialize layer shell")) + return; + + height_request = 1; + width_request = 1; + init_for_window(this); + inhibit_manager = InhibitManager.get_default(); + } + + public bool inhibit { + set { + if (inhibit_manager == null) { + return; + } + if (value && inhibitor == null) { + inhibitor = inhibit_manager.inhibit(this); + } + else if (!value && inhibitor != null) { + inhibitor = null; + } + } + get { + return inhibitor != null; + } + } + + public override void show() { + base.show(); + if(inhibit) { + inhibitor = inhibit_manager.inhibit(this); + } + } + + public string namespace { + get { return get_namespace(this); } + set { set_namespace(this, value); } + } + + public int anchor { + set { + if (check("set anchor")) + return; + + set_anchor(this, Edge.TOP, WindowAnchor.TOP in value); + set_anchor(this, Edge.BOTTOM, WindowAnchor.BOTTOM in value); + set_anchor(this, Edge.LEFT, WindowAnchor.LEFT in value); + set_anchor(this, Edge.RIGHT, WindowAnchor.RIGHT in value); + } + get { + var a = WindowAnchor.NONE; + if (get_anchor(this, Edge.TOP)) + a = a | WindowAnchor.TOP; + + if (get_anchor(this, Edge.RIGHT)) + a = a | WindowAnchor.RIGHT; + + if (get_anchor(this, Edge.LEFT)) + a = a | WindowAnchor.LEFT; + + if (get_anchor(this, Edge.BOTTOM)) + a = a | WindowAnchor.BOTTOM; + + return a; + } + } + + public Exclusivity exclusivity { + set { + if (check("set exclusivity")) + return; + + switch (value) { + case Exclusivity.NORMAL: + set_exclusive_zone(this, 0); + break; + case Exclusivity.EXCLUSIVE: + auto_exclusive_zone_enable(this); + break; + case Exclusivity.IGNORE: + set_exclusive_zone(this, -1); + break; + } + } + get { + if (auto_exclusive_zone_is_enabled(this)) + return Exclusivity.EXCLUSIVE; + + if (get_exclusive_zone(this) == -1) + return Exclusivity.IGNORE; + + return Exclusivity.NORMAL; + } + } + + public Layer layer { + get { return (Layer)get_layer(this); } + set { + if (check("set layer")) + return; + + set_layer(this, (GtkLayerShell.Layer)value); + } + } + + public Keymode keymode { + get { return (Keymode)get_keyboard_mode(this); } + set { + if (check("set keymode")) + return; + + set_keyboard_mode(this, (GtkLayerShell.KeyboardMode)value); + } + } + + public Gdk.Monitor gdkmonitor { + get { return get_monitor(this); } + set { + if (check("set gdkmonitor")) + return; + + set_monitor (this, value); + } + } + + public new int margin_top { + get { return GtkLayerShell.get_margin(this, Edge.TOP); } + set { + if (check("set margin_top")) + return; + + GtkLayerShell.set_margin(this, Edge.TOP, value); + } + } + + public new int margin_bottom { + get { return GtkLayerShell.get_margin(this, Edge.BOTTOM); } + set { + if (check("set margin_bottom")) + return; + + GtkLayerShell.set_margin(this, Edge.BOTTOM, value); + } + } + + public new int margin_left { + get { return GtkLayerShell.get_margin(this, Edge.LEFT); } + set { + if (check("set margin_left")) + return; + + GtkLayerShell.set_margin(this, Edge.LEFT, value); + } + } + + public new int margin_right { + get { return GtkLayerShell.get_margin(this, Edge.RIGHT); } + set { + if (check("set margin_right")) + return; + + GtkLayerShell.set_margin(this, Edge.RIGHT, value); + } + } + + public new int margin { + set { + if (check("set margin")) + return; + + margin_top = value; + margin_right = value; + margin_bottom = value; + margin_left = value; + } + } + + /** + * CAUTION: the id might not be the same mapped by the compositor + * to reset and let the compositor map it pass a negative number + */ + public int monitor { + set { + if (check("set monitor")) + return; + + if (value < 0) + set_monitor(this, (Gdk.Monitor)null); + + var m = Gdk.Display.get_default().get_monitor(value); + set_monitor(this, m); + } + get { + var m = get_monitor(this); + var d = Gdk.Display.get_default(); + for (var i = 0; i < d.get_n_monitors(); ++i) { + if (m == d.get_monitor(i)) + return i; + } + + return -1; + } + } +} diff --git a/lib/astal/gtk3/version b/lib/astal/gtk3/version new file mode 100644 index 0000000..4a36342 --- /dev/null +++ b/lib/astal/gtk3/version @@ -0,0 +1 @@ +3.0.0 -- cgit v1.2.3 From 2f71cd4c08bb4514efe43533e6a5d03535204c29 Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 01:26:32 +0200 Subject: refactor lua and gjs lib --- core/gjs/.gitignore | 4 - core/gjs/eslint.config.mjs | 18 - core/gjs/index.ts | 13 - core/gjs/package-lock.json | 3209 --------------------------- core/gjs/package.json | 48 - core/gjs/src/application.ts | 103 - core/gjs/src/astalify.ts | 322 --- core/gjs/src/binding.ts | 89 - core/gjs/src/file.ts | 44 - core/gjs/src/gobject.ts | 178 -- core/gjs/src/imports.ts | 10 - core/gjs/src/jsx/jsx-runtime.ts | 88 - core/gjs/src/process.ts | 68 - core/gjs/src/time.ts | 13 - core/gjs/src/variable.ts | 230 -- core/gjs/src/widgets.ts | 154 -- core/gjs/tsconfig.json | 25 - core/lua/astal-dev-1.rockspec | 31 - core/lua/astal/application.lua | 94 - core/lua/astal/binding.lua | 71 - core/lua/astal/file.lua | 45 - core/lua/astal/init.lua | 41 - core/lua/astal/process.lua | 78 - core/lua/astal/time.lua | 27 - core/lua/astal/variable.lua | 276 --- core/lua/astal/widget.lua | 322 --- core/lua/stylua.toml | 3 - core/meson.build | 27 - core/meson_options.txt | 17 - core/src/astal.vala | 389 ---- core/src/cli.vala | 87 - core/src/config.vala.in | 6 - core/src/file.vala | 81 - core/src/idle-inhibit.c | 114 - core/src/idle-inhibit.h | 22 - core/src/meson.build | 136 -- core/src/process.vala | 119 - core/src/time.vala | 73 - core/src/vapi/AstalInhibitManager.vapi | 13 - core/src/variable.vala | 196 -- core/src/widget/box.vala | 62 - core/src/widget/button.vala | 101 - core/src/widget/centerbox.vala | 54 - core/src/widget/circularprogress.vala | 182 -- core/src/widget/eventbox.vala | 66 - core/src/widget/icon.vala | 107 - core/src/widget/label.vala | 18 - core/src/widget/levelbar.vala | 15 - core/src/widget/overlay.vala | 59 - core/src/widget/scrollable.vala | 42 - core/src/widget/slider.vala | 73 - core/src/widget/stack.vala | 26 - core/src/widget/widget.vala | 157 -- core/src/widget/window.vala | 255 --- core/version | 1 - flake.nix | 5 +- lang/gjs/.gitignore | 4 + lang/gjs/eslint.config.mjs | 19 + lang/gjs/gtk3/app.ts | 105 + lang/gjs/gtk3/astalify.ts | 325 +++ lang/gjs/gtk3/index.ts | 9 + lang/gjs/gtk3/jsx-runtime.ts | 96 + lang/gjs/gtk3/widget.ts | 154 ++ lang/gjs/gtk4/app.ts | 1 + lang/gjs/gtk4/astalify.ts | 1 + lang/gjs/gtk4/index.ts | 1 + lang/gjs/gtk4/jsx-runtime.ts | 1 + lang/gjs/index.ts | 6 + lang/gjs/lib/binding.ts | 89 + lang/gjs/lib/file.ts | 45 + lang/gjs/lib/gobject.ts | 180 ++ lang/gjs/lib/process.ts | 68 + lang/gjs/lib/time.ts | 13 + lang/gjs/lib/variable.ts | 230 ++ lang/gjs/package-lock.json | 3823 ++++++++++++++++++++++++++++++++ lang/gjs/package.json | 47 + lang/gjs/tsconfig.json | 18 + lang/lua/astal-dev-1.rockspec | 31 + lang/lua/gtk3/app.lua | 96 + lang/lua/gtk3/astalify.lua | 236 ++ lang/lua/gtk3/widget.lua | 90 + lang/lua/init.lua | 27 + lang/lua/lib/binding.lua | 71 + lang/lua/lib/file.lua | 45 + lang/lua/lib/process.lua | 78 + lang/lua/lib/time.lua | 27 + lang/lua/lib/variable.lua | 276 +++ lang/lua/stylua.toml | 3 + lib/astal/gtk3/src/application.vala | 68 +- lib/astal/io/application.vala | 12 +- nix/devshell.nix | 6 +- nix/lua.nix | 7 +- 92 files changed, 6262 insertions(+), 8153 deletions(-) delete mode 100644 core/gjs/.gitignore delete mode 100644 core/gjs/eslint.config.mjs delete mode 100644 core/gjs/index.ts delete mode 100644 core/gjs/package-lock.json delete mode 100644 core/gjs/package.json delete mode 100644 core/gjs/src/application.ts delete mode 100644 core/gjs/src/astalify.ts delete mode 100644 core/gjs/src/binding.ts delete mode 100644 core/gjs/src/file.ts delete mode 100644 core/gjs/src/gobject.ts delete mode 100644 core/gjs/src/imports.ts delete mode 100644 core/gjs/src/jsx/jsx-runtime.ts delete mode 100644 core/gjs/src/process.ts delete mode 100644 core/gjs/src/time.ts delete mode 100644 core/gjs/src/variable.ts delete mode 100644 core/gjs/src/widgets.ts delete mode 100644 core/gjs/tsconfig.json delete mode 100644 core/lua/astal-dev-1.rockspec delete mode 100644 core/lua/astal/application.lua delete mode 100644 core/lua/astal/binding.lua delete mode 100644 core/lua/astal/file.lua delete mode 100644 core/lua/astal/init.lua delete mode 100644 core/lua/astal/process.lua delete mode 100644 core/lua/astal/time.lua delete mode 100644 core/lua/astal/variable.lua delete mode 100644 core/lua/astal/widget.lua delete mode 100644 core/lua/stylua.toml delete mode 100644 core/meson.build delete mode 100644 core/meson_options.txt delete mode 100644 core/src/astal.vala delete mode 100644 core/src/cli.vala delete mode 100644 core/src/config.vala.in delete mode 100644 core/src/file.vala delete mode 100644 core/src/idle-inhibit.c delete mode 100644 core/src/idle-inhibit.h delete mode 100644 core/src/meson.build delete mode 100644 core/src/process.vala delete mode 100644 core/src/time.vala delete mode 100644 core/src/vapi/AstalInhibitManager.vapi delete mode 100644 core/src/variable.vala delete mode 100644 core/src/widget/box.vala delete mode 100644 core/src/widget/button.vala delete mode 100644 core/src/widget/centerbox.vala delete mode 100644 core/src/widget/circularprogress.vala delete mode 100644 core/src/widget/eventbox.vala delete mode 100644 core/src/widget/icon.vala delete mode 100644 core/src/widget/label.vala delete mode 100644 core/src/widget/levelbar.vala delete mode 100644 core/src/widget/overlay.vala delete mode 100644 core/src/widget/scrollable.vala delete mode 100644 core/src/widget/slider.vala delete mode 100644 core/src/widget/stack.vala delete mode 100644 core/src/widget/widget.vala delete mode 100644 core/src/widget/window.vala delete mode 100644 core/version create mode 100644 lang/gjs/.gitignore create mode 100644 lang/gjs/eslint.config.mjs create mode 100644 lang/gjs/gtk3/app.ts create mode 100644 lang/gjs/gtk3/astalify.ts create mode 100644 lang/gjs/gtk3/index.ts create mode 100644 lang/gjs/gtk3/jsx-runtime.ts create mode 100644 lang/gjs/gtk3/widget.ts create mode 100644 lang/gjs/gtk4/app.ts create mode 100644 lang/gjs/gtk4/astalify.ts create mode 100644 lang/gjs/gtk4/index.ts create mode 100644 lang/gjs/gtk4/jsx-runtime.ts create mode 100644 lang/gjs/index.ts create mode 100644 lang/gjs/lib/binding.ts create mode 100644 lang/gjs/lib/file.ts create mode 100644 lang/gjs/lib/gobject.ts create mode 100644 lang/gjs/lib/process.ts create mode 100644 lang/gjs/lib/time.ts create mode 100644 lang/gjs/lib/variable.ts create mode 100644 lang/gjs/package-lock.json create mode 100644 lang/gjs/package.json create mode 100644 lang/gjs/tsconfig.json create mode 100644 lang/lua/astal-dev-1.rockspec create mode 100644 lang/lua/gtk3/app.lua create mode 100644 lang/lua/gtk3/astalify.lua create mode 100644 lang/lua/gtk3/widget.lua create mode 100644 lang/lua/init.lua create mode 100644 lang/lua/lib/binding.lua create mode 100644 lang/lua/lib/file.lua create mode 100644 lang/lua/lib/process.lua create mode 100644 lang/lua/lib/time.lua create mode 100644 lang/lua/lib/variable.lua create mode 100644 lang/lua/stylua.toml diff --git a/core/gjs/.gitignore b/core/gjs/.gitignore deleted file mode 100644 index 53f4bc3..0000000 --- a/core/gjs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -result/ -dist/ -@girs/ diff --git a/core/gjs/eslint.config.mjs b/core/gjs/eslint.config.mjs deleted file mode 100644 index 99dad7d..0000000 --- a/core/gjs/eslint.config.mjs +++ /dev/null @@ -1,18 +0,0 @@ -import eslint from "@eslint/js" -import tseslint from "typescript-eslint" -import stylistic from "@stylistic/eslint-plugin" - -export default tseslint.config({ - extends: [ - eslint.configs.recommended, - ...tseslint.configs.recommended, - stylistic.configs.customize({ - semi: false, - indent: 4, - quotes: "double", - }), - ], - rules: { - "@typescript-eslint/no-explicit-any": "off", - }, -}) diff --git a/core/gjs/index.ts b/core/gjs/index.ts deleted file mode 100644 index 901b264..0000000 --- a/core/gjs/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Gtk } from "./src/imports.js" - -export * from "./src/imports.js" -export * from "./src/process.js" -export * from "./src/time.js" -export * from "./src/file.js" -export { bind, default as Binding } from "./src/binding.js" -export { Variable } from "./src/variable.js" -export * as Widget from "./src/widgets.js" -export { default as App } from "./src/application.js" - -// gjs crashes if a widget is constructed before Gtk.init -Gtk.init(null) diff --git a/core/gjs/package-lock.json b/core/gjs/package-lock.json deleted file mode 100644 index aa679c8..0000000 --- a/core/gjs/package-lock.json +++ /dev/null @@ -1,3209 +0,0 @@ -{ - "name": "astal", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "astal", - "version": "0.1.0", - "license": "GPL", - "os": [ - "linux" - ], - "devDependencies": { - "@eslint/js": "^9.7.0", - "@stylistic/eslint-plugin": "latest", - "@ts-for-gir/cli": "latest", - "@types/eslint__js": "^8.42.3", - "eslint": "^8.57.0", - "typescript": "^5.5.3", - "typescript-eslint": "^7.16.1" - }, - "engines": { - "gjs": ">=1.79.0" - }, - "funding": { - "type": "kofi", - "url": "https://ko-fi.com/aylur" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", - "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@gi.ts/parser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@gi.ts/parser/-/parser-2.0.0.tgz", - "integrity": "sha512-Tz5T+3Ep+qY7rfBnYMGdVraCCUf1CKkDfxNd2fggfHLzjI7u5Th8a/piPgj0001jDs5czI+Ec3peh+6gkKPmHw==", - "dev": true, - "dependencies": { - "fast-xml-parser": "^4.3.5" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "node_modules/@inquirer/figures": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.4.tgz", - "integrity": "sha512-R7Gsg6elpuqdn55fBH2y9oYzrU/yKrSmIsDX4ROT51vohrECFzTf2zw9BfUbOW8xjfmM2QbVoVYdTwhrtEKWSQ==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@stylistic/eslint-plugin": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.3.0.tgz", - "integrity": "sha512-rtiz6u5gRyyEZp36FcF1/gHJbsbT3qAgXZ1qkad6Nr/xJ9wrSJkiSFFQhpYVTIZ7FJNRJurEcumZDCwN9dEI4g==", - "dev": true, - "dependencies": { - "@stylistic/eslint-plugin-js": "2.3.0", - "@stylistic/eslint-plugin-jsx": "2.3.0", - "@stylistic/eslint-plugin-plus": "2.3.0", - "@stylistic/eslint-plugin-ts": "2.3.0", - "@types/eslint": "^8.56.10" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@stylistic/eslint-plugin-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.3.0.tgz", - "integrity": "sha512-lQwoiYb0Fs6Yc5QS3uT8+T9CPKK2Eoxc3H8EnYJgM26v/DgtW+1lvy2WNgyBflU+ThShZaHm3a6CdD9QeKx23w==", - "dev": true, - "dependencies": { - "@types/eslint": "^8.56.10", - "acorn": "^8.11.3", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@stylistic/eslint-plugin-jsx": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.3.0.tgz", - "integrity": "sha512-tsQ0IEKB195H6X9A4iUSgLLLKBc8gUBWkBIU8tp1/3g2l8stu+PtMQVV/VmK1+3bem5FJCyvfcZIQ/WF1fsizA==", - "dev": true, - "dependencies": { - "@stylistic/eslint-plugin-js": "^2.3.0", - "@types/eslint": "^8.56.10", - "estraverse": "^5.3.0", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@stylistic/eslint-plugin-plus": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.3.0.tgz", - "integrity": "sha512-xboPWGUU5yaPlR+WR57GwXEuY4PSlPqA0C3IdNA/+1o2MuBi95XgDJcZiJ9N+aXsqBXAPIpFFb+WQ7QEHo4f7g==", - "dev": true, - "dependencies": { - "@types/eslint": "^8.56.10", - "@typescript-eslint/utils": "^7.12.0" - }, - "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/@stylistic/eslint-plugin-ts": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.3.0.tgz", - "integrity": "sha512-wqOR38/uz/0XPnHX68ftp8sNMSAqnYGjovOTN7w00xnjS6Lxr3Sk7q6AaxWWqbMvOj7V2fQiMC5HWAbTruJsCg==", - "dev": true, - "dependencies": { - "@stylistic/eslint-plugin-js": "2.3.0", - "@types/eslint": "^8.56.10", - "@typescript-eslint/utils": "^7.12.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "eslint": ">=8.40.0" - } - }, - "node_modules/@ts-for-gir/cli": { - "version": "4.0.0-beta.7", - "resolved": "https://registry.npmjs.org/@ts-for-gir/cli/-/cli-4.0.0-beta.7.tgz", - "integrity": "sha512-s345hGCB2su0NIFA7A10GIY9mLiNtrbSXp9ZscKsasx6Fm77QZybO09dLcIPMuePq8gbRlOpNp8MedAqO7zWRw==", - "dev": true, - "dependencies": { - "@gi.ts/parser": "^2.0.0", - "@ts-for-gir/generator-base": "^4.0.0-beta.7", - "@ts-for-gir/generator-html-doc": "^4.0.0-beta.7", - "@ts-for-gir/generator-typescript": "^4.0.0-beta.7", - "@ts-for-gir/lib": "^4.0.0-beta.7", - "colorette": "^2.0.20", - "cosmiconfig": "^9.0.0", - "glob": "^11.0.0", - "inquirer": "^9.3.5", - "prettier": "^3.3.3", - "yargs": "^17.7.2" - }, - "bin": { - "ts-for-gir": "lib/start.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ts-for-gir/generator-base": { - "version": "4.0.0-beta.7", - "resolved": "https://registry.npmjs.org/@ts-for-gir/generator-base/-/generator-base-4.0.0-beta.7.tgz", - "integrity": "sha512-Z3dlwea0LvbGwcb51xjSqp33n5wvqmsX7r1EwbA4vEPuN7AgPm2LDbL68G5HO6QOmFDt05Fe0gjumfzjGE8Xlw==", - "dev": true, - "dependencies": { - "@ts-for-gir/lib": "^4.0.0-beta.7" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ts-for-gir/generator-html-doc": { - "version": "4.0.0-beta.7", - "resolved": "https://registry.npmjs.org/@ts-for-gir/generator-html-doc/-/generator-html-doc-4.0.0-beta.7.tgz", - "integrity": "sha512-srz4nSSfcqCQUQcV4Ia5booXEy7gC54iv7Q1E80cdnM/cg0XWq4XaTfCbH27CqcY2zS+KsVUJm7PWI7LHeUeiw==", - "dev": true, - "dependencies": { - "@ts-for-gir/generator-base": "^4.0.0-beta.7", - "@ts-for-gir/lib": "^4.0.0-beta.7" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ts-for-gir/generator-typescript": { - "version": "4.0.0-beta.7", - "resolved": "https://registry.npmjs.org/@ts-for-gir/generator-typescript/-/generator-typescript-4.0.0-beta.7.tgz", - "integrity": "sha512-6WVPHVod6YINT5ZX1TSaPy8b1+A1t3TAi+1Byx/nH5aF4o1jPmn1KKorvxOIDcvlqnjux053r+rjweIR+1w9Zw==", - "dev": true, - "dependencies": { - "@ts-for-gir/generator-base": "^4.0.0-beta.7", - "@ts-for-gir/lib": "^4.0.0-beta.7", - "ejs": "^3.1.10", - "xml2js": "^0.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ts-for-gir/lib": { - "version": "4.0.0-beta.7", - "resolved": "https://registry.npmjs.org/@ts-for-gir/lib/-/lib-4.0.0-beta.7.tgz", - "integrity": "sha512-9hhCk3OBA0diIG5KuqSCJAy6D91hVUK2JiuLbxNfdmvAaMS+4BvYSGKg+DdXvmgoTip4LqCo4EsFTHfuo7QQHg==", - "dev": true, - "dependencies": { - "@gi.ts/parser": "^2.0.0", - "colorette": "^2.0.20", - "ejs": "^3.1.10", - "glob": "^11.0.0", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint__js": { - "version": "8.42.3", - "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", - "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", - "dev": true, - "dependencies": { - "@types/eslint": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz", - "integrity": "sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.16.1", - "@typescript-eslint/type-utils": "7.16.1", - "@typescript-eslint/utils": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.1.tgz", - "integrity": "sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "7.16.1", - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/typescript-estree": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz", - "integrity": "sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.1.tgz", - "integrity": "sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.16.1", - "@typescript-eslint/utils": "7.16.1", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz", - "integrity": "sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz", - "integrity": "sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/visitor-keys": "7.16.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz", - "integrity": "sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.16.1", - "@typescript-eslint/types": "7.16.1", - "@typescript-eslint/typescript-estree": "7.16.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz", - "integrity": "sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.1", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", - "dev": true, - "dependencies": { - "acorn": "^8.12.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-xml-parser": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", - "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/inquirer": { - "version": "9.3.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.3.6.tgz", - "integrity": "sha512-riK/iQB2ctwkpWYgjjWIRv3MBLt2gzb2Sj0JNQNbyTXgyXsLWcDPJ5WS5ZDTCx7BRFnJsARtYh+58fjP5M2Y0Q==", - "dev": true, - "dependencies": { - "@inquirer/figures": "^1.0.3", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", - "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jake/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", - "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", - "dev": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "dev": true - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "dev": true - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.16.1.tgz", - "integrity": "sha512-889oE5qELj65q/tGeOSvlreNKhimitFwZqQ0o7PcWC7/lgRkAMknznsCsV8J8mZGTP/Z+cIbX8accf2DE33hrA==", - "dev": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "7.16.1", - "@typescript-eslint/parser": "7.16.1", - "@typescript-eslint/utils": "7.16.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xml2js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", - "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/core/gjs/package.json b/core/gjs/package.json deleted file mode 100644 index e829409..0000000 --- a/core/gjs/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "astal", - "version": "0.1.0", - "description": "Building blocks for buildin linux desktop shell", - "type": "module", - "author": "Aylur", - "license": "GPL", - "repository": { - "type": "git", - "url": "https://github.com/astal-sh/libastal.git", - "directory": "gjs" - }, - "funding": { - "type": "kofi", - "url": "https://ko-fi.com/aylur" - }, - "exports": { - ".": "./index.ts", - "./application": "./src/application.ts", - "./binding": "./src/binding.ts", - "./file": "./src/file.ts", - "./gobject": "./src/gobject.ts", - "./process": "./src/process.ts", - "./time": "./src/time.ts", - "./variable": "./src/variable.ts", - "./widgets": "./src/widgets.ts" - }, - "engines": { - "gjs": ">=1.79.0" - }, - "os": [ - "linux" - ], - "publishConfig": {}, - "devDependencies": { - "@eslint/js": "^9.7.0", - "@stylistic/eslint-plugin": "latest", - "@ts-for-gir/cli": "latest", - "@types/eslint__js": "^8.42.3", - "eslint": "^8.57.0", - "typescript": "^5.5.3", - "typescript-eslint": "^7.16.1" - }, - "scripts": { - "lint": "eslint . --fix", - "types": "ts-for-gir generate -o @girs" - } -} diff --git a/core/gjs/src/application.ts b/core/gjs/src/application.ts deleted file mode 100644 index bf82cbe..0000000 --- a/core/gjs/src/application.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Astal, GObject, Gio, GLib } from "./imports.js" - -type RequestHandler = { - (request: string, res: (response: any) => void): void -} - -type Config = Partial<{ - icons: string - instanceName: string - gtkTheme: string - iconTheme: string - cursorTheme: string - css: string - requestHandler: RequestHandler - main(...args: string[]): void - client(message: (msg: string) => string, ...args: string[]): void - hold: boolean -}> - -// @ts-expect-error missing types -// https://github.com/gjsify/ts-for-gir/issues/164 -import { setConsoleLogDomain } from "console" -import { exit, programArgs } from "system" - -export default new (class AstalJS extends Astal.Application { - static { GObject.registerClass(this) } - - eval(body: string): Promise { - return new Promise((res, rej) => { - try { - const fn = Function(`return (async function() { - ${body.includes(";") ? body : `return ${body};`} - })`) - fn()() - .then(res) - .catch(rej) - } - catch (error) { - rej(error) - } - }) - } - - requestHandler?: RequestHandler - - vfunc_request(msg: string, conn: Gio.SocketConnection): void { - if (typeof this.requestHandler === "function") { - this.requestHandler(msg, (response) => { - Astal.write_sock(conn, String(response), (_, res) => - Astal.write_sock_finish(res), - ) - }) - } - else { - super.vfunc_request(msg, conn) - } - } - - apply_css(style: string, reset = false) { - super.apply_css(style, reset) - } - - quit(code?: number): void { - super.quit() - exit(code ?? 0) - } - - start({ requestHandler, css, hold, main, client, icons, ...cfg }: Config = {}) { - client ??= () => { - print(`Astal instance "${this.instanceName}" already running`) - exit(1) - } - - Object.assign(this, cfg) - setConsoleLogDomain(this.instanceName) - - this.requestHandler = requestHandler - this.connect("activate", () => { - const path: string[] = import.meta.url.split("/").slice(3) - const file = path.at(-1)!.replace(".js", ".css") - const css = `/${path.slice(0, -1).join("/")}/${file}` - if (file.endsWith(".css") && GLib.file_test(css, GLib.FileTest.EXISTS)) - this.apply_css(css, false) - - main?.(...programArgs) - }) - - if (!this.acquire_socket()) - return client(msg => Astal.Application.send_message(this.instanceName, msg)!, ...programArgs) - - if (css) - this.apply_css(css, false) - - if (icons) - this.add_icons(icons) - - hold ??= true - if (hold) - this.hold() - - this.runAsync([]) - } -}) diff --git a/core/gjs/src/astalify.ts b/core/gjs/src/astalify.ts deleted file mode 100644 index c4cbc5c..0000000 --- a/core/gjs/src/astalify.ts +++ /dev/null @@ -1,322 +0,0 @@ -import Binding, { kebabify, snakeify, type Connectable, type Subscribable } from "./binding.js" -import { Astal, Gtk, Gdk, GObject } from "./imports.js" -import { execAsync } from "./process.js" -import Variable from "./variable.js" - -function mergeBindings(array: any[]) { - function getValues(...args: any[]) { - let i = 0 - return array.map(value => value instanceof Binding - ? args[i++] - : value, - ) - } - - const bindings = array.filter(i => i instanceof Binding) - - if (bindings.length === 0) - return array - - if (bindings.length === 1) - return bindings[0].as(getValues) - - return Variable.derive(bindings, getValues)() -} - -function setProp(obj: any, prop: string, value: any) { - try { - // the setter method has to be used because - // array like properties are not bound correctly as props - const setter = `set_${snakeify(prop)}` - if (typeof obj[setter] === "function") - return obj[setter](value) - - return (obj[prop] = value) - } - catch (error) { - console.error(`could not set property "${prop}" on ${obj}:`, error) - } -} - -export default function astalify< - C extends { new(...args: any[]): Gtk.Widget }, ->(cls: C) { - class Widget extends cls { - get css(): string { return Astal.widget_get_css(this) } - set css(css: string) { Astal.widget_set_css(this, css) } - get_css(): string { return this.css } - set_css(css: string) { this.css = css } - - get className(): string { return Astal.widget_get_class_names(this).join(" ") } - set className(className: string) { Astal.widget_set_class_names(this, className.split(/\s+/)) } - get_class_name(): string { return this.className } - set_class_name(className: string) { this.className = className } - - get cursor(): Cursor { return Astal.widget_get_cursor(this) as Cursor } - set cursor(cursor: Cursor) { Astal.widget_set_cursor(this, cursor) } - get_cursor(): Cursor { return this.cursor } - set_cursor(cursor: Cursor) { this.cursor = cursor } - - get clickThrough(): boolean { return Astal.widget_get_click_through(this) } - set clickThrough(clickThrough: boolean) { Astal.widget_set_click_through(this, clickThrough) } - get_click_through(): boolean { return this.clickThrough } - set_click_through(clickThrough: boolean) { this.clickThrough = clickThrough } - - declare __no_implicit_destroy: boolean - get noImplicitDestroy(): boolean { return this.__no_implicit_destroy } - set noImplicitDestroy(value: boolean) { this.__no_implicit_destroy = value } - - _setChildren(children: Gtk.Widget[]) { - children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget - ? ch - : new Gtk.Label({ visible: true, label: String(ch) })) - - // remove - if (this instanceof Gtk.Bin) { - const ch = this.get_child() - if (ch) - this.remove(ch) - if (ch && !children.includes(ch) && !this.noImplicitDestroy) - ch?.destroy() - } - else if (this instanceof Gtk.Container) { - for (const ch of this.get_children()) { - this.remove(ch) - if (!children.includes(ch) && !this.noImplicitDestroy) - ch?.destroy() - } - } - - // TODO: add more container types - if (this instanceof Astal.Box) { - this.set_children(children) - } - - else if (this instanceof Astal.Stack) { - this.set_children(children) - } - - else if (this instanceof Astal.CenterBox) { - this.startWidget = children[0] - this.centerWidget = children[1] - this.endWidget = children[2] - } - - else if (this instanceof Astal.Overlay) { - const [child, ...overlays] = children - this.set_child(child) - this.set_overlays(overlays) - } - - else if (this instanceof Gtk.Container) { - for (const ch of children) - this.add(ch) - } - } - - toggleClassName(cn: string, cond = true) { - Astal.widget_toggle_class_name(this, cn, cond) - } - - hook( - object: Connectable, - signal: string, - callback: (self: this, ...args: any[]) => void, - ): this - hook( - object: Subscribable, - callback: (self: this, ...args: any[]) => void, - ): this - hook( - object: Connectable | Subscribable, - signalOrCallback: string | ((self: this, ...args: any[]) => void), - callback?: (self: this, ...args: any[]) => void, - ) { - if (typeof object.connect === "function" && callback) { - const id = object.connect(signalOrCallback, (_: any, ...args: unknown[]) => { - callback(this, ...args) - }) - this.connect("destroy", () => { - (object.disconnect as Connectable["disconnect"])(id) - }) - } - - else if (typeof object.subscribe === "function" && typeof signalOrCallback === "function") { - const unsub = object.subscribe((...args: unknown[]) => { - signalOrCallback(this, ...args) - }) - this.connect("destroy", unsub) - } - - return this - } - - constructor(...params: any[]) { - super() - const [config] = params - - const { setup, child, children = [], ...props } = config - props.visible ??= true - - if (child) - children.unshift(child) - - // collect bindings - const bindings = Object.keys(props).reduce((acc: any, prop) => { - if (props[prop] instanceof Binding) { - const binding = props[prop] - delete props[prop] - return [...acc, [prop, binding]] - } - return acc - }, []) - - // collect signal handlers - const onHandlers = Object.keys(props).reduce((acc: any, key) => { - if (key.startsWith("on")) { - const sig = kebabify(key).split("-").slice(1).join("-") - const handler = props[key] - delete props[key] - return [...acc, [sig, handler]] - } - return acc - }, []) - - // set children - const mergedChildren = mergeBindings(children.flat(Infinity)) - if (mergedChildren instanceof Binding) { - this._setChildren(mergedChildren.get()) - this.connect("destroy", mergedChildren.subscribe((v) => { - this._setChildren(v) - })) - } - else { - if (mergedChildren.length > 0) { - this._setChildren(mergedChildren) - } - } - - // setup signal handlers - for (const [signal, callback] of onHandlers) { - if (typeof callback === "function") { - this.connect(signal, callback) - } - else { - this.connect(signal, () => execAsync(callback) - .then(print).catch(console.error)) - } - } - - // setup bindings handlers - for (const [prop, binding] of bindings) { - if (prop === "child" || prop === "children") { - this.connect("destroy", binding.subscribe((v: any) => { - this._setChildren(v) - })) - } - this.connect("destroy", binding.subscribe((v: any) => { - setProp(this, prop, v) - })) - setProp(this, prop, binding.get()) - } - - Object.assign(this, props) - setup?.(this) - } - } - - GObject.registerClass({ - GTypeName: `Astal_${cls.name}`, - Properties: { - "class-name": GObject.ParamSpec.string( - "class-name", "", "", GObject.ParamFlags.READWRITE, "", - ), - "css": GObject.ParamSpec.string( - "css", "", "", GObject.ParamFlags.READWRITE, "", - ), - "cursor": GObject.ParamSpec.string( - "cursor", "", "", GObject.ParamFlags.READWRITE, "default", - ), - "click-through": GObject.ParamSpec.boolean( - "click-through", "", "", GObject.ParamFlags.READWRITE, false, - ), - "no-implicit-destroy": GObject.ParamSpec.boolean( - "no-implicit-destroy", "", "", GObject.ParamFlags.READWRITE, false, - ), - }, - }, Widget) - - return Widget -} - -type BindableProps = { - [K in keyof T]: Binding | T[K]; -} - -type SigHandler< - W extends InstanceType, - Args extends Array, -> = ((self: W, ...args: Args) => unknown) | string | string[] - -export type ConstructProps< - Self extends InstanceType, - Props extends Gtk.Widget.ConstructorProps, - Signals extends Record<`on${string}`, Array> = Record<`on${string}`, any[]>, -> = Partial<{ - // @ts-expect-error can't assign to unknown, but it works as expected though - [S in keyof Signals]: SigHandler -}> & Partial<{ - [Key in `on${string}`]: SigHandler -}> & BindableProps & { - className?: string - css?: string - cursor?: string - clickThrough?: boolean -}> & { - onDestroy?: (self: Self) => unknown - onDraw?: (self: Self) => unknown - onKeyPressEvent?: (self: Self, event: Gdk.Event) => unknown - onKeyReleaseEvent?: (self: Self, event: Gdk.Event) => unknown - onButtonPressEvent?: (self: Self, event: Gdk.Event) => unknown - onButtonReleaseEvent?: (self: Self, event: Gdk.Event) => unknown - onRealize?: (self: Self) => unknown - setup?: (self: Self) => void -} - -export type BindableChild = Gtk.Widget | Binding - -type Cursor = - | "default" - | "help" - | "pointer" - | "context-menu" - | "progress" - | "wait" - | "cell" - | "crosshair" - | "text" - | "vertical-text" - | "alias" - | "copy" - | "no-drop" - | "move" - | "not-allowed" - | "grab" - | "grabbing" - | "all-scroll" - | "col-resize" - | "row-resize" - | "n-resize" - | "e-resize" - | "s-resize" - | "w-resize" - | "ne-resize" - | "nw-resize" - | "sw-resize" - | "se-resize" - | "ew-resize" - | "ns-resize" - | "nesw-resize" - | "nwse-resize" - | "zoom-in" - | "zoom-out" diff --git a/core/gjs/src/binding.ts b/core/gjs/src/binding.ts deleted file mode 100644 index 95d905f..0000000 --- a/core/gjs/src/binding.ts +++ /dev/null @@ -1,89 +0,0 @@ -export const snakeify = (str: string) => str - .replace(/([a-z])([A-Z])/g, "$1_$2") - .replaceAll("-", "_") - .toLowerCase() - -export const kebabify = (str: string) => str - .replace(/([a-z])([A-Z])/g, "$1-$2") - .replaceAll("_", "-") - .toLowerCase() - -export interface Subscribable { - subscribe(callback: (value: T) => void): () => void - get(): T - [key: string]: any -} - -export interface Connectable { - connect(signal: string, callback: (...args: any[]) => unknown): number - disconnect(id: number): void - [key: string]: any -} - -export default class Binding { - private transformFn = (v: any) => v - - #emitter: Subscribable | Connectable - #prop?: string - - static bind< - T extends Connectable, - P extends keyof T, - >(object: T, property: P): Binding - - static bind(object: Subscribable): Binding - - static bind(emitter: Connectable | Subscribable, prop?: string) { - return new Binding(emitter, prop) - } - - private constructor(emitter: Connectable | Subscribable, prop?: string) { - this.#emitter = emitter - this.#prop = prop && kebabify(prop) - } - - toString() { - return `Binding<${this.#emitter}${this.#prop ? `, "${this.#prop}"` : ""}>` - } - - as(fn: (v: Value) => T): Binding { - const bind = new Binding(this.#emitter, this.#prop) - bind.transformFn = (v: Value) => fn(this.transformFn(v)) - return bind as unknown as Binding - } - - get(): Value { - if (typeof this.#emitter.get === "function") - return this.transformFn(this.#emitter.get()) - - if (typeof this.#prop === "string") { - const getter = `get_${snakeify(this.#prop)}` - if (typeof this.#emitter[getter] === "function") - return this.transformFn(this.#emitter[getter]()) - - return this.transformFn(this.#emitter[this.#prop]) - } - - throw Error("can not get value of binding") - } - - subscribe(callback: (value: Value) => void): () => void { - if (typeof this.#emitter.subscribe === "function") { - return this.#emitter.subscribe(() => { - callback(this.get()) - }) - } - else if (typeof this.#emitter.connect === "function") { - const signal = `notify::${this.#prop}` - const id = this.#emitter.connect(signal, () => { - callback(this.get()) - }) - return () => { - (this.#emitter.disconnect as Connectable["disconnect"])(id) - } - } - throw Error(`${this.#emitter} is not bindable`) - } -} - -export const { bind } = Binding diff --git a/core/gjs/src/file.ts b/core/gjs/src/file.ts deleted file mode 100644 index 90b33a1..0000000 --- a/core/gjs/src/file.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Astal, Gio } from "./imports.js" - -export function readFile(path: string): string { - return Astal.read_file(path) || "" -} - -export function readFileAsync(path: string): Promise { - return new Promise((resolve, reject) => { - Astal.read_file_async(path, (_, res) => { - try { - resolve(Astal.read_file_finish(res) || "") - } - catch (error) { - reject(error) - } - }) - }) -} - -export function writeFile(path: string, content: string): void { - Astal.write_file(path, content) -} - -export function writeFileAsync(path: string, content: string): Promise { - return new Promise((resolve, reject) => { - Astal.write_file_async(path, content, (_, res) => { - try { - resolve(Astal.write_file_finish(res)) - } - catch (error) { - reject(error) - } - }) - }) -} - -export function monitorFile( - path: string, - callback: (file: string, event: Gio.FileMonitorEvent) => void, -): Gio.FileMonitor { - return Astal.monitor_file(path, (file: string, event: Gio.FileMonitorEvent) => { - callback(file, event) - })! -} diff --git a/core/gjs/src/gobject.ts b/core/gjs/src/gobject.ts deleted file mode 100644 index 2658555..0000000 --- a/core/gjs/src/gobject.ts +++ /dev/null @@ -1,178 +0,0 @@ -import GObject from "gi://GObject" -export default GObject - -const meta = Symbol("meta") - -const { ParamSpec, ParamFlags } = GObject - -const kebabify = (str: string) => str - .replace(/([a-z])([A-Z])/g, "$1-$2") - .replaceAll("_", "-") - .toLowerCase() - -type SignalDeclaration = { - flags?: GObject.SignalFlags - accumulator?: GObject.AccumulatorType - return_type?: GObject.GType - param_types?: Array -} - -type PropertyDeclaration = - | InstanceType - | { $gtype: GObject.GType } - | typeof String - | typeof Number - | typeof Boolean - | typeof Object - -type GObjectConstructor = { - [meta]?: { - Properties?: { [key: string]: GObject.ParamSpec } - Signals?: { [key: string]: GObject.SignalDefinition } - } - new(...args: any[]): any -} - -type MetaInfo = GObject.MetaInfo, never> - -export function register(options: MetaInfo = {}) { - return function (cls: GObjectConstructor) { - GObject.registerClass({ - Signals: { ...cls[meta]?.Signals }, - Properties: { ...cls[meta]?.Properties }, - ...options, - }, cls) - } -} - -export function property(declaration: PropertyDeclaration = Object) { - return function (target: any, prop: any, desc?: PropertyDescriptor) { - target.constructor[meta] ??= {} - target.constructor[meta].Properties ??= {} - - const name = kebabify(prop) - - if (!desc) { - let value = defaultValue(declaration) - - Object.defineProperty(target, prop, { - get() { - return value - }, - set(v) { - if (v !== value) { - value = v - this.notify(name) - } - }, - }) - - Object.defineProperty(target, `set_${name.replace("-", "_")}`, { - value: function (v: any) { - this[prop] = v - } - }) - - Object.defineProperty(target, `get_${name.replace("-", "_")}`, { - value: function () { - return this[prop] - } - }) - - target.constructor[meta].Properties[kebabify(prop)] = pspec(name, ParamFlags.READWRITE, declaration) - } - - else { - let flags = 0 - if (desc.get) flags |= ParamFlags.READABLE - if (desc.set) flags |= ParamFlags.WRITABLE - - target.constructor[meta].Properties[kebabify(prop)] = pspec(name, flags, declaration) - } - } -} - -export function signal(...params: Array<{ $gtype: GObject.GType } | typeof Object>): - (target: any, signal: any, desc?: PropertyDescriptor) => void - -export function signal(declaration?: SignalDeclaration): - (target: any, signal: any, desc?: PropertyDescriptor) => void - -export function signal( - declaration?: SignalDeclaration | { $gtype: GObject.GType } | typeof Object, - ...params: Array<{ $gtype: GObject.GType } | typeof Object> -) { - return function (target: any, signal: any, desc?: PropertyDescriptor) { - target.constructor[meta] ??= {} - target.constructor[meta].Signals ??= {} - - const name = kebabify(signal) - - if (declaration || params.length > 0) { - // @ts-expect-error TODO: type assert - const arr = [declaration, ...params].map(v => v.$gtype) - target.constructor[meta].Signals[name] = { - param_types: arr, - } - } - else { - target.constructor[meta].Signals[name] = declaration - } - - if (!desc) { - Object.defineProperty(target, signal, { - value: function (...args: any[]) { - this.emit(name, ...args) - }, - }) - } - else { - const og: ((...args: any[]) => void) = desc.value - desc.value = function (...args: any[]) { - // @ts-expect-error not typed - this.emit(name, ...args) - } - Object.defineProperty(target, `on_${name.replace("-", "_")}`, { - value: function (...args: any[]) { - return og(...args) - }, - }) - } - } -} - -function pspec(name: string, flags: number, declaration: PropertyDeclaration) { - if (declaration instanceof ParamSpec) - return declaration - - switch (declaration) { - case String: - return ParamSpec.string(name, "", "", flags, "") - case Number: - return ParamSpec.double(name, "", "", flags, -Number.MAX_VALUE, Number.MAX_VALUE, 0) - case Boolean: - return ParamSpec.boolean(name, "", "", flags, false) - case Object: - return ParamSpec.jsobject(name, "", "", flags) - default: - // @ts-expect-error misstyped - return ParamSpec.object(name, "", "", flags, declaration.$gtype) - } -} - -function defaultValue(declaration: PropertyDeclaration) { - if (declaration instanceof ParamSpec) - return declaration.get_default_value() - - switch (declaration) { - case String: - return "default-string" - case Number: - return 0 - case Boolean: - return false - case Object: - default: - return null - } -} diff --git a/core/gjs/src/imports.ts b/core/gjs/src/imports.ts deleted file mode 100644 index cbed004..0000000 --- a/core/gjs/src/imports.ts +++ /dev/null @@ -1,10 +0,0 @@ -// this file's purpose is to have glib versions in one place -// this is only really needed for Gtk/Astal because -// ts-gir might generate gtk4 versions too - -export { default as Astal } from "gi://Astal?version=0.1" -export { default as GObject } from "gi://GObject?version=2.0" -export { default as Gio } from "gi://Gio?version=2.0" -export { default as Gtk } from "gi://Gtk?version=3.0" -export { default as Gdk } from "gi://Gdk?version=3.0" -export { default as GLib } from "gi://GLib?version=2.0" diff --git a/core/gjs/src/jsx/jsx-runtime.ts b/core/gjs/src/jsx/jsx-runtime.ts deleted file mode 100644 index f40dc05..0000000 --- a/core/gjs/src/jsx/jsx-runtime.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Gtk } from "../imports.js" -import * as Widget from "../widgets.js" - -function isArrowFunction(func: any): func is (args: any) => any { - return !Object.hasOwn(func, "prototype") -} - -export function jsx( - ctor: keyof typeof ctors | typeof Gtk.Widget, - { children, ...props }: any, -) { - children ??= [] - - if (!Array.isArray(children)) - children = [children] - - children = children.filter(Boolean) - - if (children.length === 1) - props.child = children[0] - else if (children.length > 1) - props.children = children - - if (typeof ctor === "string") { - return new ctors[ctor](props) - } - - if (isArrowFunction(ctor)) - return ctor(props) - - // @ts-expect-error can be class or function - return new ctor(props) -} - -const ctors = { - box: Widget.Box, - button: Widget.Button, - centerbox: Widget.CenterBox, - circularprogress: Widget.CircularProgress, - drawingarea: Widget.DrawingArea, - entry: Widget.Entry, - eventbox: Widget.EventBox, - // TODO: fixed - // TODO: flowbox - icon: Widget.Icon, - label: Widget.Label, - levelbar: Widget.LevelBar, - // TODO: listbox - overlay: Widget.Overlay, - revealer: Widget.Revealer, - scrollable: Widget.Scrollable, - slider: Widget.Slider, - stack: Widget.Stack, - switch: Widget.Switch, - window: Widget.Window, -} - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace JSX { - type Element = Gtk.Widget - type ElementClass = Gtk.Widget - interface IntrinsicElements { - box: Widget.BoxProps - button: Widget.ButtonProps - centerbox: Widget.CenterBoxProps - circularprogress: Widget.CircularProgressProps - drawingarea: Widget.DrawingAreaProps - entry: Widget.EntryProps - eventbox: Widget.EventBoxProps - // TODO: fixed - // TODO: flowbox - icon: Widget.IconProps - label: Widget.LabelProps - levelbar: Widget.LevelBarProps - // TODO: listbox - overlay: Widget.OverlayProps - revealer: Widget.RevealerProps - scrollable: Widget.ScrollableProps - slider: Widget.SliderProps - stack: Widget.StackProps - switch: Widget.SwitchProps - window: Widget.WindowProps - } - } -} - -export const jsxs = jsx diff --git a/core/gjs/src/process.ts b/core/gjs/src/process.ts deleted file mode 100644 index bf8ea28..0000000 --- a/core/gjs/src/process.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Astal } from "./imports.js" - -type Args = { - cmd: string | string[] - out?: (stdout: string) => void - err?: (stderr: string) => void -} - -export function subprocess(args: Args): Astal.Process - -export function subprocess( - cmd: string | string[], - onOut?: (stdout: string) => void, - onErr?: (stderr: string) => void, -): Astal.Process - -export function subprocess( - argsOrCmd: Args | string | string[], - onOut: (stdout: string) => void = print, - onErr: (stderr: string) => void = printerr, -) { - const args = Array.isArray(argsOrCmd) || typeof argsOrCmd === "string" - const { cmd, err, out } = { - cmd: args ? argsOrCmd : argsOrCmd.cmd, - err: args ? onErr : argsOrCmd.err || onErr, - out: args ? onOut : argsOrCmd.out || onOut, - } - - const proc = Array.isArray(cmd) - ? Astal.Process.subprocessv(cmd) - : Astal.Process.subprocess(cmd) - - proc.connect("stdout", (_, stdout: string) => out(stdout)) - proc.connect("stderr", (_, stderr: string) => err(stderr)) - return proc -} - -/** @throws {GLib.Error} Throws stderr */ -export function exec(cmd: string | string[]) { - return Array.isArray(cmd) - ? Astal.Process.execv(cmd) - : Astal.Process.exec(cmd) -} - -export function execAsync(cmd: string | string[]): Promise { - return new Promise((resolve, reject) => { - if (Array.isArray(cmd)) { - Astal.Process.exec_asyncv(cmd, (_, res) => { - try { - resolve(Astal.Process.exec_asyncv_finish(res)) - } - catch (error) { - reject(error) - } - }) - } - else { - Astal.Process.exec_async(cmd, (_, res) => { - try { - resolve(Astal.Process.exec_finish(res)) - } - catch (error) { - reject(error) - } - }) - } - }) -} diff --git a/core/gjs/src/time.ts b/core/gjs/src/time.ts deleted file mode 100644 index 4e28ad0..0000000 --- a/core/gjs/src/time.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Astal } from "./imports.js" - -export function interval(interval: number, callback?: () => void) { - return Astal.Time.interval(interval, () => void callback?.()) -} - -export function timeout(timeout: number, callback?: () => void) { - return Astal.Time.timeout(timeout, () => void callback?.()) -} - -export function idle(callback?: () => void) { - return Astal.Time.idle(() => void callback?.()) -} diff --git a/core/gjs/src/variable.ts b/core/gjs/src/variable.ts deleted file mode 100644 index b61e335..0000000 --- a/core/gjs/src/variable.ts +++ /dev/null @@ -1,230 +0,0 @@ -import Binding, { type Connectable, type Subscribable } from "./binding.js" -import { Astal } from "./imports.js" -import { interval } from "./time.js" -import { execAsync, subprocess } from "./process.js" - -class VariableWrapper extends Function { - private variable!: Astal.VariableBase - private errHandler? = console.error - - private _value: T - private _poll?: Astal.Time - private _watch?: Astal.Process - - private pollInterval = 1000 - private pollExec?: string[] | string - private pollTransform?: (stdout: string, prev: T) => T - private pollFn?: (prev: T) => T | Promise - - private watchTransform?: (stdout: string, prev: T) => T - private watchExec?: string[] | string - - constructor(init: T) { - super() - this._value = init - this.variable = new Astal.VariableBase() - this.variable.connect("dropped", () => { - this.stopWatch() - this.stopPoll() - }) - this.variable.connect("error", (_, err) => this.errHandler?.(err)) - return new Proxy(this, { - apply: (target, _, args) => target._call(args[0]), - }) - } - - private _call(transform?: (value: T) => R): Binding { - const b = Binding.bind(this) - return transform ? b.as(transform) : b as unknown as Binding - } - - toString() { - return String(`Variable<${this.get()}>`) - } - - get(): T { return this._value } - set(value: T) { - if (value !== this._value) { - this._value = value - this.variable.emit("changed") - } - } - - startPoll() { - if (this._poll) - return - - if (this.pollFn) { - this._poll = interval(this.pollInterval, () => { - const v = this.pollFn!(this.get()) - if (v instanceof Promise) { - v.then(v => this.set(v)) - .catch(err => this.variable.emit("error", err)) - } - else { - this.set(v) - } - }) - } - else if (this.pollExec) { - this._poll = interval(this.pollInterval, () => { - execAsync(this.pollExec!) - .then(v => this.set(this.pollTransform!(v, this.get()))) - .catch(err => this.variable.emit("error", err)) - }) - } - } - - startWatch() { - if (this._watch) - return - - this._watch = subprocess({ - cmd: this.watchExec!, - out: out => this.set(this.watchTransform!(out, this.get())), - err: err => this.variable.emit("error", err), - }) - } - - stopPoll() { - this._poll?.cancel() - delete this._poll - } - - stopWatch() { - this._watch?.kill() - delete this._watch - } - - isPolling() { return !!this._poll } - isWatching() { return !!this._watch } - - drop() { - this.variable.emit("dropped") - } - - onDropped(callback: () => void) { - this.variable.connect("dropped", callback) - return this as unknown as Variable - } - - onError(callback: (err: string) => void) { - delete this.errHandler - this.variable.connect("error", (_, err) => callback(err)) - return this as unknown as Variable - } - - subscribe(callback: (value: T) => void) { - const id = this.variable.connect("changed", () => { - callback(this.get()) - }) - return () => this.variable.disconnect(id) - } - - poll( - interval: number, - exec: string | string[], - transform?: (stdout: string, prev: T) => T - ): Variable - - poll( - interval: number, - callback: (prev: T) => T | Promise - ): Variable - - poll( - interval: number, - exec: string | string[] | ((prev: T) => T | Promise), - transform: (stdout: string, prev: T) => T = out => out as T, - ) { - this.stopPoll() - this.pollInterval = interval - this.pollTransform = transform - if (typeof exec === "function") { - this.pollFn = exec - delete this.pollExec - } - else { - this.pollExec = exec - delete this.pollFn - } - this.startPoll() - return this as unknown as Variable - } - - watch( - exec: string | string[], - transform: (stdout: string, prev: T) => T = out => out as T, - ) { - this.stopWatch() - this.watchExec = exec - this.watchTransform = transform - this.startWatch() - return this as unknown as Variable - } - - observe( - objs: Array<[obj: Connectable, signal: string]>, - callback: (...args: any[]) => T, - ): Variable - - observe( - obj: Connectable, - signal: string, - callback: (...args: any[]) => T, - ): Variable - - observe( - objs: Connectable | Array<[obj: Connectable, signal: string]>, - sigOrFn: string | ((obj: Connectable, ...args: any[]) => T), - callback?: (obj: Connectable, ...args: any[]) => T, - ) { - const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => this.get()) - const set = (obj: Connectable, ...args: any[]) => this.set(f(obj, ...args)) - - if (Array.isArray(objs)) { - for (const obj of objs) { - const [o, s] = obj - const id = o.connect(s, set) - this.onDropped(() => o.disconnect(id)) - } - } - else { - if (typeof sigOrFn === "string") { - const id = objs.connect(sigOrFn, set) - this.onDropped(() => objs.disconnect(id)) - } - } - - return this as unknown as Variable - } - - static derive< - const Deps extends Array>, - Args extends { - [K in keyof Deps]: Deps[K] extends Subscribable ? T : never - }, - V = Args, - >(deps: Deps, fn: (...args: Args) => V = (...args) => args as unknown as V) { - const update = () => fn(...deps.map(d => d.get()) as Args) - const derived = new Variable(update()) - const unsubs = deps.map(dep => dep.subscribe(() => derived.set(update()))) - derived.onDropped(() => unsubs.map(unsub => unsub())) - return derived - } -} - -export interface Variable extends Omit, "bind"> { - (transform: (value: T) => R): Binding - (): Binding -} - -export const Variable = new Proxy(VariableWrapper as any, { - apply: (_t, _a, args) => new VariableWrapper(args[0]), -}) as { - derive: typeof VariableWrapper["derive"] - (init: T): Variable - new(init: T): Variable -} - -export default Variable diff --git a/core/gjs/src/widgets.ts b/core/gjs/src/widgets.ts deleted file mode 100644 index e14ca0b..0000000 --- a/core/gjs/src/widgets.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* eslint-disable max-len */ -import { Astal, GObject, Gtk } from "./imports.js" -import astalify, { type ConstructProps, type BindableChild } from "./astalify.js" - -export { astalify, ConstructProps } - -// Box -Object.defineProperty(Astal.Box.prototype, "children", { - get() { return this.get_children() }, - set(v) { this.set_children(v) }, -}) - -export type BoxProps = ConstructProps -export class Box extends astalify(Astal.Box) { - static { GObject.registerClass({ GTypeName: "Box" }, this) } - constructor(props?: BoxProps, ...children: Array) { super({ children, ...props } as any) } -} - -// Button -export type ButtonProps = ConstructProps -export class Button extends astalify(Astal.Button) { - static { GObject.registerClass({ GTypeName: "Button" }, this) } - constructor(props?: ButtonProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// CenterBox -export type CenterBoxProps = ConstructProps -export class CenterBox extends astalify(Astal.CenterBox) { - static { GObject.registerClass({ GTypeName: "CenterBox" }, this) } - constructor(props?: CenterBoxProps, ...children: Array) { super({ children, ...props } as any) } -} - -// CircularProgress -export type CircularProgressProps = ConstructProps -export class CircularProgress extends astalify(Astal.CircularProgress) { - static { GObject.registerClass({ GTypeName: "CircularProgress" }, this) } - constructor(props?: CircularProgressProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// DrawingArea -export type DrawingAreaProps = ConstructProps -export class DrawingArea extends astalify(Gtk.DrawingArea) { - static { GObject.registerClass({ GTypeName: "DrawingArea" }, this) } - constructor(props?: DrawingAreaProps) { super(props as any) } -} - -// Entry -export type EntryProps = ConstructProps -export class Entry extends astalify(Gtk.Entry) { - static { GObject.registerClass({ GTypeName: "Entry" }, this) } - constructor(props?: EntryProps) { super(props as any) } -} - -// EventBox -export type EventBoxProps = ConstructProps -export class EventBox extends astalify(Astal.EventBox) { - static { GObject.registerClass({ GTypeName: "EventBox" }, this) } - constructor(props?: EventBoxProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// // TODO: Fixed -// // TODO: FlowBox -// -// Icon -export type IconProps = ConstructProps -export class Icon extends astalify(Astal.Icon) { - static { GObject.registerClass({ GTypeName: "Icon" }, this) } - constructor(props?: IconProps) { super(props as any) } -} - -// Label -export type LabelProps = ConstructProps -export class Label extends astalify(Astal.Label) { - static { GObject.registerClass({ GTypeName: "Label" }, this) } - constructor(props?: LabelProps) { super(props as any) } -} - -// LevelBar -export type LevelBarProps = ConstructProps -export class LevelBar extends astalify(Astal.LevelBar) { - static { GObject.registerClass({ GTypeName: "LevelBar" }, this) } - constructor(props?: LevelBarProps) { super(props as any) } -} - -// TODO: ListBox - -// Overlay -export type OverlayProps = ConstructProps -export class Overlay extends astalify(Astal.Overlay) { - static { GObject.registerClass({ GTypeName: "Overlay" }, this) } - constructor(props?: OverlayProps, ...children: Array) { super({ children, ...props } as any) } -} - -// Revealer -export type RevealerProps = ConstructProps -export class Revealer extends astalify(Gtk.Revealer) { - static { GObject.registerClass({ GTypeName: "Revealer" }, this) } - constructor(props?: RevealerProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// Scrollable -export type ScrollableProps = ConstructProps -export class Scrollable extends astalify(Astal.Scrollable) { - static { GObject.registerClass({ GTypeName: "Scrollable" }, this) } - constructor(props?: ScrollableProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// Slider -export type SliderProps = ConstructProps -export class Slider extends astalify(Astal.Slider) { - static { GObject.registerClass({ GTypeName: "Slider" }, this) } - constructor(props?: SliderProps) { super(props as any) } -} - -// Stack -export type StackProps = ConstructProps -export class Stack extends astalify(Astal.Stack) { - static { GObject.registerClass({ GTypeName: "Stack" }, this) } - constructor(props?: StackProps, ...children: Array) { super({ children, ...props } as any) } -} - -// Switch -export type SwitchProps = ConstructProps -export class Switch extends astalify(Gtk.Switch) { - static { GObject.registerClass({ GTypeName: "Switch" }, this) } - constructor(props?: SwitchProps) { super(props as any) } -} - -// Window -export type WindowProps = ConstructProps -export class Window extends astalify(Astal.Window) { - static { GObject.registerClass({ GTypeName: "Window" }, this) } - constructor(props?: WindowProps, child?: BindableChild) { super({ child, ...props } as any) } -} diff --git a/core/gjs/tsconfig.json b/core/gjs/tsconfig.json deleted file mode 100644 index b535a07..0000000 --- a/core/gjs/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "experimentalDecorators": true, - "target": "ESNext", - "module": "ESNext", - "lib": [ - "ESNext" - ], - "outDir": "dist", - "declaration": true, - "strict": true, - "moduleResolution": "Bundler", - "skipLibCheck": true, - "checkJs": true, - "allowJs": true, - "jsx": "react-jsx", - "jsxImportSource": "src/jsx", - "baseUrl": ".", - }, - "include": [ - "@girs", - "src/**/*", - "index.ts" - ] -} diff --git a/core/lua/astal-dev-1.rockspec b/core/lua/astal-dev-1.rockspec deleted file mode 100644 index 9a41fd1..0000000 --- a/core/lua/astal-dev-1.rockspec +++ /dev/null @@ -1,31 +0,0 @@ -package = "astal" -version = "dev-1" - -source = { - url = "git+https://github.com/aylur/astal", -} - -description = { - summary = "lua bindings for libastal.", - homepage = "https://aylur.github.io/astal/", - license = "GPL-3", -} - -dependencies = { - "lua >= 5.1, < 5.4", - "lgi >= 0.9.2", -} - -build = { - type = "builtin", - modules = { - ["astal.application"] = "astal/application.lua", - ["astal.binding"] = "astal/binding.lua", - ["astal.init"] = "astal/init.lua", - ["astal.process"] = "astal/process.lua", - ["astal.time"] = "astal/time.lua", - ["astal.variable"] = "astal/variable.lua", - ["astal.widget"] = "astal/widget.lua", - ["astal.file"] = "astal/file.lua", - }, -} diff --git a/core/lua/astal/application.lua b/core/lua/astal/application.lua deleted file mode 100644 index 663a457..0000000 --- a/core/lua/astal/application.lua +++ /dev/null @@ -1,94 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "0.1") - -local AstalLua = Astal.Application:derive("AstalLua") -local request_handler - -function AstalLua:do_request(msg, conn) - if type(request_handler) == "function" then - request_handler(msg, function(response) - Astal.write_sock(conn, tostring(response), function(_, res) - Astal.write_sock_finish(res) - end) - end) - else - Astal.Application.do_request(self, msg, conn) - end -end - -function AstalLua:quit(code) - Astal.Application.quit(self) - os.exit(code) -end - -local app = AstalLua() - ----@class StartConfig ----@field icons? string ----@field instance_name? string ----@field gtk_theme? string ----@field icon_theme? string ----@field cursor_theme? string ----@field css? string ----@field hold? boolean ----@field request_handler? fun(msg: string, response: fun(res: any)) ----@field main? fun(...): unknown ----@field client? fun(message: fun(msg: string): string, ...): unknown - ----@param config StartConfig | nil -function Astal.Application:start(config) - if config == nil then - config = {} - end - - if config.client == nil then - config.client = function() - print('Astal instance "' .. app.instance_name .. '" is already running') - os.exit(1) - end - end - - if config.hold == nil then - config.hold = true - end - - request_handler = config.request_handler - - if config.css then - self:apply_css(config.css) - end - if config.icons then - self:add_icons(config.icons) - end - if config.instance_name then - self.instance_name = config.instance_name - end - if config.gtk_theme then - self.gtk_theme = config.gtk_theme - end - if config.icon_theme then - self.icon_theme = config.icon_theme - end - if config.cursor_theme then - self.cursor_theme = config.cursor_theme - end - - app.on_activate = function() - if type(config.main) == "function" then - config.main(table.unpack(arg)) - end - if config.hold then - self:hold() - end - end - - if not app:acquire_socket() then - return config.client(function(msg) - return Astal.Application.send_message(self.instance_name, msg) - end, table.unpack(arg)) - end - - self:run(nil) -end - -return app diff --git a/core/lua/astal/binding.lua b/core/lua/astal/binding.lua deleted file mode 100644 index ba1e6e4..0000000 --- a/core/lua/astal/binding.lua +++ /dev/null @@ -1,71 +0,0 @@ -local lgi = require("lgi") -local GObject = lgi.require("GObject", "2.0") - ----@class Binding ----@field emitter table|Variable ----@field property? string ----@field transformFn function -local Binding = {} - ----@param emitter table ----@param property? string ----@return Binding -function Binding.new(emitter, property) - return setmetatable({ - emitter = emitter, - property = property, - transformFn = function(v) - return v - end, - }, Binding) -end - -function Binding:__tostring() - local str = "Binding<" .. tostring(self.emitter) - if self.property ~= nil then - str = str .. ", " .. self.property - end - return str .. ">" -end - -function Binding:get() - if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then - return self.transformFn(self.emitter[self.property]) - end - if type(self.emitter.get) == "function" then - return self.transformFn(self.emitter:get()) - end - error("can not get: Not a GObject or a Variable " + self) -end - ----@param transform fun(value: any): any ----@return Binding -function Binding:as(transform) - local b = Binding.new(self.emitter, self.property) - b.transformFn = function(v) - return transform(self.transformFn(v)) - end - return b -end - ----@param callback fun(value: any) ----@return function -function Binding:subscribe(callback) - if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then - local id = self.emitter.on_notify:connect(function() - callback(self:get()) - end, self.property, false) - return function() - GObject.signal_handler_disconnect(self.emitter, id) - end - end - if type(self.emitter.subscribe) == "function" then - return self.emitter:subscribe(function() - callback(self:get()) - end) - end - error("can not subscribe: Not a GObject or a Variable " + self) -end - -Binding.__index = Binding -return Binding diff --git a/core/lua/astal/file.lua b/core/lua/astal/file.lua deleted file mode 100644 index ca5a592..0000000 --- a/core/lua/astal/file.lua +++ /dev/null @@ -1,45 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "0.1") -local GObject = lgi.require("GObject", "2.0") - -local M = {} - ----@param path string ----@return string -function M.read_file(path) - return Astal.read_file(path) -end - ----@param path string ----@param callback fun(content: string, err: string): nil -function M.read_file_async(path, callback) - Astal.read_file_async(path, function(_, res) - local content, err = Astal.read_file_finish(res) - callback(content, err) - end) -end - ----@param path string ----@param content string -function M.write_file(path, content) - Astal.write_file(path, content) -end - ----@param path string ----@param content string ----@param callback? fun(err: string): nil -function M.write_file_async(path, content, callback) - Astal.write_file_async(path, content, function(_, res) - if type(callback) == "function" then - callback(Astal.write_file_finish(res)) - end - end) -end - ----@param path string ----@param callback fun(file: string, event: integer): nil -function M.monitor_file(path, callback) - return Astal.monitor_file(path, GObject.Closure(callback)) -end - -return M diff --git a/core/lua/astal/init.lua b/core/lua/astal/init.lua deleted file mode 100644 index f56c3f5..0000000 --- a/core/lua/astal/init.lua +++ /dev/null @@ -1,41 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "0.1") -local Gtk = lgi.require("Gtk", "3.0") -local Gdk = lgi.require("Gdk", "3.0") -local GObject = lgi.require("GObject", "2.0") -local Widget = require("astal.widget") -local Variable = require("astal.variable") -local Binding = require("astal.binding") -local App = require("astal.application") -local Process = require("astal.process") -local Time = require("astal.time") -local File = require("astal.file") - -return { - App = App, - Variable = Variable, - Widget = Widget, - bind = Binding.new, - - interval = Time.interval, - timeout = Time.timeout, - idle = Time.idle, - - subprocess = Process.subprocess, - exec = Process.exec, - exec_async = Process.exec_async, - - read_file = File.read_file, - read_file_async = File.read_file_async, - write_file = File.write_file, - write_file_async = File.write_file_async, - monitor_file = File.monitor_file, - - Astal = Astal, - Gtk = Gtk, - Gdk = Gdk, - GObject = GObject, - GLib = lgi.require("GLib", "2.0"), - Gio = lgi.require("Gio", "2.0"), - require = lgi.require, -} diff --git a/core/lua/astal/process.lua b/core/lua/astal/process.lua deleted file mode 100644 index 62360b3..0000000 --- a/core/lua/astal/process.lua +++ /dev/null @@ -1,78 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "0.1") - -local M = {} - ----@param commandline string | string[] ----@param on_stdout? fun(out: string): nil ----@param on_stderr? fun(err: string): nil ----@return { kill: function } | nil proc -function M.subprocess(commandline, on_stdout, on_stderr) - if on_stdout == nil then - on_stdout = function(out) - io.stdout:write(tostring(out) .. "\n") - end - end - - if on_stderr == nil then - on_stderr = function(err) - io.stderr:write(tostring(err) .. "\n") - end - end - - local proc, err - if type(commandline) == "table" then - proc, err = Astal.Process.subprocessv(commandline) - else - proc, err = Astal.Process.subprocess(commandline) - end - if err ~= nil then - err(err) - return nil - end - proc.on_stdout = function(_, stdoud) - on_stdout(stdoud) - end - proc.on_stderr = function(_, stderr) - on_stderr(stderr) - end - return proc -end - ----@param commandline string | string[] ----@return string, string -function M.exec(commandline) - if type(commandline) == "table" then - return Astal.Process.execv(commandline) - else - return Astal.Process.exec(commandline) - end -end - ----@param commandline string | string[] ----@param callback? fun(out: string, err: string): nil -function M.exec_async(commandline, callback) - if callback == nil then - callback = function(out, err) - if err ~= nil then - io.stdout:write(tostring(out) .. "\n") - else - io.stderr:write(tostring(err) .. "\n") - end - end - end - - if type(commandline) == "table" then - Astal.Process.exec_asyncv(commandline, function(_, res) - local out, err = Astal.Process.exec_asyncv_finish(res) - callback(out, err) - end) - else - Astal.Process.exec_async(commandline, function(_, res) - local out, err = Astal.Process.exec_finish(res) - callback(out, err) - end) - end -end - -return M diff --git a/core/lua/astal/time.lua b/core/lua/astal/time.lua deleted file mode 100644 index f4e2b81..0000000 --- a/core/lua/astal/time.lua +++ /dev/null @@ -1,27 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "0.1") -local GObject = lgi.require("GObject", "2.0") - -local M = {} - ----@param interval number ----@param fn function ----@return { cancel: function, on_now: function } -function M.interval(interval, fn) - return Astal.Time.interval(interval, GObject.Closure(fn)) -end - ----@param timeout number ----@param fn function ----@return { cancel: function, on_now: function } -function M.timeout(timeout, fn) - return Astal.Time.timeout(timeout, GObject.Closure(fn)) -end - ----@param fn function ----@return { cancel: function, on_now: function } -function M.idle(fn) - return Astal.Time.idle(GObject.Closure(fn)) -end - -return M diff --git a/core/lua/astal/variable.lua b/core/lua/astal/variable.lua deleted file mode 100644 index 662eee7..0000000 --- a/core/lua/astal/variable.lua +++ /dev/null @@ -1,276 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "0.1") -local GObject = lgi.require("GObject", "2.0") -local Binding = require("astal.binding") -local Time = require("astal.time") -local Process = require("astal.process") - ----@class Variable ----@field private variable table ----@field private err_handler? function ----@field private _value any ----@field private _poll? table ----@field private _watch? table ----@field private poll_interval number ----@field private poll_exec? string[] | string ----@field private poll_transform? fun(next: any, prev: any): any ----@field private poll_fn? function ----@field private watch_transform? fun(next: any, prev: any): any ----@field private watch_exec? string[] | string -local Variable = {} -Variable.__index = Variable - ----@param value any ----@return Variable -function Variable.new(value) - local v = Astal.VariableBase() - local variable = setmetatable({ - variable = v, - _value = value, - }, Variable) - v.on_dropped = function() - variable:stop_watch() - variable:stop_watch() - end - v.on_error = function(_, err) - if variable.err_handler then - variable.err_handler(err) - end - end - return variable -end - ----@param transform function ----@return Binding -function Variable:__call(transform) - if transform == nil then - transform = function(v) - return v - end - return Binding.new(self) - end - return Binding.new(self):as(transform) -end - -function Variable:__tostring() - return "Variable<" .. tostring(self:get()) .. ">" -end - -function Variable:get() - return self._value or nil -end - -function Variable:set(value) - if value ~= self:get() then - self._value = value - self.variable:emit_changed() - end -end - -function Variable:start_poll() - if self._poll ~= nil then - return - end - - if self.poll_fn then - self._poll = Time.interval(self.poll_interval, function() - self:set(self.poll_fn(self:get())) - end) - elseif self.poll_exec then - self._poll = Time.interval(self.poll_interval, function() - Process.exec_async(self.poll_exec, function(out, err) - if err ~= nil then - return self.variable.emit_error(err) - end - self:set(self.poll_transform(out, self:get())) - end) - end) - end -end - -function Variable:start_watch() - if self._watch then - return - end - - self._watch = Process.subprocess(self.watch_exec, function(out) - self:set(self.watch_transform(out, self:get())) - end, function(err) - self.variable.emit_error(err) - end) -end - -function Variable:stop_poll() - if self._poll then - self._poll.cancel() - end - self._poll = nil -end - -function Variable:stop_watch() - if self._watch then - self._watch.kill() - end - self._watch = nil -end - -function Variable:is_polling() - return self._poll ~= nil -end - -function Variable:is_watching() - return self._watch ~= nil -end - -function Variable:drop() - self.variable.emit_dropped() -end - ----@param callback function ----@return Variable -function Variable:on_dropped(callback) - self.variable.on_dropped = callback - return self -end - ----@param callback function ----@return Variable -function Variable:on_error(callback) - self.err_handler = nil - self.variable.on_eror = function(_, err) - callback(err) - end - return self -end - ----@param callback fun(value: any) ----@return function -function Variable:subscribe(callback) - local id = self.variable.on_changed:connect(function() - callback(self:get()) - end) - return function() - GObject.signal_handler_disconnect(self.variable, id) - end -end - ----@param interval number ----@param exec string | string[] | function ----@param transform? fun(next: any, prev: any): any -function Variable:poll(interval, exec, transform) - if transform == nil then - transform = function(next) - return next - end - end - self:stop_poll() - self.poll_interval = interval - self.poll_transform = transform - - if type(exec) == "function" then - self.poll_fn = exec - self.poll_exec = nil - else - self.poll_exec = exec - self.poll_fn = nil - end - self:start_poll() - return self -end - ----@param exec string | string[] ----@param transform? fun(next: any, prev: any): any -function Variable:watch(exec, transform) - if transform == nil then - transform = function(next) - return next - end - end - self:stop_poll() - self.watch_exec = exec - self.watch_transform = transform - self:start_watch() - return self -end - ----@param object table | table[] ----@param sigOrFn string | fun(...): any ----@param callback fun(...): any ----@return Variable -function Variable:observe(object, sigOrFn, callback) - local f - if type(sigOrFn) == "function" then - f = sigOrFn - elseif type(callback) == "function" then - f = callback - else - f = function() - return self:get() - end - end - local set = function(...) - self:set(f(...)) - end - - if type(sigOrFn) == "string" then - object["on_" .. sigOrFn]:connect(set) - else - for _, obj in ipairs(object) do - obj[1]["on_" .. obj[2]]:connect(set) - end - end - return self -end - ----@param deps Variable | (Binding | Variable)[] ----@param transform? fun(...): any ----@return Variable -function Variable.derive(deps, transform) - if type(transform) == "nil" then - transform = function(...) - return { ... } - end - end - - if getmetatable(deps) == Variable then - local var = Variable.new(transform(deps:get())) - deps:subscribe(function(v) - var:set(transform(v)) - end) - return var - end - - for i, var in ipairs(deps) do - if getmetatable(var) == Variable then - deps[i] = Binding.new(var) - end - end - - local update = function() - local params = {} - for i, binding in ipairs(deps) do - params[i] = binding:get() - end - return transform(table.unpack(params), 1, #deps) - end - - local var = Variable.new(update()) - - local unsubs = {} - for i, b in ipairs(deps) do - unsubs[i] = b:subscribe(update) - end - - var.variable.on_dropped = function() - for _, unsub in ipairs(unsubs) do - unsub() - end - end - return var -end - -return setmetatable(Variable, { - __call = function(_, v) - return Variable.new(v) - end, -}) diff --git a/core/lua/astal/widget.lua b/core/lua/astal/widget.lua deleted file mode 100644 index 90831bc..0000000 --- a/core/lua/astal/widget.lua +++ /dev/null @@ -1,322 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "0.1") -local Gtk = lgi.require("Gtk", "3.0") -local GObject = lgi.require("GObject", "2.0") -local Binding = require("astal.binding") -local Variable = require("astal.variable") -local exec_async = require("astal.process").exec_async - -local function filter(tbl, fn) - local copy = {} - for key, value in pairs(tbl) do - if fn(value, key) then - if type(key) == "number" then - table.insert(copy, value) - else - copy[key] = value - end - end - end - return copy -end - -local function map(tbl, fn) - local copy = {} - for key, value in pairs(tbl) do - copy[key] = fn(value) - end - return copy -end - -local flatten -flatten = function(tbl) - local copy = {} - for _, value in pairs(tbl) do - if type(value) == "table" and getmetatable(value) == nil then - for _, inner in pairs(flatten(value)) do - table.insert(copy, inner) - end - else - table.insert(copy, value) - end - end - return copy -end - -local function includes(tbl, elem) - for _, value in pairs(tbl) do - if value == elem then - return true - end - end - return false -end - -local function set_children(parent, children) - children = map(flatten(children), function(item) - if Gtk.Widget:is_type_of(item) then - return item - end - return Gtk.Label({ - visible = true, - label = tostring(item), - }) - end) - - -- remove - if Gtk.Bin:is_type_of(parent) then - local ch = parent:get_child() - if ch ~= nil then - parent:remove(ch) - end - if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then - ch:destroy() - end - elseif Gtk.Container:is_type_of(parent) then - for _, ch in ipairs(parent:get_children()) do - parent:remove(ch) - if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then - ch:destroy() - end - end - end - - -- TODO: add more container types - if Astal.Box:is_type_of(parent) then - parent:set_children(children) - elseif Astal.Stack:is_type_of(parent) then - parent:set_children(children) - elseif Astal.CenterBox:is_type_of(parent) then - parent.start_widget = children[1] - parent.center_widget = children[2] - parent.end_widget = children[3] - elseif Astal.Overlay:is_type_of(parent) then - parent:set_child(children[1]) - children[1] = nil - parent:set_overlays(children) - elseif Gtk.Container:is_type_of(parent) then - for _, child in pairs(children) do - if Gtk.Widget:is_type_of(child) then - parent:add(child) - end - end - end -end - -local function merge_bindings(array) - local function get_values(...) - local args = { ... } - local i = 0 - return map(array, function(value) - if getmetatable(value) == Binding then - i = i + 1 - return args[i] - else - return value - end - end) - end - - local bindings = filter(array, function(v) - return getmetatable(v) == Binding - end) - - if #bindings == 0 then - return array - end - - if #bindings == 1 then - return bindings[1]:as(get_values) - end - - return Variable.derive(bindings, get_values)() -end - -local function astalify(ctor) - function ctor:hook(object, signalOrCallback, callback) - if GObject.Object:is_type_of(object) and type(signalOrCallback) == "string" then - local id - if string.sub(signalOrCallback, 1, 8) == "notify::" then - local prop = string.gsub(signalOrCallback, "notify::", "") - id = object.on_notify:connect(function() - callback(self, object[prop]) - end, prop, false) - else - id = object["on_" .. signalOrCallback]:connect(function(_, ...) - callback(self, ...) - end) - end - self.on_destroy = function() - GObject.signal_handler_disconnect(object, id) - end - elseif type(object.subscribe) == "function" then - local unsub = object.subscribe(function(...) - signalOrCallback(self, ...) - end) - self.on_destroy = unsub - else - error("can not hook: not gobject+signal or subscribable") - end - end - - function ctor:toggle_class_name(name, on) - Astal.widget_toggle_class_name(self, name, on) - end - - return function(tbl) - if tbl == nil then - tbl = {} - end - - local bindings = {} - local setup = tbl.setup - - -- collect children - local children = merge_bindings(flatten(filter(tbl, function(_, key) - return type(key) == "number" - end))) - - -- default visible to true - if type(tbl.visible) ~= "boolean" then - tbl.visible = true - end - - -- collect props - local props = filter(tbl, function(_, key) - return type(key) == "string" and key ~= "setup" - end) - - -- collect signal handlers - for prop, value in pairs(props) do - if string.sub(prop, 0, 2) == "on" and type(value) ~= "function" then - props[prop] = function() - exec_async(value, print) - end - end - end - - -- collect bindings - for prop, value in pairs(props) do - if getmetatable(value) == Binding then - bindings[prop] = value - props[prop] = value:get() - end - end - - -- construct, attach bindings, add children - local widget = ctor() - - if getmetatable(children) == Binding then - set_children(widget, children:get()) - widget.on_destroy = children:subscribe(function(v) - set_children(widget, v) - end) - else - if #children > 0 then - set_children(widget, children) - end - end - - for prop, binding in pairs(bindings) do - widget.on_destroy = binding:subscribe(function(v) - widget[prop] = v - end) - end - - for prop, value in pairs(props) do - widget[prop] = value - end - - if type(setup) == "function" then - setup(widget) - end - - return widget - end -end - -local Widget = { - astalify = astalify, - Box = astalify(Astal.Box), - Button = astalify(Astal.Button), - CenterBox = astalify(Astal.CenterBox), - CircularProgress = astalify(Astal.CircularProgress), - DrawingArea = astalify(Gtk.DrawingArea), - Entry = astalify(Gtk.Entry), - EventBox = astalify(Astal.EventBox), - -- TODO: Fixed - -- TODO: FlowBox - Icon = astalify(Astal.Icon), - Label = astalify(Gtk.Label), - LevelBar = astalify(Astal.LevelBar), - -- TODO: ListBox - Overlay = astalify(Astal.Overlay), - Revealer = astalify(Gtk.Revealer), - Scrollable = astalify(Astal.Scrollable), - Slider = astalify(Astal.Slider), - Stack = astalify(Astal.Stack), - Switch = astalify(Gtk.Switch), - Window = astalify(Astal.Window), -} - -Gtk.Widget._attribute.css = { - get = Astal.widget_get_css, - set = Astal.widget_set_css, -} - -Gtk.Widget._attribute.class_name = { - get = function(self) - local result = "" - local strings = Astal.widget_get_class_names(self) - for i, str in ipairs(strings) do - result = result .. str - if i < #strings then - result = result .. " " - end - end - return result - end, - set = function(self, class_name) - local names = {} - for word in class_name:gmatch("%S+") do - table.insert(names, word) - end - Astal.widget_set_class_names(self, names) - end, -} - -Gtk.Widget._attribute.cursor = { - get = Astal.widget_get_cursor, - set = Astal.widget_set_cursor, -} - -Gtk.Widget._attribute.click_through = { - get = Astal.widget_get_click_through, - set = Astal.widget_set_click_through, -} - -local no_implicit_destroy = {} -Gtk.Widget._attribute.no_implicit_destroy = { - get = function(self) - return no_implicit_destroy[self] or false - end, - set = function(self, v) - if no_implicit_destroy[self] == nil then - self.on_destroy = function() - no_implicit_destroy[self] = nil - end - end - no_implicit_destroy[self] = v - end, -} - -Astal.Box._attribute.children = { - get = Astal.Box.get_children, - set = Astal.Box.set_children, -} - -return setmetatable(Widget, { - __call = function(_, ctor) - return astalify(ctor) - end, -}) diff --git a/core/lua/stylua.toml b/core/lua/stylua.toml deleted file mode 100644 index d4a4951..0000000 --- a/core/lua/stylua.toml +++ /dev/null @@ -1,3 +0,0 @@ -indent_type = "Spaces" -indent_width = 4 -column_width = 100 diff --git a/core/meson.build b/core/meson.build deleted file mode 100644 index a2606db..0000000 --- a/core/meson.build +++ /dev/null @@ -1,27 +0,0 @@ -project( - 'astal', - 'vala', - 'c', - version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(), - meson_version: '>= 0.62.0', - default_options: [ - 'warning_level=2', - 'werror=false', - 'c_std=gnu11', - ], -) - -prefix = get_option('prefix') -libdir = get_option('prefix') / get_option('libdir') -pkgdatadir = prefix / get_option('datadir') / 'astal' - -assert( - get_option('lib') or get_option('cli'), - 'Either lib or cli option must be set to true.', -) - -if get_option('gjs') - install_subdir('gjs', install_dir: pkgdatadir) -endif - -subdir('src') diff --git a/core/meson_options.txt b/core/meson_options.txt deleted file mode 100644 index a60ff42..0000000 --- a/core/meson_options.txt +++ /dev/null @@ -1,17 +0,0 @@ -option( - 'lib', - type: 'boolean', - value: true, -) - -option( - 'cli', - type: 'boolean', - value: true, -) - -option( - 'gjs', - type: 'boolean', - value: true, -) diff --git a/core/src/astal.vala b/core/src/astal.vala deleted file mode 100644 index 176062f..0000000 --- a/core/src/astal.vala +++ /dev/null @@ -1,389 +0,0 @@ -namespace Astal { -[DBus (name="io.Astal.Application")] -public class Application : Gtk.Application { - private List css_providers = new List(); - private SocketService service; - private DBusConnection conn; - private string _instance_name; - - public string socket_path { get; private set; } - - [DBus (visible=false)] - public signal void monitor_added(Gdk.Monitor monitor); - - [DBus (visible=false)] - public signal void monitor_removed(Gdk.Monitor monitor); - - [DBus (visible=false)] - public signal void window_toggled(Gtk.Window window); - - [DBus (visible=false)] - public List monitors { - owned get { - var display = Gdk.Display.get_default(); - var list = new List(); - for (var i = 0; i <= display.get_n_monitors(); ++i) { - var mon = display.get_monitor(i); - if (mon != null) { - list.append(mon); - } - } - return list; - } - } - - [DBus (visible=false)] - public string instance_name { - get { return _instance_name; } - set { - application_id = "io.Astal." + value; - _instance_name = value; - } - } - - [DBus (visible=false)] - public List windows { - get { return get_windows(); } - } - - [DBus (visible=false)] - public Gtk.Settings settings { - get { return Gtk.Settings.get_default(); } - } - - [DBus (visible=false)] - public Gdk.Screen screen { - get { return Gdk.Screen.get_default(); } - } - - [DBus (visible=false)] - public string gtk_theme { - owned get { return settings.gtk_theme_name; } - set { settings.gtk_theme_name = value; } - } - - [DBus (visible=false)] - public string icon_theme { - owned get { return settings.gtk_icon_theme_name; } - set { settings.gtk_icon_theme_name = value; } - } - - [DBus (visible=false)] - public string cursor_theme { - owned get { return settings.gtk_cursor_theme_name; } - set { settings.gtk_cursor_theme_name = value; } - } - - [DBus (visible=false)] - public void reset_css() { - foreach(var provider in css_providers) { - Gtk.StyleContext.remove_provider_for_screen(screen, provider); - } - css_providers = new List(); - } - - public void inspector() throws DBusError, IOError { - Gtk.Window.set_interactive_debugging(true); - } - - [DBus (visible=false)] - public Gtk.Window? get_window(string name) { - foreach(var win in windows) { - if (win.name == name) - return win; - } - - critical("no window with name \"%s\"".printf(name)); - return null; - } - - public void toggle_window(string window) throws DBusError, IOError { - var win = get_window(window); - if (win != null) { - win.visible = !win.visible; - } else { - throw new IOError.FAILED("window not found"); - } - } - - [DBus (visible=false)] - public void apply_css(string style, bool reset = false) { - var provider = new Gtk.CssProvider(); - - if (reset) - reset_css(); - - try { - if (FileUtils.test(style, FileTest.EXISTS)) - provider.load_from_path(style); - else - provider.load_from_data(style); - } catch (Error err) { - critical(err.message); - } - - Gtk.StyleContext.add_provider_for_screen( - screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_USER); - - css_providers.append(provider); - } - - [DBus (visible=false)] - public void add_icons(string? path) { - if (path != null) - Gtk.IconTheme.get_default().prepend_search_path(path); - } - - private async void _socket_request(SocketConnection conn) { - string message = yield read_sock(conn); - request(message != null ? message.strip() : "", conn); - } - - [DBus (visible=false)] - public virtual void request(string msg, SocketConnection conn) { - write_sock.begin(conn, @"missing response implementation on $application_id"); - } - - /** - * should be called before `run()` - * the return value indicates if instance is already running - */ - [DBus (visible=false)] - public bool acquire_socket() { - foreach (var instance in get_instances()) { - if (instance == instance_name) { - return false; - } - } - - var rundir = GLib.Environment.get_user_runtime_dir(); - socket_path = @"$rundir/$instance_name.sock"; - - if (FileUtils.test(socket_path, GLib.FileTest.EXISTS)) { - try { - File.new_for_path(socket_path).delete(null); - } catch (Error err) { - critical(err.message); - } - } - - try { - service = new SocketService(); - service.add_address( - new UnixSocketAddress(socket_path), - SocketType.STREAM, - SocketProtocol.DEFAULT, - null, - null); - - service.incoming.connect((conn) => { - _socket_request.begin(conn, (_, res) => _socket_request.end(res)); - return false; - }); - - Bus.own_name( - BusType.SESSION, - "io.Astal." + instance_name, - BusNameOwnerFlags.NONE, - (conn) => { - try { - this.conn = conn; - conn.register_object("/io/Astal/Application", this); - } catch (Error err) { - critical(err.message); - } - }, - () => {}, - () => {}); - - info("socket acquired: %s\n", socket_path); - return true; - } catch (Error err) { - critical("could not acquire socket %s\n", application_id); - critical(err.message); - return false; - } - } - - public string message(string? msg) throws DBusError, IOError { - var rundir = GLib.Environment.get_user_runtime_dir(); - var socket_path = @"$rundir/$instance_name.sock"; - var client = new SocketClient(); - - if (msg == null) - msg = ""; - - try { - var conn = client.connect(new UnixSocketAddress(socket_path), null); - conn.output_stream.write(msg.concat("\x04").data); - - var stream = new DataInputStream(conn.input_stream); - return stream.read_upto("\x04", -1, null, null); - } catch (Error err) { - printerr(err.message); - return ""; - } - } - - public new void quit() throws DBusError, IOError { - if (service != null) { - if (FileUtils.test(socket_path, GLib.FileTest.EXISTS)){ - try { - File.new_for_path(socket_path).delete(null); - } catch (Error err) { - warning(err.message); - } - } - } - - base.quit(); - } - - construct { - if (instance_name == null) - instance_name = "astal"; - - activate.connect(() => { - var display = Gdk.Display.get_default(); - display.monitor_added.connect((mon) => { - monitor_added(mon); - notify_property("monitors"); - }); - display.monitor_removed.connect((mon) => { - monitor_removed(mon); - notify_property("monitors"); - }); - }); - - window_added.connect((window) => { - ulong id1, id2; - id1 = window.notify["visible"].connect(() => window_toggled(window)); - id2 = window_removed.connect((removed) => { - if (removed == window) { - window.disconnect(id1); - this.disconnect(id2); - } - }); - }); - - shutdown.connect(() => { try { quit(); } catch(Error err) {} }); - Unix.signal_add(1, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); - Unix.signal_add(2, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); - Unix.signal_add(15, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); - } - - public static List get_instances() { - var list = new List(); - var prefix = "io.Astal."; - - try { - DBusImpl dbus = Bus.get_proxy_sync( - BusType.SESSION, - "org.freedesktop.DBus", - "/org/freedesktop/DBus" - ); - - foreach (var busname in dbus.list_names()) { - if (busname.has_prefix(prefix)) - list.append(busname.replace(prefix, "")); - } - } catch (Error err) { - critical(err.message); - } - - return list; - } - - public static void quit_instance(string instance) { - try { - IApplication proxy = Bus.get_proxy_sync( - BusType.SESSION, - "io.Astal." + instance, - "/io/Astal/Application" - ); - - proxy.quit(); - } catch (Error err) { - critical(err.message); - } - } - - public static void open_inspector(string instance) { - try { - IApplication proxy = Bus.get_proxy_sync( - BusType.SESSION, - "io.Astal." + instance, - "/io/Astal/Application" - ); - - proxy.inspector(); - } catch (Error err) { - critical(err.message); - } - } - - public static void toggle_window_by_name(string instance, string window) { - try { - IApplication proxy = Bus.get_proxy_sync( - BusType.SESSION, - "io.Astal." + instance, - "/io/Astal/Application" - ); - - proxy.toggle_window(window); - } catch (Error err) { - critical(err.message); - } - } - - public static string send_message(string instance_name, string msg) { - var rundir = GLib.Environment.get_user_runtime_dir(); - var socket_path = @"$rundir/$instance_name.sock"; - var client = new SocketClient(); - - try { - var conn = client.connect(new UnixSocketAddress(socket_path), null); - conn.output_stream.write(msg.concat("\x04").data); - - var stream = new DataInputStream(conn.input_stream); - return stream.read_upto("\x04", -1, null, null); - } catch (Error err) { - printerr(err.message); - return ""; - } - } -} - -[DBus (name="org.freedesktop.DBus")] -private interface DBusImpl : DBusProxy { - public abstract string[] list_names() throws GLib.Error; -} - -[DBus (name="io.Astal.Application")] -private interface IApplication : DBusProxy { - public abstract void quit() throws GLib.Error; - public abstract void inspector() throws GLib.Error; - public abstract void toggle_window(string window) throws GLib.Error; - public abstract string message(string window) throws GLib.Error; -} - -public async string read_sock(SocketConnection conn) { - try { - var stream = new DataInputStream(conn.input_stream); - return yield stream.read_upto_async("\x04", -1, Priority.DEFAULT, null, null); - } catch (Error err) { - critical(err.message); - return err.message; - } -} - -public async void write_sock(SocketConnection conn, string response) { - try { - yield conn.output_stream.write_async( - response.concat("\x04").data, - Priority.DEFAULT); - } catch (Error err) { - critical(err.message); - } -} -} diff --git a/core/src/cli.vala b/core/src/cli.vala deleted file mode 100644 index 0b60cd1..0000000 --- a/core/src/cli.vala +++ /dev/null @@ -1,87 +0,0 @@ -private static bool version; -private static bool help; -private static bool list; -private static bool quit; -private static bool inspector; -private static string? toggle_window; -private static string? instance_name; - -private const GLib.OptionEntry[] options = { - { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null }, - { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null }, - { "list", 'l', OptionFlags.NONE, OptionArg.NONE, ref list, null, null }, - { "quit", 'q', OptionFlags.NONE, OptionArg.NONE, ref quit, null, null }, - { "quit", 'q', OptionFlags.NONE, OptionArg.NONE, ref quit, null, null }, - { "inspector", 'I', OptionFlags.NONE, OptionArg.NONE, ref inspector, null, null }, - { "toggle-window", 't', OptionFlags.NONE, OptionArg.STRING, ref toggle_window, null, null }, - { "instance", 'i', OptionFlags.NONE, OptionArg.STRING, ref instance_name, null, null }, - { null }, -}; - -int main(string[] argv) { - try { - var opts = new OptionContext(); - opts.add_main_entries(options, null); - opts.set_help_enabled(false); - opts.set_ignore_unknown_options(false); - opts.parse(ref argv); - } catch (OptionError err) { - printerr (err.message); - return 1; - } - - if (help) { - print("Client for Astal.Application instances\n\n"); - print("Usage:\n"); - print(" %s [flags] message\n\n", argv[0]); - print("Flags:\n"); - print(" -h, --help Print this help and exit\n"); - print(" -v, --version Print version number and exit\n"); - print(" -l, --list List running Astal instances and exit\n"); - print(" -q, --quit Quit an Astal.Application instance\n"); - print(" -i, --instance Instance name of the Astal instance\n"); - print(" -I, --inspector Open up Gtk debug tool\n"); - print(" -t, --toggle-window Show or hide a window\n"); - return 0; - } - - if (version) { - print(Astal.VERSION); - return 0; - } - - if (instance_name == null) - instance_name = "astal"; - - if (list) { - foreach (var name in Astal.Application.get_instances()) - stdout.printf("%s\n", name); - - return 0; - } - - if (quit) { - Astal.Application.quit_instance(instance_name); - return 0; - } - - if (inspector) { - Astal.Application.open_inspector(instance_name); - return 0; - } - - if (toggle_window != null) { - Astal.Application.toggle_window_by_name(instance_name, toggle_window); - return 0; - } - - var request = ""; - for (var i = 1; i < argv.length; ++i) { - request = request.concat(" ", argv[i]); - } - - var reply = Astal.Application.send_message(instance_name, request); - print("%s\n", reply); - - return 0; -} diff --git a/core/src/config.vala.in b/core/src/config.vala.in deleted file mode 100644 index 88bfe9c..0000000 --- a/core/src/config.vala.in +++ /dev/null @@ -1,6 +0,0 @@ -namespace Astal { - public const int MAJOR_VERSION = @MAJOR_VERSION@; - public const int MINOR_VERSION = @MINOR_VERSION@; - public const int MICRO_VERSION = @MICRO_VERSION@; - public const string VERSION = "@VERSION@"; -} diff --git a/core/src/file.vala b/core/src/file.vala deleted file mode 100644 index d8acccc..0000000 --- a/core/src/file.vala +++ /dev/null @@ -1,81 +0,0 @@ -namespace Astal { -public string read_file(string path) { - var str = ""; - try { - FileUtils.get_contents(path, out str, null); - } catch (Error error) { - critical(error.message); - } - return str; -} - -public async string read_file_async(string path) throws Error { - uint8[] content; - yield File.new_for_path(path).load_contents_async(null, out content, null); - return (string)content; -} - -public void write_file(string path, string content) { - try { - FileUtils.set_contents(path, content); - } catch (Error error) { - critical(error.message); - } -} - -public async void write_file_async(string path, string content) throws Error { - yield File.new_for_path(path).replace_contents_async( - content.data, - null, - false, - GLib.FileCreateFlags.REPLACE_DESTINATION, - null, - null); -} - -public FileMonitor? monitor_file(string path, Closure callback) { - try { - var file = File.new_for_path(path); - var mon = file.monitor(GLib.FileMonitorFlags.NONE); - - mon.changed.connect((file, _file, event) => { - var f = Value(Type.STRING); - var e = Value(Type.INT); - var ret = Value(Type.POINTER); - - f.set_string(file.get_path()); - e.set_int(event); - - callback.invoke(ref ret, { f, e }); - }); - - if (FileUtils.test(path, FileTest.IS_DIR)) { - var enumerator = file.enumerate_children("standard::*", - FileQueryInfoFlags.NONE, null); - - var i = enumerator.next_file(null); - while (i != null) { - if (i.get_file_type() == FileType.DIRECTORY) { - var filepath = file.get_child(i.get_name()).get_path(); - if (filepath != null) { - var m = monitor_file(path, callback); - mon.notify["cancelled"].connect(() => { - m.cancel(); - }); - } - } - i = enumerator.next_file(null); - } - } - - mon.ref(); - mon.notify["cancelled"].connect(() => { - mon.unref(); - }); - return mon; - } catch (Error error) { - critical(error.message); - return null; - } -} -} diff --git a/core/src/idle-inhibit.c b/core/src/idle-inhibit.c deleted file mode 100644 index 48f2471..0000000 --- a/core/src/idle-inhibit.c +++ /dev/null @@ -1,114 +0,0 @@ -#include "idle-inhibit.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "idle-inhibit-unstable-v1-client.h" - -struct _AstalInhibitManager { - GObject parent_instance; -}; - -typedef struct { - gboolean init; - struct wl_registry* wl_registry; - struct wl_display* display; - struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager; -} AstalInhibitManagerPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE(AstalInhibitManager, astal_inhibit_manager, G_TYPE_OBJECT) - -AstalInhibitor* astal_inhibit_manager_inhibit(AstalInhibitManager* self, GtkWindow* window) { - AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); - g_assert_true(priv->init); - GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); - struct wl_surface* surface = gdk_wayland_window_get_wl_surface(gdk_window); - return zwp_idle_inhibit_manager_v1_create_inhibitor(priv->idle_inhibit_manager, surface); -} - -static void global_registry_handler(void* data, struct wl_registry* registry, uint32_t id, - const char* interface, uint32_t version) { - AstalInhibitManager* self = ASTAL_INHIBIT_MANAGER(data); - AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); - - if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { - priv->idle_inhibit_manager = - wl_registry_bind(registry, id, &zwp_idle_inhibit_manager_v1_interface, 1); - } -} - -static void global_registry_remover(void* data, struct wl_registry* registry, uint32_t id) { - // neither inhibit_manager nor inhibitor is going to be removed by the compositor, so we don't - // need do anything here. -} - -static const struct wl_registry_listener registry_listener = {global_registry_handler, - global_registry_remover}; - -static gboolean astal_inhibit_manager_wayland_init(AstalInhibitManager* self) { - AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); - - if (priv->init) return TRUE; - - GdkDisplay* gdk_display = gdk_display_get_default(); - priv->display = gdk_wayland_display_get_wl_display(gdk_display); - - priv->wl_registry = wl_display_get_registry(priv->display); - wl_registry_add_listener(priv->wl_registry, ®istry_listener, self); - - wl_display_roundtrip(priv->display); - - if (priv->idle_inhibit_manager == NULL) { - g_critical("Can not connect idle inhibitor protocol"); - return FALSE; - } - - priv->init = TRUE; - return TRUE; -} - -AstalInhibitManager* astal_inhibit_manager_get_default() { - static AstalInhibitManager* self = NULL; - - if (self == NULL) { - self = g_object_new(ASTAL_TYPE_INHIBIT_MANAGER, NULL); - if (!astal_inhibit_manager_wayland_init(self)) { - g_object_unref(self); - self = NULL; - } - } - - return self; -} - -static void astal_inhibit_manager_init(AstalInhibitManager* self) { - AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); - priv->init = FALSE; - priv->display = NULL; - priv->wl_registry = NULL; - priv->idle_inhibit_manager = NULL; -} - -static void astal_inhibit_manager_finalize(GObject* object) { - AstalInhibitManager* self = ASTAL_INHIBIT_MANAGER(object); - AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); - - if (priv->display != NULL) wl_display_roundtrip(priv->display); - - if (priv->wl_registry != NULL) wl_registry_destroy(priv->wl_registry); - if (priv->idle_inhibit_manager != NULL) - zwp_idle_inhibit_manager_v1_destroy(priv->idle_inhibit_manager); - - G_OBJECT_CLASS(astal_inhibit_manager_parent_class)->finalize(object); -} - -static void astal_inhibit_manager_class_init(AstalInhibitManagerClass* class) { - GObjectClass* object_class = G_OBJECT_CLASS(class); - object_class->finalize = astal_inhibit_manager_finalize; -} diff --git a/core/src/idle-inhibit.h b/core/src/idle-inhibit.h deleted file mode 100644 index 5e9a3ab..0000000 --- a/core/src/idle-inhibit.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ASTAL_IDLE_INHIBITOR_H -#define ASTAL_IDLE_INHIBITOR_H - -#include -#include - -#include "idle-inhibit-unstable-v1-client.h" - -G_BEGIN_DECLS - -#define ASTAL_TYPE_INHIBIT_MANAGER (astal_inhibit_manager_get_type()) - -G_DECLARE_FINAL_TYPE(AstalInhibitManager, astal_inhibit_manager, ASTAL, INHIBIT_MANAGER, GObject) - -typedef struct zwp_idle_inhibitor_v1 AstalInhibitor; - -AstalInhibitManager* astal_inhibit_manager_get_default(); -AstalInhibitor* astal_inhibit_manager_inhibit(AstalInhibitManager* self, GtkWindow* window); - -G_END_DECLS - -#endif // !ASTAL_IDLE_INHIBITOR_H diff --git a/core/src/meson.build b/core/src/meson.build deleted file mode 100644 index b5adda2..0000000 --- a/core/src/meson.build +++ /dev/null @@ -1,136 +0,0 @@ -version_split = meson.project_version().split('.') -api_version = version_split[0] + '.' + version_split[1] -gir = 'Astal-' + api_version + '.gir' -typelib = 'Astal-' + api_version + '.typelib' - -vapi_dir = meson.current_source_dir() / 'vapi' -add_project_arguments(['--vapidir', vapi_dir], language: 'vala') - -config = configure_file( - input: 'config.vala.in', - output: 'config.vala', - configuration: { - 'VERSION': meson.project_version(), - 'MAJOR_VERSION': version_split[0], - 'MINOR_VERSION': version_split[1], - 'MICRO_VERSION': version_split[2], - }, -) - -pkgconfig_deps = [ - dependency('glib-2.0'), - dependency('gio-unix-2.0'), - dependency('gobject-2.0'), - dependency('gio-2.0'), - dependency('gtk+-3.0'), - dependency('gdk-pixbuf-2.0'), - dependency('gtk-layer-shell-0'), - dependency('wayland-client'), -] - -deps = pkgconfig_deps + meson.get_compiler('c').find_library('m') - - -wayland_protos = dependency('wayland-protocols') -wayland_scanner = find_program('wayland-scanner') - -wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir') - -gen_client_header = generator( - wayland_scanner, - output: ['@BASENAME@-client.h'], - arguments: ['-c', 'client-header', '@INPUT@', '@BUILD_DIR@/@BASENAME@-client.h'], -) - -gen_private_code = generator( - wayland_scanner, - output: ['@BASENAME@.c'], - arguments: ['-c', 'private-code', '@INPUT@', '@BUILD_DIR@/@BASENAME@.c'], -) - -protocols = [ - join_paths(wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml') -] - -client_protocol_srcs = [] - -foreach protocol : protocols - client_header = gen_client_header.process(protocol) - code = gen_private_code.process(protocol) - client_protocol_srcs += [client_header, code] -endforeach - -sources = [ - config, - 'widget/box.vala', - 'widget/button.vala', - 'widget/centerbox.vala', - 'widget/circularprogress.vala', - 'widget/eventbox.vala', - 'widget/icon.vala', - 'widget/label.vala', - 'widget/levelbar.vala', - 'widget/overlay.vala', - 'widget/scrollable.vala', - 'widget/slider.vala', - 'widget/stack.vala', - 'widget/widget.vala', - 'widget/window.vala', - 'astal.vala', - 'file.vala', - 'process.vala', - 'time.vala', - 'variable.vala', - 'idle-inhibit.h', - 'idle-inhibit.c', -] + client_protocol_srcs - -if get_option('lib') - lib = library( - meson.project_name(), - sources, - dependencies: deps, - vala_args: ['--pkg', 'AstalInhibitManager'], - vala_header: meson.project_name() + '.h', - vala_vapi: meson.project_name() + '-' + api_version + '.vapi', - vala_gir: gir, - version: meson.project_version(), - install: true, - install_dir: [true, true, true, true], - ) - - import('pkgconfig').generate( - lib, - name: meson.project_name(), - filebase: meson.project_name() + '-' + api_version, - version: meson.project_version(), - subdirs: meson.project_name(), - requires: pkgconfig_deps, - install_dir: libdir / 'pkgconfig', - ) - - custom_target( - typelib, - command: [ - find_program('g-ir-compiler'), - '--output', '@OUTPUT@', - '--shared-library', libdir / '@PLAINNAME@', - meson.current_build_dir() / gir, - ], - input: lib, - output: typelib, - depends: lib, - install: true, - install_dir: libdir / 'girepository-1.0', - ) -endif - -if get_option('cli') - executable( - meson.project_name(), - ['cli.vala', sources], - vala_args: ['--pkg', 'AstalInhibitManager'], - dependencies: deps, - install: true, - ) -endif diff --git a/core/src/process.vala b/core/src/process.vala deleted file mode 100644 index 073fe93..0000000 --- a/core/src/process.vala +++ /dev/null @@ -1,119 +0,0 @@ -public class Astal.Process : Object { - private void read_stream(DataInputStream stream, bool err) { - stream.read_line_utf8_async.begin(Priority.DEFAULT, null, (_, res) => { - try { - var output = stream.read_line_utf8_async.end(res); - if (output != null) { - if (err) - stdout(output.strip()); - else - stderr(output.strip()); - - read_stream(stream, err); - } - } catch (Error err) { - printerr("%s\n", err.message); - } - }); - } - - private DataInputStream out_stream; - private DataInputStream err_stream; - private DataOutputStream in_stream; - private Subprocess process; - public string[] argv { construct; get; } - - public signal void stdout (string out); - public signal void stderr (string err); - - public void kill() { - process.force_exit(); - } - - public void signal(int signal_num) { - process.send_signal(signal_num); - } - - public void write(string in) throws Error { - in_stream.put_string(in); - } - - public void write_async(string in) { - in_stream.write_all_async.begin( - in.data, - Priority.DEFAULT, null, (_, res) => { - try { - in_stream.write_all_async.end(res, null); - } catch (Error err) { - printerr("%s\n", err.message); - } - } - ); - } - - public Process.subprocessv(string[] cmd) throws Error { - Object(argv: cmd); - process = new Subprocess.newv(cmd, - SubprocessFlags.STDIN_PIPE | - SubprocessFlags.STDERR_PIPE | - SubprocessFlags.STDOUT_PIPE - ); - out_stream = new DataInputStream(process.get_stdout_pipe()); - err_stream = new DataInputStream(process.get_stderr_pipe()); - in_stream = new DataOutputStream(process.get_stdin_pipe()); - read_stream(out_stream, true); - read_stream(err_stream, false); - } - - public static Process subprocess(string cmd) throws Error { - string[] argv; - Shell.parse_argv(cmd, out argv); - return new Process.subprocessv(argv); - } - - public static string execv(string[] cmd) throws Error { - var process = new Subprocess.newv( - cmd, - SubprocessFlags.STDERR_PIPE | - SubprocessFlags.STDOUT_PIPE - ); - - string err_str, out_str; - process.communicate_utf8(null, null, out out_str, out err_str); - var success = process.get_successful(); - process.dispose(); - if (success) - return out_str.strip(); - else - throw new IOError.FAILED(err_str.strip()); - } - - public static string exec(string cmd) throws Error { - string[] argv; - Shell.parse_argv(cmd, out argv); - return Process.execv(argv); - } - - public static async string exec_asyncv(string[] cmd) throws Error { - var process = new Subprocess.newv( - cmd, - SubprocessFlags.STDERR_PIPE | - SubprocessFlags.STDOUT_PIPE - ); - - string err_str, out_str; - yield process.communicate_utf8_async(null, null, out out_str, out err_str); - var success = process.get_successful(); - process.dispose(); - if (success) - return out_str.strip(); - else - throw new IOError.FAILED(err_str.strip()); - } - - public static async string exec_async(string cmd) throws Error { - string[] argv; - Shell.parse_argv(cmd, out argv); - return yield exec_asyncv(argv); - } -} diff --git a/core/src/time.vala b/core/src/time.vala deleted file mode 100644 index 4034c04..0000000 --- a/core/src/time.vala +++ /dev/null @@ -1,73 +0,0 @@ -namespace Astal { -public class Time : Object { - public signal void now (); - public signal void cancelled (); - private Cancellable cancellable; - private uint timeout_id; - private bool fulfilled = false; - - construct { - cancellable = new Cancellable(); - cancellable.cancelled.connect(() => { - if (!fulfilled) { - Source.remove(timeout_id); - cancelled(); - dispose(); - } - }); - } - - private void connect_closure(Closure? closure) { - if (closure == null) - return; - - now.connect(() => { - Value ret = Value(Type.POINTER); // void - closure.invoke(ref ret, {}); - }); - } - - public Time.interval_prio(uint interval, int prio = Priority.DEFAULT, Closure? fn) { - connect_closure(fn); - Idle.add_once(() => now()); - timeout_id = Timeout.add(interval, () => { - now(); - return Source.CONTINUE; - }, prio); - } - - public Time.timeout_prio(uint timeout, int prio = Priority.DEFAULT, Closure? fn) { - connect_closure(fn); - timeout_id = Timeout.add(timeout, () => { - now(); - fulfilled = true; - return Source.REMOVE; - }, prio); - } - - public Time.idle_prio(int prio = Priority.DEFAULT_IDLE, Closure? fn) { - connect_closure(fn); - timeout_id = Idle.add(() => { - now(); - fulfilled = true; - return Source.REMOVE; - }, prio); - } - - public static Time interval(uint interval, Closure? fn) { - return new Time.interval_prio(interval, Priority.DEFAULT, fn); - } - - public static Time timeout(uint timeout, Closure? fn) { - return new Time.timeout_prio(timeout, Priority.DEFAULT, fn); - } - - public static Time idle(Closure? fn) { - return new Time.idle_prio(Priority.DEFAULT_IDLE, fn); - } - - public void cancel() { - cancellable.cancel(); - } -} -} diff --git a/core/src/vapi/AstalInhibitManager.vapi b/core/src/vapi/AstalInhibitManager.vapi deleted file mode 100644 index 6232a3c..0000000 --- a/core/src/vapi/AstalInhibitManager.vapi +++ /dev/null @@ -1,13 +0,0 @@ -[CCode (cprefix = "Astal", gir_namespace = "Astal", lower_case_cprefix = "astal_")] -namespace Astal { - [CCode (cheader_filename = "idle-inhibit.h", type_id = "astal_idle_inhibit_manager_get_type()")] - public class InhibitManager : GLib.Object { - public static unowned InhibitManager? get_default(); - public Inhibitor inhibit (Gtk.Window window); - } - - [CCode (cheader_filename = "idle-inhibit.h", free_function = "zwp_idle_inhibitor_v1_destroy")] - [Compact] - public class Inhibitor { - } -} diff --git a/core/src/variable.vala b/core/src/variable.vala deleted file mode 100644 index c7edb16..0000000 --- a/core/src/variable.vala +++ /dev/null @@ -1,196 +0,0 @@ -namespace Astal { -public class VariableBase : Object { - public signal void changed (); - public signal void dropped (); - public signal void error (string err); - - // lua-lgi crashes when using its emitting mechanism - public void emit_changed() { changed(); } - public void emit_dropped() { dropped(); } - public void emit_error(string err) { this.error(err); } - - ~VariableBase() { - dropped(); - } -} - -public class Variable : VariableBase { - public Value value { owned get; set; } - - private uint poll_id = 0; - private Process? watch_proc; - - private uint poll_interval { get; set; default = 1000; } - private string[] poll_exec { get; set; } - private Closure? poll_transform { get; set; } - private Closure? poll_fn { get; set; } - - private Closure? watch_transform { get; set; } - private string[] watch_exec { get; set; } - - public Variable(Value init) { - Object(value: init); - } - - public Variable poll( - uint interval, - string exec, - Closure? transform - ) throws Error { - string[] argv; - Shell.parse_argv(exec, out argv); - return pollv(interval, argv, transform); - } - - public Variable pollv( - uint interval, - string[] execv, - Closure? transform - ) throws Error { - if (is_polling()) - stop_poll(); - - poll_interval = interval; - poll_exec = execv; - poll_transform = transform; - poll_fn = null; - start_poll(); - return this; - } - - public Variable pollfn( - uint interval, - Closure fn - ) throws Error { - if (is_polling()) - stop_poll(); - - poll_interval = interval; - poll_fn = fn; - poll_exec = null; - start_poll(); - return this; - } - - public Variable watch( - string exec, - Closure? transform - ) throws Error { - string[] argv; - Shell.parse_argv(exec, out argv); - return watchv(argv, transform); - } - - public Variable watchv( - string[] execv, - Closure? transform - ) throws Error { - if (is_watching()) - stop_watch(); - - watch_exec = execv; - watch_transform = transform; - start_watch(); - return this; - } - - construct { - notify["value"].connect(() => changed()); - dropped.connect(() => { - if (is_polling()) - stop_poll(); - - if (is_watching()) - stop_watch(); - }); - } - - private void set_closure(string val, Closure? transform) { - if (transform != null) { - var str = Value(typeof(string)); - str.set_string(val); - - var ret_val = Value(this.value.type()); - transform.invoke(ref ret_val, { str, this.value }); - this.value = ret_val; - } - else { - if (this.value.type() == Type.STRING && this.value.get_string() == val) - return; - - var str = Value(typeof(string)); - str.set_string(val); - this.value = str; - } - } - - private void set_fn() { - var ret_val = Value(this.value.type()); - poll_fn.invoke(ref ret_val, { this.value }); - this.value = ret_val; - } - - public void start_poll() throws Error { - return_if_fail(poll_id == 0); - - if (poll_fn != null) { - set_fn(); - poll_id = Timeout.add(poll_interval, () => { - set_fn(); - return Source.CONTINUE; - }, Priority.DEFAULT); - } - if (poll_exec != null) { - Process.exec_asyncv.begin(poll_exec, (_, res) => { - try { - var str = Process.exec_asyncv.end(res); - set_closure(str, poll_transform); - } catch (Error err) { - this.error(err.message); - } - }); - poll_id = Timeout.add(poll_interval, () => { - Process.exec_asyncv.begin(poll_exec, (_, res) => { - try { - var str = Process.exec_asyncv.end(res); - set_closure(str, poll_transform); - } catch (Error err) { - this.error(err.message); - Source.remove(poll_id); - poll_id = 0; - } - }); - return Source.CONTINUE; - }, Priority.DEFAULT); - } - } - - public void start_watch() throws Error { - return_if_fail(watch_proc == null); - return_if_fail(watch_exec != null); - - watch_proc = new Process.subprocessv(watch_exec); - watch_proc.stdout.connect((str) => set_closure(str, watch_transform)); - watch_proc.stderr.connect((str) => this.error(str)); - } - - public void stop_poll() { - return_if_fail(poll_id != 0); - Source.remove(poll_id); - poll_id = 0; - } - - public void stop_watch() { - return_if_fail(watch_proc != null); - watch_proc.kill(); - watch_proc = null; - } - - public bool is_polling() { return poll_id > 0; } - public bool is_watching() { return watch_proc != null; } - - ~Variable() { - dropped(); - } -} -} diff --git a/core/src/widget/box.vala b/core/src/widget/box.vala deleted file mode 100644 index 943c821..0000000 --- a/core/src/widget/box.vala +++ /dev/null @@ -1,62 +0,0 @@ -namespace Astal { -public class Box : Gtk.Box { - [CCode (notify = false)] - public bool vertical { - get { return orientation == Gtk.Orientation.VERTICAL; } - set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } - } - - public List children { - set { _set_children(value); } - owned get { return get_children(); } - } - - public new Gtk.Widget child { - owned get { return _get_child(); } - set { _set_child(value); } - } - - construct { - notify["orientation"].connect(() => { - notify_property("vertical"); - }); - } - - private void _set_child(Gtk.Widget child) { - var list = new List(); - list.append(child); - _set_children(list); - } - - private Gtk.Widget? _get_child() { - foreach(var child in get_children()) - return child; - - return null; - } - - private void _set_children(List arr) { - foreach(var child in get_children()) { - remove(child); - } - - foreach(var child in arr) - add(child); - } - - public Box(bool vertical, List children) { - this.vertical = vertical; - _set_children(children); - } - - public Box.newh(List children) { - this.vertical = false; - _set_children(children); - } - - public Box.newv(List children) { - this.vertical = true; - _set_children(children); - } -} -} diff --git a/core/src/widget/button.vala b/core/src/widget/button.vala deleted file mode 100644 index ad60da1..0000000 --- a/core/src/widget/button.vala +++ /dev/null @@ -1,101 +0,0 @@ -namespace Astal { -public class Button : Gtk.Button { - public signal void hover (HoverEvent event); - public signal void hover_lost (HoverEvent event); - public signal void click (ClickEvent event); - public signal void click_release (ClickEvent event); - public signal void scroll (ScrollEvent event); - - construct { - add_events(Gdk.EventMask.SCROLL_MASK); - add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); - - enter_notify_event.connect((self, event) => { - hover(HoverEvent(event) { lost = false }); - }); - - leave_notify_event.connect((self, event) => { - hover_lost(HoverEvent(event) { lost = true }); - }); - - button_press_event.connect((event) => { - click(ClickEvent(event) { release = false }); - }); - - button_release_event.connect((event) => { - click_release(ClickEvent(event) { release = true }); - }); - - scroll_event.connect((event) => { - scroll(ScrollEvent(event)); - }); - } -} - -public enum MouseButton { - PRIMARY = 1, - MIDDLE = 2, - SECONDARY = 3, - BACK = 4, - FORWARD = 5, -} - -// these structs are here because gjs converts every event -// into a union Gdk.Event, which cannot be destructured -// and are not as convinent to work with as a struct -public struct ClickEvent { - bool release; - uint time; - double x; - double y; - Gdk.ModifierType modifier; - MouseButton button; - - public ClickEvent(Gdk.EventButton event) { - this.time = event.time; - this.x = event.x; - this.y = event.y; - this.button = (MouseButton)event.button; - this.modifier = event.state; - } -} - -public struct HoverEvent { - bool lost; - uint time; - double x; - double y; - Gdk.ModifierType modifier; - Gdk.CrossingMode mode; - Gdk.NotifyType detail; - - public HoverEvent(Gdk.EventCrossing event) { - this.time = event.time; - this.x = event.x; - this.y = event.y; - this.modifier = event.state; - this.mode = event.mode; - this.detail = event.detail; - } -} - -public struct ScrollEvent { - uint time; - double x; - double y; - Gdk.ModifierType modifier; - Gdk.ScrollDirection direction; - double delta_x; - double delta_y; - - public ScrollEvent(Gdk.EventScroll event) { - this.time = event.time; - this.x = event.x; - this.y = event.y; - this.modifier = event.state; - this.direction = event.direction; - this.delta_x = event.delta_x; - this.delta_y = event.delta_y; - } -} -} diff --git a/core/src/widget/centerbox.vala b/core/src/widget/centerbox.vala deleted file mode 100644 index 0588828..0000000 --- a/core/src/widget/centerbox.vala +++ /dev/null @@ -1,54 +0,0 @@ -namespace Astal { -public class CenterBox : Gtk.Box { - [CCode (notify = false)] - public bool vertical { - get { return orientation == Gtk.Orientation.VERTICAL; } - set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } - } - - construct { - notify["orientation"].connect(() => { - notify_property("vertical"); - }); - } - - static construct { - set_css_name("centerbox"); - } - - private Gtk.Widget _start_widget; - public Gtk.Widget start_widget { - get { return _start_widget; } - set { - if (_start_widget != null) - remove(_start_widget); - - if (value != null) - pack_start(value, true, true, 0); - } - } - - private Gtk.Widget _end_widget; - public Gtk.Widget end_widget { - get { return _end_widget; } - set { - if (_end_widget != null) - remove(_end_widget); - - if (value != null) - pack_end(value, true, true, 0); - } - } - - public Gtk.Widget center_widget { - get { return get_center_widget(); } - set { - if (center_widget != null) - remove(center_widget); - - if (value != null) - set_center_widget(value); - } - } -} -} diff --git a/core/src/widget/circularprogress.vala b/core/src/widget/circularprogress.vala deleted file mode 100644 index 4e1410d..0000000 --- a/core/src/widget/circularprogress.vala +++ /dev/null @@ -1,182 +0,0 @@ -namespace Astal { -public class CircularProgress : Gtk.Bin { - public double start_at { get; set; } - public double end_at { get; set; } - public double value { get; set; } - public bool inverted { get; set; } - public bool rounded { get; set; } - - construct { - notify["start-at"].connect(queue_draw); - notify["end-at"].connect(queue_draw); - notify["value"].connect(queue_draw); - notify["inverted"].connect(queue_draw); - notify["rounded"].connect(queue_draw); - notify["child"].connect(queue_draw); - } - - static construct { - set_css_name("circular-progress"); - } - - public override void get_preferred_height(out int minh, out int nath) { - var val = get_style_context().get_property("min-height", Gtk.StateFlags.NORMAL); - if (val.get_int() <= 0) { - minh = 40; - nath = 40; - } - - minh = val.get_int(); - nath = val.get_int(); - } - - public override void get_preferred_width(out int minw, out int natw) { - var val = get_style_context().get_property("min-width", Gtk.StateFlags.NORMAL); - if (val.get_int() <= 0) { - minw = 40; - natw = 40; - } - - minw = val.get_int(); - natw = val.get_int(); - } - - private double to_radian(double percentage) { - percentage = Math.floor(percentage * 100); - return (percentage / 100) * (2 * Math.PI); - } - - private bool is_full_circle(double start, double end, double epsilon = 1e-10) { - // Ensure that start and end are between 0 and 1 - start = (start % 1 + 1) % 1; - end = (end % 1 + 1) % 1; - - // Check if the difference between start and end is close to 1 - return Math.fabs(start - end) <= epsilon; - } - - private double scale_arc_value(double start, double end, double value) { - // Ensure that start and end are between 0 and 1 - start = (start % 1 + 1) % 1; - end = (end % 1 + 1) % 1; - - // Calculate the length of the arc - var arc_length = end - start; - if (arc_length < 0) - arc_length += 1; // Adjust for circular representation - - // Calculate the position on the arc based on the percentage value - var scaled = arc_length + value; - - // Ensure the position is between 0 and 1 - return (scaled % 1 + 1) % 1; - } - - private double min(double[] arr) { - double min = arr[0]; - foreach(var i in arr) - if (min > i) min = i; - return min; - } - - private double max(double[] arr) { - double max = arr[0]; - foreach(var i in arr) - if (max < i) max = i; - return max; - } - - public override bool draw(Cairo.Context cr) { - Gtk.Allocation allocation; - get_allocation(out allocation); - - var styles = get_style_context(); - var width = allocation.width; - var height = allocation.height; - var thickness = styles.get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); - var margin = styles.get_margin(Gtk.StateFlags.NORMAL); - var fg = styles.get_color(Gtk.StateFlags.NORMAL); - var bg = styles.get_background_color(Gtk.StateFlags.NORMAL); - - var bg_stroke = thickness + min({margin.bottom, margin.top, margin.left, margin.right}); - var fg_stroke = thickness; - var radius = min({width, height}) / 2.0 - max({bg_stroke, fg_stroke}) / 2.0; - var center_x = width / 2; - var center_y = height / 2; - - var start_background = to_radian(start_at); - var end_background = to_radian(end_at); - var ranged_value = value + start_at; - - var is_circle = is_full_circle(this.start_at, this.end_at); - - if (is_circle) { - // Redefine end_draw in radius to create an accurate full circle - end_background = start_background + 2 * Math.PI; - ranged_value = to_radian(value); - } else { - // Range the value for the arc shape - ranged_value = to_radian(scale_arc_value( - start_at, - end_at, - value - )); - } - - double start_progress, end_progress; - - if (inverted) { - start_progress = end_background - ranged_value; - end_progress = end_background; - } else { - start_progress = start_background; - end_progress = start_background + ranged_value; - } - - // Draw background - cr.set_source_rgba(bg.red, bg.green, bg.blue, bg.alpha); - cr.arc(center_x, center_y, radius, start_background, end_background); - cr.set_line_width(bg_stroke); - cr.stroke(); - - // Draw rounded background ends - if (rounded) { - var start_x = center_x + Math.cos(start_background) * radius; - var start_y = center_y + Math.sin(start_background) * radius; - var end_x = center_x + Math.cos(end_background) * radius; - var end_y = center_y + Math.sin(end_background) * radius; - cr.set_line_width(0); - cr.arc(start_x, start_y, bg_stroke / 2, 0, 0 - 0.01); - cr.fill(); - cr.arc(end_x, end_y, bg_stroke / 2, 0, 0 - 0.01); - cr.fill(); - } - - // Draw progress - cr.set_source_rgba(fg.red, fg.green, fg.blue, fg.alpha); - cr.arc(center_x, center_y, radius, start_progress, end_progress); - cr.set_line_width(fg_stroke); - cr.stroke(); - - // Draw rounded progress ends - if (rounded) { - var start_x = center_x + Math.cos(start_progress) * radius; - var start_y = center_y + Math.sin(start_progress) * radius; - var end_x = center_x + Math.cos(end_progress) * radius; - var end_y = center_y + Math.sin(end_progress) * radius; - cr.set_line_width(0); - cr.arc(start_x, start_y, fg_stroke / 2, 0, 0 - 0.01); - cr.fill(); - cr.arc(end_x, end_y, fg_stroke / 2, 0, 0 - 0.01); - cr.fill(); - } - - if (get_child() != null) { - get_child().size_allocate(allocation); - propagate_draw(get_child(), cr); - } - - return true; - } -} -} diff --git a/core/src/widget/eventbox.vala b/core/src/widget/eventbox.vala deleted file mode 100644 index 6b715cc..0000000 --- a/core/src/widget/eventbox.vala +++ /dev/null @@ -1,66 +0,0 @@ -namespace Astal { -public class EventBox : Gtk.EventBox { - public signal void hover (HoverEvent event); - public signal void hover_lost (HoverEvent event); - public signal void click (ClickEvent event); - public signal void click_release (ClickEvent event); - public signal void scroll (ScrollEvent event); - public signal void motion (MotionEvent event); - - static construct { - set_css_name("eventbox"); - } - - construct { - add_events(Gdk.EventMask.SCROLL_MASK); - add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); - add_events(Gdk.EventMask.POINTER_MOTION_MASK); - - enter_notify_event.connect((self, event) => { - if (event.window == self.get_window() && - event.detail != Gdk.NotifyType.INFERIOR) { - this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); - hover(HoverEvent(event) { lost = false }); - } - }); - - leave_notify_event.connect((self, event) => { - if (event.window == self.get_window() && - event.detail != Gdk.NotifyType.INFERIOR) { - this.unset_state_flags(Gtk.StateFlags.PRELIGHT); - hover_lost(HoverEvent(event) { lost = true }); - } - }); - - button_press_event.connect((event) => { - click(ClickEvent(event) { release = false }); - }); - - button_release_event.connect((event) => { - click_release(ClickEvent(event) { release = true }); - }); - - scroll_event.connect((event) => { - scroll(ScrollEvent(event)); - }); - - motion_notify_event.connect((event) => { - motion(MotionEvent(event)); - }); - } -} - -public struct MotionEvent { - uint time; - double x; - double y; - Gdk.ModifierType modifier; - - public MotionEvent(Gdk.EventMotion event) { - this.time = event.time; - this.x = event.x; - this.y = event.y; - this.modifier = event.state; - } -} -} diff --git a/core/src/widget/icon.vala b/core/src/widget/icon.vala deleted file mode 100644 index 4f1c7f1..0000000 --- a/core/src/widget/icon.vala +++ /dev/null @@ -1,107 +0,0 @@ -namespace Astal { -public Gtk.IconInfo? lookup_icon(string icon) { - var theme = Gtk.IconTheme.get_default(); - return theme.lookup_icon(icon, 16, Gtk.IconLookupFlags.USE_BUILTIN); -} - -public class Icon : Gtk.Image { - private IconType type = IconType.NAMED; - private double size { get; set; default = 14; } - - public new Gdk.Pixbuf pixbuf { get; set; } - public string icon { get; set; default = ""; } - public GLib.Icon g_icon {get; set;} - - private async void display_icon() { - switch(type) { - case IconType.NAMED: - icon_name = icon; - pixel_size = (int)size; - break; - case IconType.FILE: - try { - var file = File.new_for_path(icon); - var stream = yield file.read_async(); - var pb = yield new Gdk.Pixbuf.from_stream_at_scale_async( - stream, - (int)size * scale_factor, - (int)size * scale_factor, - true, - null - ); - var cs = Gdk.cairo_surface_create_from_pixbuf(pb, 0, this.get_window()); - set_from_surface(cs); - } catch (Error err) { - printerr(err.message); - } - break; - case IconType.PIXBUF: - var pb_scaled = pixbuf.scale_simple( - (int)size * scale_factor, - (int)size * scale_factor, - Gdk.InterpType.BILINEAR - ); - if (pb_scaled != null) { - var cs = Gdk.cairo_surface_create_from_pixbuf(pb_scaled, 0, this.get_window()); - set_from_surface(cs); - } - break; - case IconType.GICON: - pixel_size = (int)size; - gicon = g_icon; - break; - - } - } - - static construct { - set_css_name("icon"); - } - - construct { - notify["icon"].connect(() => { - if(FileUtils.test(icon, GLib.FileTest.EXISTS)) - type = IconType.FILE; - else if (lookup_icon(icon) != null) - type = IconType.NAMED; - else { - type = IconType.NAMED; - warning("cannot assign %s as icon, "+ - "it is not a file nor a named icon", icon); - } - display_icon.begin(); - }); - - notify["pixbuf"].connect(() => { - type = IconType.PIXBUF; - display_icon.begin(); - }); - - notify["g-icon"].connect(() => { - type = IconType.GICON; - display_icon.begin(); - }); - - size_allocate.connect(() => { - size = get_style_context() - .get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); - - display_icon.begin(); - }); - - get_style_context().changed.connect(() => { - size = get_style_context() - .get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); - - display_icon.begin(); - }); - } -} - -private enum IconType { - NAMED, - FILE, - PIXBUF, - GICON, -} -} diff --git a/core/src/widget/label.vala b/core/src/widget/label.vala deleted file mode 100644 index 4063b6f..0000000 --- a/core/src/widget/label.vala +++ /dev/null @@ -1,18 +0,0 @@ -using Pango; - -public class Astal.Label : Gtk.Label { - public bool truncate { - set { ellipsize = value ? EllipsizeMode.END : EllipsizeMode.NONE; } - get { return ellipsize == EllipsizeMode.END; } - } - - public new bool justify_fill { - set { justify = value ? Gtk.Justification.FILL : Gtk.Justification.LEFT; } - get { return justify == Gtk.Justification.FILL; } - } - - construct { - notify["ellipsize"].connect(() => notify_property("truncate")); - notify["justify"].connect(() => notify_property("justify_fill")); - } -} diff --git a/core/src/widget/levelbar.vala b/core/src/widget/levelbar.vala deleted file mode 100644 index 1db2cc7..0000000 --- a/core/src/widget/levelbar.vala +++ /dev/null @@ -1,15 +0,0 @@ -namespace Astal { -public class LevelBar : Gtk.LevelBar { - [CCode (notify = false)] - public bool vertical { - get { return orientation == Gtk.Orientation.VERTICAL; } - set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } - } - - construct { - notify["orientation"].connect(() => { - notify_property("vertical"); - }); - } -} -} diff --git a/core/src/widget/overlay.vala b/core/src/widget/overlay.vala deleted file mode 100644 index 207aaa7..0000000 --- a/core/src/widget/overlay.vala +++ /dev/null @@ -1,59 +0,0 @@ -namespace Astal { -public class Overlay : Gtk.Overlay { - public bool pass_through { get; set; } - - public Gtk.Widget? overlay { - get { return overlays.nth_data(0); } - set { - foreach (var ch in get_children()) { - if (ch != child) - remove(ch); - } - - if (value != null) - add_overlay(value); - } - } - - public List overlays { - owned get { return get_children(); } - set { - foreach (var ch in get_children()) { - if (ch != child) - remove(ch); - } - - foreach (var ch in value) - add_overlay(ch); - } - } - - public new Gtk.Widget? child { - get { return get_child(); } - set { - var ch = get_child(); - if (ch != null) - remove(ch); - - if (value != null) - add(value); - } - } - - construct { - notify["pass-through"].connect(() => { - update_pass_through(); - }); - } - - private void update_pass_through() { - foreach (var child in get_children()) - set_overlay_pass_through(child, pass_through); - } - - public new void add_overlay(Gtk.Widget widget) { - base.add_overlay(widget); - set_overlay_pass_through(widget, pass_through); - } -} -} diff --git a/core/src/widget/scrollable.vala b/core/src/widget/scrollable.vala deleted file mode 100644 index 1a0e081..0000000 --- a/core/src/widget/scrollable.vala +++ /dev/null @@ -1,42 +0,0 @@ -namespace Astal { -public class Scrollable : Gtk.ScrolledWindow { - private Gtk.PolicyType _hscroll = Gtk.PolicyType.AUTOMATIC; - private Gtk.PolicyType _vscroll = Gtk.PolicyType.AUTOMATIC; - - public Gtk.PolicyType hscroll { - get { return _hscroll; } - set { - _hscroll = value; - set_policy(value, vscroll); - } - } - - public Gtk.PolicyType vscroll { - get { return _vscroll; } - set { - _vscroll = value; - set_policy(hscroll, value); - } - } - - static construct { - set_css_name("scrollable"); - } - - construct { - if (hadjustment != null) - hadjustment = new Gtk.Adjustment(0,0,0,0,0,0); - - if (vadjustment != null) - vadjustment = new Gtk.Adjustment(0,0,0,0,0,0); - } - - public new Gtk.Widget get_child() { - var ch = base.get_child(); - if (ch is Gtk.Viewport) { - return ch.get_child(); - } - return ch; - } -} -} diff --git a/core/src/widget/slider.vala b/core/src/widget/slider.vala deleted file mode 100644 index 16e6bd2..0000000 --- a/core/src/widget/slider.vala +++ /dev/null @@ -1,73 +0,0 @@ -namespace Astal { -public class Slider : Gtk.Scale { - [CCode (notify = false)] - public bool vertical { - get { return orientation == Gtk.Orientation.VERTICAL; } - set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } - } - - // emitted when the user drags the slider - public signal void dragged (); - - construct { - draw_value = false; - - if (adjustment == null) - adjustment = new Gtk.Adjustment(0,0,0,0,0,0); - - if (max == 0 && min == 0) { - max = 1; - } - - if (step == 0) { - step = 0.05; - } - - notify["orientation"].connect(() => { - notify_property("vertical"); - }); - - button_press_event.connect(() => { dragging = true; }); - key_press_event.connect(() => { dragging = true; }); - button_release_event.connect(() => { dragging = false; }); - key_release_event.connect(() => { dragging = false; }); - scroll_event.connect((event) => { - dragging = true; - if (event.delta_y > 0) - value -= step; - else - value += step; - dragging = false; - }); - - value_changed.connect(() => { - if (dragging) - dragged(); - }); - } - - public bool dragging { get; private set; } - - public double value { - get { return adjustment.value; } - set { if (!dragging) adjustment.value = value; } - } - - public double min { - get { return adjustment.lower; } - set { adjustment.lower = value; } - } - - public double max { - get { return adjustment.upper; } - set { adjustment.upper = value; } - } - - public double step { - get { return adjustment.step_increment; } - set { adjustment.step_increment = value; } - } - - // TODO: marks -} -} diff --git a/core/src/widget/stack.vala b/core/src/widget/stack.vala deleted file mode 100644 index 02f9959..0000000 --- a/core/src/widget/stack.vala +++ /dev/null @@ -1,26 +0,0 @@ -public class Astal.Stack : Gtk.Stack { - public string shown { - get { return visible_child_name; } - set { visible_child_name = value; } - } - - public List children { - set { _set_children(value); } - owned get { return get_children(); } - } - - private void _set_children(List arr) { - foreach(var child in get_children()) { - remove(child); - } - - var i = 0; - foreach(var child in arr) { - if (child.name != null) { - add_named(child, child.name); - } else { - add_named(child, (++i).to_string()); - } - } - } -} diff --git a/core/src/widget/widget.vala b/core/src/widget/widget.vala deleted file mode 100644 index 2506bc8..0000000 --- a/core/src/widget/widget.vala +++ /dev/null @@ -1,157 +0,0 @@ -namespace Astal { -private class Css { - private static HashTable _providers; - public static HashTable providers { - get { - if (_providers == null) { - _providers = new HashTable( - (w) => (uint)w, - (a, b) => a == b); - } - - return _providers; - } - } -} - -private void remove_provider(Gtk.Widget widget) { - var providers = Css.providers; - - if (providers.contains(widget)) { - var p = providers.get(widget); - widget.get_style_context().remove_provider(p); - providers.remove(widget); - p.dispose(); - } -} - -public void widget_set_css(Gtk.Widget widget, string css) { - var providers = Css.providers; - - if (providers.contains(widget)) { - remove_provider(widget); - } else { - widget.destroy.connect(() => { - remove_provider(widget); - }); - } - - var style = !css.contains("{") || !css.contains("}") - ? "* { ".concat(css, "}") : css; - - var p = new Gtk.CssProvider(); - widget.get_style_context() - .add_provider(p, Gtk.STYLE_PROVIDER_PRIORITY_USER); - - try { - p.load_from_data(style, style.length); - providers.set(widget, p); - } catch (Error err) { - warning(err.message); - } -} - -public string widget_get_css(Gtk.Widget widget) { - var providers = Css.providers; - - if (providers.contains(widget)) - return providers.get(widget).to_string(); - - return ""; -} - -public void widget_set_class_names(Gtk.Widget widget, string[] class_names) { - foreach (var name in widget_get_class_names(widget)) - widget_toggle_class_name(widget, name, false); - - foreach (var name in class_names) - widget_toggle_class_name(widget, name, true); -} - -public List widget_get_class_names(Gtk.Widget widget) { - return widget.get_style_context().list_classes(); -} - -public void widget_toggle_class_name( - Gtk.Widget widget, - string class_name, - bool condition = true -) { - var c = widget.get_style_context(); - if (condition) - c.add_class(class_name); - else - c.remove_class(class_name); -} - -private class Cursor { - private static HashTable _cursors; - public static HashTable cursors { - get { - if (_cursors == null) { - _cursors = new HashTable( - (w) => (uint)w, - (a, b) => a == b); - } - return _cursors; - } - } -} - -private void widget_setup_cursor(Gtk.Widget widget) { - widget.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK); - widget.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); - widget.enter_notify_event.connect(() => { - widget.get_window().set_cursor( - new Gdk.Cursor.from_name( - Gdk.Display.get_default(), - Cursor.cursors.get(widget))); - return false; - }); - widget.leave_notify_event.connect(() => { - widget.get_window().set_cursor( - new Gdk.Cursor.from_name( - Gdk.Display.get_default(), - "default")); - return false; - }); - widget.destroy.connect(() => { - if (Cursor.cursors.contains(widget)) - Cursor.cursors.remove(widget); - }); -} - -public void widget_set_cursor(Gtk.Widget widget, string cursor) { - if (!Cursor.cursors.contains(widget)) - widget_setup_cursor(widget); - - Cursor.cursors.set(widget, cursor); -} - -public string widget_get_cursor(Gtk.Widget widget) { - return Cursor.cursors.get(widget); -} - -private class ClickThrough { - private static HashTable _click_through; - public static HashTable click_through { - get { - if (_click_through == null) { - _click_through = new HashTable( - (w) => (uint)w, - (a, b) => a == b); - } - return _click_through; - } - } -} - -public void widget_set_click_through(Gtk.Widget widget, bool click_through) { - ClickThrough.click_through.set(widget, click_through); - widget.input_shape_combine_region(click_through ? new Cairo.Region() : null); -} - -public bool widget_get_click_through(Gtk.Widget widget) { - return ClickThrough.click_through.get(widget); -} -} diff --git a/core/src/widget/window.vala b/core/src/widget/window.vala deleted file mode 100644 index f1a377f..0000000 --- a/core/src/widget/window.vala +++ /dev/null @@ -1,255 +0,0 @@ -using GtkLayerShell; - -namespace Astal { -public enum WindowAnchor { - NONE = 0, - TOP = 1, - RIGHT = 2, - LEFT = 4, - BOTTOM = 8, -} - -public enum Exclusivity { - NORMAL, - EXCLUSIVE, - IGNORE, -} - -public enum Layer { - BACKGROUND = 0, // GtkLayerShell.Layer.BACKGROUND - BOTTOM = 1, // GtkLayerShell.Layer.BOTTOM - TOP = 2, // GtkLayerShell.Layer.TOP - OVERLAY = 3, // GtkLayerShell.Layer.OVERLAY -} - -public enum Keymode { - NONE = 0, // GtkLayerShell.KeyboardMode.NONE - ON_DEMAND = 1, // GtkLayerShell.KeyboardMode.ON_DEMAND - EXCLUSIVE = 2, // GtkLayerShell.KeyboardMode.EXCLUSIVE -} - -public class Window : Gtk.Window { - private static bool check(string action) { - if (!is_supported()) { - critical(@"can not $action on window: layer shell not supported"); - print("tip: running from an xwayland terminal can cause this, for example VsCode"); - return true; - } - return false; - } - - private InhibitManager? inhibit_manager; - private Inhibitor? inhibitor; - - construct { - if (check("initialize layer shell")) - return; - - height_request = 1; - width_request = 1; - init_for_window(this); - inhibit_manager = InhibitManager.get_default(); - } - - public bool inhibit { - set { - if (inhibit_manager == null) { - return; - } - if (value && inhibitor == null) { - inhibitor = inhibit_manager.inhibit(this); - } - else if (!value && inhibitor != null) { - inhibitor = null; - } - } - get { - return inhibitor != null; - } - } - - public override void show() { - base.show(); - if(inhibit) { - inhibitor = inhibit_manager.inhibit(this); - } - } - - public string namespace { - get { return get_namespace(this); } - set { set_namespace(this, value); } - } - - public int anchor { - set { - if (check("set anchor")) - return; - - set_anchor(this, Edge.TOP, WindowAnchor.TOP in value); - set_anchor(this, Edge.BOTTOM, WindowAnchor.BOTTOM in value); - set_anchor(this, Edge.LEFT, WindowAnchor.LEFT in value); - set_anchor(this, Edge.RIGHT, WindowAnchor.RIGHT in value); - } - get { - var a = WindowAnchor.NONE; - if (get_anchor(this, Edge.TOP)) - a = a | WindowAnchor.TOP; - - if (get_anchor(this, Edge.RIGHT)) - a = a | WindowAnchor.RIGHT; - - if (get_anchor(this, Edge.LEFT)) - a = a | WindowAnchor.LEFT; - - if (get_anchor(this, Edge.BOTTOM)) - a = a | WindowAnchor.BOTTOM; - - return a; - } - } - - public Exclusivity exclusivity { - set { - if (check("set exclusivity")) - return; - - switch (value) { - case Exclusivity.NORMAL: - set_exclusive_zone(this, 0); - break; - case Exclusivity.EXCLUSIVE: - auto_exclusive_zone_enable(this); - break; - case Exclusivity.IGNORE: - set_exclusive_zone(this, -1); - break; - } - } - get { - if (auto_exclusive_zone_is_enabled(this)) - return Exclusivity.EXCLUSIVE; - - if (get_exclusive_zone(this) == -1) - return Exclusivity.IGNORE; - - return Exclusivity.NORMAL; - } - } - - public Layer layer { - get { return (Layer)get_layer(this); } - set { - if (check("set layer")) - return; - - set_layer(this, (GtkLayerShell.Layer)value); - } - } - - public Keymode keymode { - get { return (Keymode)get_keyboard_mode(this); } - set { - if (check("set keymode")) - return; - - set_keyboard_mode(this, (GtkLayerShell.KeyboardMode)value); - } - } - - public Gdk.Monitor gdkmonitor { - get { return get_monitor(this); } - set { - if (check("set gdkmonitor")) - return; - - set_monitor (this, value); - } - } - - public new int margin_top { - get { return GtkLayerShell.get_margin(this, Edge.TOP); } - set { - if (check("set margin_top")) - return; - - GtkLayerShell.set_margin(this, Edge.TOP, value); - } - } - - public new int margin_bottom { - get { return GtkLayerShell.get_margin(this, Edge.BOTTOM); } - set { - if (check("set margin_bottom")) - return; - - GtkLayerShell.set_margin(this, Edge.BOTTOM, value); - } - } - - public new int margin_left { - get { return GtkLayerShell.get_margin(this, Edge.LEFT); } - set { - if (check("set margin_left")) - return; - - GtkLayerShell.set_margin(this, Edge.LEFT, value); - } - } - - public new int margin_right { - get { return GtkLayerShell.get_margin(this, Edge.RIGHT); } - set { - if (check("set margin_right")) - return; - - GtkLayerShell.set_margin(this, Edge.RIGHT, value); - } - } - - public new int margin { - set { - if (check("set margin")) - return; - - margin_top = value; - margin_right = value; - margin_bottom = value; - margin_left = value; - } - } - - /** - * CAUTION: the id might not be the same mapped by the compositor - * to reset and let the compositor map it pass a negative number - */ - public int monitor { - set { - if (check("set monitor")) - return; - - if (value < 0) - set_monitor(this, (Gdk.Monitor)null); - - var m = Gdk.Display.get_default().get_monitor(value); - set_monitor(this, m); - } - get { - var m = get_monitor(this); - var d = Gdk.Display.get_default(); - for (var i = 0; i < d.get_n_monitors(); ++i) { - if (m == d.get_monitor(i)) - return i; - } - - return -1; - } - } -} - -/** - * CAUTION: the id might not be the same mapped by the compositor - */ -public uint get_num_monitors() { - return Gdk.Display.get_default().get_n_monitors(); -} -} diff --git a/core/version b/core/version deleted file mode 100644 index 6e8bf73..0000000 --- a/core/version +++ /dev/null @@ -1 +0,0 @@ -0.1.0 diff --git a/flake.nix b/flake.nix index 8b8f739..e5c9018 100644 --- a/flake.nix +++ b/flake.nix @@ -43,9 +43,10 @@ packages.${system} = with pkgs; { docs = import ./docs {inherit self pkgs;}; - default = self.packages.${system}.astal; + default = self.packages.${system}.io; - astal = mkPkg "astal" ./core [gtk3 gtk-layer-shell]; + io = mkPkg "astal" ./lib/astal/io []; + astal3 = mkPkg "astal" ./lib/astal/gtk3 [self.packages.${system}.io gtk3 gtk-layer-shell]; apps = mkPkg "astal-apps" ./lib/apps [json-glib]; auth = mkPkg "astal-auth" ./lib/auth [pam]; battery = mkPkg "astal-battery" ./lib/battery [json-glib]; diff --git a/lang/gjs/.gitignore b/lang/gjs/.gitignore new file mode 100644 index 0000000..53f4bc3 --- /dev/null +++ b/lang/gjs/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +result/ +dist/ +@girs/ diff --git a/lang/gjs/eslint.config.mjs b/lang/gjs/eslint.config.mjs new file mode 100644 index 0000000..05e49ee --- /dev/null +++ b/lang/gjs/eslint.config.mjs @@ -0,0 +1,19 @@ +import eslint from "@eslint/js" +import tseslint from "typescript-eslint" +import stylistic from "@stylistic/eslint-plugin" + +export default tseslint.config({ + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + stylistic.configs.customize({ + semi: false, + indent: 4, + quotes: "double", + }), + ], + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@stylistic/new-parens": "off", + }, +}) diff --git a/lang/gjs/gtk3/app.ts b/lang/gjs/gtk3/app.ts new file mode 100644 index 0000000..1191dc4 --- /dev/null +++ b/lang/gjs/gtk3/app.ts @@ -0,0 +1,105 @@ +import IO from "gi://AstalIO" +import GObject from "gi://GObject" +import Astal from "gi://Astal?version=3.0" +import Gio from "gi://Gio?version=2.0" +import Gtk from "gi://Gtk?version=3.0" + +Gtk.init(null) + +type RequestHandler = { + (request: string, res: (response: any) => void): void +} + +type Config = Partial<{ + icons: string + instanceName: string + gtkTheme: string + iconTheme: string + cursorTheme: string + css: string + requestHandler: RequestHandler + main(...args: string[]): void + client(message: (msg: string) => string, ...args: string[]): void + hold: boolean +}> + +import { setConsoleLogDomain } from "console" +import { exit, programArgs } from "system" + +export default new (class AstalJS extends Astal.Application { + static { GObject.registerClass({ GTypeName: "AstalJS" }, this) } + + eval(body: string): Promise { + return new Promise((res, rej) => { + try { + const fn = Function(`return (async function() { + ${body.includes(";") ? body : `return ${body};`} + })`) + fn()() + .then(res) + .catch(rej) + } + catch (error) { + rej(error) + } + }) + } + + requestHandler?: RequestHandler + + vfunc_request(msg: string, conn: Gio.SocketConnection): void { + if (typeof this.requestHandler === "function") { + this.requestHandler(msg, (response) => { + IO.write_sock(conn, String(response), (_, res) => + IO.write_sock_finish(res), + ) + }) + } + else { + super.vfunc_request(msg, conn) + } + } + + apply_css(style: string, reset = false) { + super.apply_css(style, reset) + } + + quit(code?: number): void { + super.quit() + exit(code ?? 0) + } + + start({ requestHandler, css, hold, main, client, icons, ...cfg }: Config = {}) { + client ??= () => { + print(`Astal instance "${this.instanceName}" already running`) + exit(1) + } + + Object.assign(this, cfg) + setConsoleLogDomain(this.instanceName) + + this.requestHandler = requestHandler + this.connect("activate", () => { + main?.(...programArgs) + }) + + try { + this.acquire_socket() + } + catch (error) { + return client(msg => IO.send_message(this.instanceName, msg)!, ...programArgs) + } + + if (css) + this.apply_css(css, false) + + if (icons) + this.add_icons(icons) + + hold ??= true + if (hold) + this.hold() + + this.runAsync([]) + } +}) diff --git a/lang/gjs/gtk3/astalify.ts b/lang/gjs/gtk3/astalify.ts new file mode 100644 index 0000000..d31046c --- /dev/null +++ b/lang/gjs/gtk3/astalify.ts @@ -0,0 +1,325 @@ +import Astal from "gi://Astal?version=3.0" +import Gtk from "gi://Gtk?version=3.0" +import Gdk from "gi://Gdk?version=3.0" +import GObject from "gi://GObject" +import { execAsync } from "../lib/process.js" +import Variable from "../lib/variable.js" +import Binding, { kebabify, snakeify, type Connectable, type Subscribable } from "../lib/binding.js" + +export function mergeBindings(array: any[]) { + function getValues(...args: any[]) { + let i = 0 + return array.map(value => value instanceof Binding + ? args[i++] + : value, + ) + } + + const bindings = array.filter(i => i instanceof Binding) + + if (bindings.length === 0) + return array + + if (bindings.length === 1) + return bindings[0].as(getValues) + + return Variable.derive(bindings, getValues)() +} + +function setProp(obj: any, prop: string, value: any) { + try { + // the setter method has to be used because + // array like properties are not bound correctly as props + const setter = `set_${snakeify(prop)}` + if (typeof obj[setter] === "function") + return obj[setter](value) + + return (obj[prop] = value) + } + catch (error) { + console.error(`could not set property "${prop}" on ${obj}:`, error) + } +} + +export default function astalify< + C extends { new(...args: any[]): Gtk.Widget }, +>(cls: C) { + class Widget extends cls { + get css(): string { return Astal.widget_get_css(this) } + set css(css: string) { Astal.widget_set_css(this, css) } + get_css(): string { return this.css } + set_css(css: string) { this.css = css } + + get className(): string { return Astal.widget_get_class_names(this).join(" ") } + set className(className: string) { Astal.widget_set_class_names(this, className.split(/\s+/)) } + get_class_name(): string { return this.className } + set_class_name(className: string) { this.className = className } + + get cursor(): Cursor { return Astal.widget_get_cursor(this) as Cursor } + set cursor(cursor: Cursor) { Astal.widget_set_cursor(this, cursor) } + get_cursor(): Cursor { return this.cursor } + set_cursor(cursor: Cursor) { this.cursor = cursor } + + get clickThrough(): boolean { return Astal.widget_get_click_through(this) } + set clickThrough(clickThrough: boolean) { Astal.widget_set_click_through(this, clickThrough) } + get_click_through(): boolean { return this.clickThrough } + set_click_through(clickThrough: boolean) { this.clickThrough = clickThrough } + + declare __no_implicit_destroy: boolean + get noImplicitDestroy(): boolean { return this.__no_implicit_destroy } + set noImplicitDestroy(value: boolean) { this.__no_implicit_destroy = value } + + _setChildren(children: Gtk.Widget[]) { + children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget + ? ch + : new Gtk.Label({ visible: true, label: String(ch) })) + + // remove + if (this instanceof Gtk.Bin) { + const ch = this.get_child() + if (ch) + this.remove(ch) + if (ch && !children.includes(ch) && !this.noImplicitDestroy) + ch?.destroy() + } + else if (this instanceof Gtk.Container) { + for (const ch of this.get_children()) { + this.remove(ch) + if (!children.includes(ch) && !this.noImplicitDestroy) + ch?.destroy() + } + } + + // TODO: add more container types + if (this instanceof Astal.Box) { + this.set_children(children) + } + + else if (this instanceof Astal.Stack) { + this.set_children(children) + } + + else if (this instanceof Astal.CenterBox) { + this.startWidget = children[0] + this.centerWidget = children[1] + this.endWidget = children[2] + } + + else if (this instanceof Astal.Overlay) { + const [child, ...overlays] = children + this.set_child(child) + this.set_overlays(overlays) + } + + else if (this instanceof Gtk.Container) { + for (const ch of children) + this.add(ch) + } + } + + toggleClassName(cn: string, cond = true) { + Astal.widget_toggle_class_name(this, cn, cond) + } + + hook( + object: Connectable, + signal: string, + callback: (self: this, ...args: any[]) => void, + ): this + hook( + object: Subscribable, + callback: (self: this, ...args: any[]) => void, + ): this + hook( + object: Connectable | Subscribable, + signalOrCallback: string | ((self: this, ...args: any[]) => void), + callback?: (self: this, ...args: any[]) => void, + ) { + if (typeof object.connect === "function" && callback) { + const id = object.connect(signalOrCallback, (_: any, ...args: unknown[]) => { + callback(this, ...args) + }) + this.connect("destroy", () => { + (object.disconnect as Connectable["disconnect"])(id) + }) + } + + else if (typeof object.subscribe === "function" && typeof signalOrCallback === "function") { + const unsub = object.subscribe((...args: unknown[]) => { + signalOrCallback(this, ...args) + }) + this.connect("destroy", unsub) + } + + return this + } + + constructor(...params: any[]) { + super() + const [config] = params + + const { setup, child, children = [], ...props } = config + props.visible ??= true + + if (child) + children.unshift(child) + + // collect bindings + const bindings = Object.keys(props).reduce((acc: any, prop) => { + if (props[prop] instanceof Binding) { + const binding = props[prop] + delete props[prop] + return [...acc, [prop, binding]] + } + return acc + }, []) + + // collect signal handlers + const onHandlers = Object.keys(props).reduce((acc: any, key) => { + if (key.startsWith("on")) { + const sig = kebabify(key).split("-").slice(1).join("-") + const handler = props[key] + delete props[key] + return [...acc, [sig, handler]] + } + return acc + }, []) + + // set children + const mergedChildren = mergeBindings(children.flat(Infinity)) + if (mergedChildren instanceof Binding) { + this._setChildren(mergedChildren.get()) + this.connect("destroy", mergedChildren.subscribe((v) => { + this._setChildren(v) + })) + } + else { + if (mergedChildren.length > 0) { + this._setChildren(mergedChildren) + } + } + + // setup signal handlers + for (const [signal, callback] of onHandlers) { + if (typeof callback === "function") { + this.connect(signal, callback) + } + else { + this.connect(signal, () => execAsync(callback) + .then(print).catch(console.error)) + } + } + + // setup bindings handlers + for (const [prop, binding] of bindings) { + if (prop === "child" || prop === "children") { + this.connect("destroy", binding.subscribe((v: any) => { + this._setChildren(v) + })) + } + this.connect("destroy", binding.subscribe((v: any) => { + setProp(this, prop, v) + })) + setProp(this, prop, binding.get()) + } + + Object.assign(this, props) + setup?.(this) + } + } + + GObject.registerClass({ + GTypeName: `Astal_${cls.name}`, + Properties: { + "class-name": GObject.ParamSpec.string( + "class-name", "", "", GObject.ParamFlags.READWRITE, "", + ), + "css": GObject.ParamSpec.string( + "css", "", "", GObject.ParamFlags.READWRITE, "", + ), + "cursor": GObject.ParamSpec.string( + "cursor", "", "", GObject.ParamFlags.READWRITE, "default", + ), + "click-through": GObject.ParamSpec.boolean( + "click-through", "", "", GObject.ParamFlags.READWRITE, false, + ), + "no-implicit-destroy": GObject.ParamSpec.boolean( + "no-implicit-destroy", "", "", GObject.ParamFlags.READWRITE, false, + ), + }, + }, Widget) + + return Widget +} + +type BindableProps = { + [K in keyof T]: Binding | T[K]; +} + +type SigHandler< + W extends InstanceType, + Args extends Array, +> = ((self: W, ...args: Args) => unknown) | string | string[] + +export type ConstructProps< + Self extends InstanceType, + Props extends Gtk.Widget.ConstructorProps, + Signals extends Record<`on${string}`, Array> = Record<`on${string}`, any[]>, +> = Partial<{ + // @ts-expect-error can't assign to unknown, but it works as expected though + [S in keyof Signals]: SigHandler +}> & Partial<{ + [Key in `on${string}`]: SigHandler +}> & BindableProps & { + className?: string + css?: string + cursor?: string + clickThrough?: boolean +}> & { + onDestroy?: (self: Self) => unknown + onDraw?: (self: Self) => unknown + onKeyPressEvent?: (self: Self, event: Gdk.Event) => unknown + onKeyReleaseEvent?: (self: Self, event: Gdk.Event) => unknown + onButtonPressEvent?: (self: Self, event: Gdk.Event) => unknown + onButtonReleaseEvent?: (self: Self, event: Gdk.Event) => unknown + onRealize?: (self: Self) => unknown + setup?: (self: Self) => void +} + +export type BindableChild = Gtk.Widget | Binding + +type Cursor = + | "default" + | "help" + | "pointer" + | "context-menu" + | "progress" + | "wait" + | "cell" + | "crosshair" + | "text" + | "vertical-text" + | "alias" + | "copy" + | "no-drop" + | "move" + | "not-allowed" + | "grab" + | "grabbing" + | "all-scroll" + | "col-resize" + | "row-resize" + | "n-resize" + | "e-resize" + | "s-resize" + | "w-resize" + | "ne-resize" + | "nw-resize" + | "sw-resize" + | "se-resize" + | "ew-resize" + | "ns-resize" + | "nesw-resize" + | "nwse-resize" + | "zoom-in" + | "zoom-out" diff --git a/lang/gjs/gtk3/index.ts b/lang/gjs/gtk3/index.ts new file mode 100644 index 0000000..cfafbda --- /dev/null +++ b/lang/gjs/gtk3/index.ts @@ -0,0 +1,9 @@ +import Astal from "gi://Astal?version=3.0" +import Gtk from "gi://Gtk?version=3.0" +import Gdk from "gi://Gdk?version=3.0" +import astalify, { type ConstructProps } from "./astalify.js" + +export { Astal, Gtk, Gdk } +export { default as App } from "./app.js" +export { astalify, ConstructProps } +export * as Widget from "./widget.js" diff --git a/lang/gjs/gtk3/jsx-runtime.ts b/lang/gjs/gtk3/jsx-runtime.ts new file mode 100644 index 0000000..22dc424 --- /dev/null +++ b/lang/gjs/gtk3/jsx-runtime.ts @@ -0,0 +1,96 @@ +import Gtk from "gi://Gtk?version=3.0" +import { mergeBindings, type BindableChild } from "./astalify.js" +import * as Widget from "./widget.js" + +function isArrowFunction(func: any): func is (args: any) => any { + return !Object.hasOwn(func, "prototype") +} + +export function Fragment({ children = [], child }: { + child?: BindableChild + children?: Array +}) { + return mergeBindings([...children, child]) +} + +export function jsx( + ctor: keyof typeof ctors | typeof Gtk.Widget, + { children, ...props }: any, +) { + children ??= [] + + if (!Array.isArray(children)) + children = [children] + + children = children.filter(Boolean) + + if (children.length === 1) + props.child = children[0] + else if (children.length > 1) + props.children = children + + if (typeof ctor === "string") { + return new ctors[ctor](props) + } + + if (isArrowFunction(ctor)) + return ctor(props) + + // @ts-expect-error can be class or function + return new ctor(props) +} + +const ctors = { + box: Widget.Box, + button: Widget.Button, + centerbox: Widget.CenterBox, + circularprogress: Widget.CircularProgress, + drawingarea: Widget.DrawingArea, + entry: Widget.Entry, + eventbox: Widget.EventBox, + // TODO: fixed + // TODO: flowbox + icon: Widget.Icon, + label: Widget.Label, + levelbar: Widget.LevelBar, + // TODO: listbox + overlay: Widget.Overlay, + revealer: Widget.Revealer, + scrollable: Widget.Scrollable, + slider: Widget.Slider, + stack: Widget.Stack, + switch: Widget.Switch, + window: Widget.Window, +} + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX { + type Element = Gtk.Widget + type ElementClass = Gtk.Widget + interface IntrinsicElements { + box: Widget.BoxProps + button: Widget.ButtonProps + centerbox: Widget.CenterBoxProps + circularprogress: Widget.CircularProgressProps + drawingarea: Widget.DrawingAreaProps + entry: Widget.EntryProps + eventbox: Widget.EventBoxProps + // TODO: fixed + // TODO: flowbox + icon: Widget.IconProps + label: Widget.LabelProps + levelbar: Widget.LevelBarProps + // TODO: listbox + overlay: Widget.OverlayProps + revealer: Widget.RevealerProps + scrollable: Widget.ScrollableProps + slider: Widget.SliderProps + stack: Widget.StackProps + switch: Widget.SwitchProps + window: Widget.WindowProps + } + } +} + +export const jsxs = jsx diff --git a/lang/gjs/gtk3/widget.ts b/lang/gjs/gtk3/widget.ts new file mode 100644 index 0000000..fd70ed6 --- /dev/null +++ b/lang/gjs/gtk3/widget.ts @@ -0,0 +1,154 @@ +/* eslint-disable max-len */ +import Astal from "gi://Astal?version=3.0" +import Gtk from "gi://Gtk?version=3.0" +import GObject from "gi://GObject" +import astalify, { type ConstructProps, type BindableChild } from "./astalify.js" + +// Box +Object.defineProperty(Astal.Box.prototype, "children", { + get() { return this.get_children() }, + set(v) { this.set_children(v) }, +}) + +export type BoxProps = ConstructProps +export class Box extends astalify(Astal.Box) { + static { GObject.registerClass({ GTypeName: "Box" }, this) } + constructor(props?: BoxProps, ...children: Array) { super({ children, ...props } as any) } +} + +// Button +export type ButtonProps = ConstructProps +export class Button extends astalify(Astal.Button) { + static { GObject.registerClass({ GTypeName: "Button" }, this) } + constructor(props?: ButtonProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// CenterBox +export type CenterBoxProps = ConstructProps +export class CenterBox extends astalify(Astal.CenterBox) { + static { GObject.registerClass({ GTypeName: "CenterBox" }, this) } + constructor(props?: CenterBoxProps, ...children: Array) { super({ children, ...props } as any) } +} + +// CircularProgress +export type CircularProgressProps = ConstructProps +export class CircularProgress extends astalify(Astal.CircularProgress) { + static { GObject.registerClass({ GTypeName: "CircularProgress" }, this) } + constructor(props?: CircularProgressProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// DrawingArea +export type DrawingAreaProps = ConstructProps +export class DrawingArea extends astalify(Gtk.DrawingArea) { + static { GObject.registerClass({ GTypeName: "DrawingArea" }, this) } + constructor(props?: DrawingAreaProps) { super(props as any) } +} + +// Entry +export type EntryProps = ConstructProps +export class Entry extends astalify(Gtk.Entry) { + static { GObject.registerClass({ GTypeName: "Entry" }, this) } + constructor(props?: EntryProps) { super(props as any) } +} + +// EventBox +export type EventBoxProps = ConstructProps +export class EventBox extends astalify(Astal.EventBox) { + static { GObject.registerClass({ GTypeName: "EventBox" }, this) } + constructor(props?: EventBoxProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// // TODO: Fixed +// // TODO: FlowBox +// +// Icon +export type IconProps = ConstructProps +export class Icon extends astalify(Astal.Icon) { + static { GObject.registerClass({ GTypeName: "Icon" }, this) } + constructor(props?: IconProps) { super(props as any) } +} + +// Label +export type LabelProps = ConstructProps +export class Label extends astalify(Astal.Label) { + static { GObject.registerClass({ GTypeName: "Label" }, this) } + constructor(props?: LabelProps) { super(props as any) } +} + +// LevelBar +export type LevelBarProps = ConstructProps +export class LevelBar extends astalify(Astal.LevelBar) { + static { GObject.registerClass({ GTypeName: "LevelBar" }, this) } + constructor(props?: LevelBarProps) { super(props as any) } +} + +// TODO: ListBox + +// Overlay +export type OverlayProps = ConstructProps +export class Overlay extends astalify(Astal.Overlay) { + static { GObject.registerClass({ GTypeName: "Overlay" }, this) } + constructor(props?: OverlayProps, ...children: Array) { super({ children, ...props } as any) } +} + +// Revealer +export type RevealerProps = ConstructProps +export class Revealer extends astalify(Gtk.Revealer) { + static { GObject.registerClass({ GTypeName: "Revealer" }, this) } + constructor(props?: RevealerProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// Scrollable +export type ScrollableProps = ConstructProps +export class Scrollable extends astalify(Astal.Scrollable) { + static { GObject.registerClass({ GTypeName: "Scrollable" }, this) } + constructor(props?: ScrollableProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// Slider +export type SliderProps = ConstructProps +export class Slider extends astalify(Astal.Slider) { + static { GObject.registerClass({ GTypeName: "Slider" }, this) } + constructor(props?: SliderProps) { super(props as any) } +} + +// Stack +export type StackProps = ConstructProps +export class Stack extends astalify(Astal.Stack) { + static { GObject.registerClass({ GTypeName: "Stack" }, this) } + constructor(props?: StackProps, ...children: Array) { super({ children, ...props } as any) } +} + +// Switch +export type SwitchProps = ConstructProps +export class Switch extends astalify(Gtk.Switch) { + static { GObject.registerClass({ GTypeName: "Switch" }, this) } + constructor(props?: SwitchProps) { super(props as any) } +} + +// Window +export type WindowProps = ConstructProps +export class Window extends astalify(Astal.Window) { + static { GObject.registerClass({ GTypeName: "Window" }, this) } + constructor(props?: WindowProps, child?: BindableChild) { super({ child, ...props } as any) } +} diff --git a/lang/gjs/gtk4/app.ts b/lang/gjs/gtk4/app.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/gtk4/app.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/gtk4/astalify.ts b/lang/gjs/gtk4/astalify.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/gtk4/astalify.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/gtk4/index.ts b/lang/gjs/gtk4/index.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/gtk4/index.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/gtk4/jsx-runtime.ts b/lang/gjs/gtk4/jsx-runtime.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/gtk4/jsx-runtime.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/index.ts b/lang/gjs/index.ts new file mode 100644 index 0000000..4f52259 --- /dev/null +++ b/lang/gjs/index.ts @@ -0,0 +1,6 @@ +export * from "./lib/process.js" +export * from "./lib/time.js" +export * from "./lib/file.js" +export * from "./lib/gobject.js" +export { bind, default as Binding } from "./lib/binding.js" +export { Variable } from "./lib/variable.js" diff --git a/lang/gjs/lib/binding.ts b/lang/gjs/lib/binding.ts new file mode 100644 index 0000000..95d905f --- /dev/null +++ b/lang/gjs/lib/binding.ts @@ -0,0 +1,89 @@ +export const snakeify = (str: string) => str + .replace(/([a-z])([A-Z])/g, "$1_$2") + .replaceAll("-", "_") + .toLowerCase() + +export const kebabify = (str: string) => str + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replaceAll("_", "-") + .toLowerCase() + +export interface Subscribable { + subscribe(callback: (value: T) => void): () => void + get(): T + [key: string]: any +} + +export interface Connectable { + connect(signal: string, callback: (...args: any[]) => unknown): number + disconnect(id: number): void + [key: string]: any +} + +export default class Binding { + private transformFn = (v: any) => v + + #emitter: Subscribable | Connectable + #prop?: string + + static bind< + T extends Connectable, + P extends keyof T, + >(object: T, property: P): Binding + + static bind(object: Subscribable): Binding + + static bind(emitter: Connectable | Subscribable, prop?: string) { + return new Binding(emitter, prop) + } + + private constructor(emitter: Connectable | Subscribable, prop?: string) { + this.#emitter = emitter + this.#prop = prop && kebabify(prop) + } + + toString() { + return `Binding<${this.#emitter}${this.#prop ? `, "${this.#prop}"` : ""}>` + } + + as(fn: (v: Value) => T): Binding { + const bind = new Binding(this.#emitter, this.#prop) + bind.transformFn = (v: Value) => fn(this.transformFn(v)) + return bind as unknown as Binding + } + + get(): Value { + if (typeof this.#emitter.get === "function") + return this.transformFn(this.#emitter.get()) + + if (typeof this.#prop === "string") { + const getter = `get_${snakeify(this.#prop)}` + if (typeof this.#emitter[getter] === "function") + return this.transformFn(this.#emitter[getter]()) + + return this.transformFn(this.#emitter[this.#prop]) + } + + throw Error("can not get value of binding") + } + + subscribe(callback: (value: Value) => void): () => void { + if (typeof this.#emitter.subscribe === "function") { + return this.#emitter.subscribe(() => { + callback(this.get()) + }) + } + else if (typeof this.#emitter.connect === "function") { + const signal = `notify::${this.#prop}` + const id = this.#emitter.connect(signal, () => { + callback(this.get()) + }) + return () => { + (this.#emitter.disconnect as Connectable["disconnect"])(id) + } + } + throw Error(`${this.#emitter} is not bindable`) + } +} + +export const { bind } = Binding diff --git a/lang/gjs/lib/file.ts b/lang/gjs/lib/file.ts new file mode 100644 index 0000000..7b9de3a --- /dev/null +++ b/lang/gjs/lib/file.ts @@ -0,0 +1,45 @@ +import Astal from "gi://AstalIO" +import Gio from "gi://Gio" + +export function readFile(path: string): string { + return Astal.read_file(path) || "" +} + +export function readFileAsync(path: string): Promise { + return new Promise((resolve, reject) => { + Astal.read_file_async(path, (_, res) => { + try { + resolve(Astal.read_file_finish(res) || "") + } + catch (error) { + reject(error) + } + }) + }) +} + +export function writeFile(path: string, content: string): void { + Astal.write_file(path, content) +} + +export function writeFileAsync(path: string, content: string): Promise { + return new Promise((resolve, reject) => { + Astal.write_file_async(path, content, (_, res) => { + try { + resolve(Astal.write_file_finish(res)) + } + catch (error) { + reject(error) + } + }) + }) +} + +export function monitorFile( + path: string, + callback: (file: string, event: Gio.FileMonitorEvent) => void, +): Gio.FileMonitor { + return Astal.monitor_file(path, (file: string, event: Gio.FileMonitorEvent) => { + callback(file, event) + })! +} diff --git a/lang/gjs/lib/gobject.ts b/lang/gjs/lib/gobject.ts new file mode 100644 index 0000000..4740764 --- /dev/null +++ b/lang/gjs/lib/gobject.ts @@ -0,0 +1,180 @@ +export { default as GObject, default as default } from "gi://GObject" +export { default as Gio } from "gi://Gio" +export { default as GLib } from "gi://GLib" + +import GObject from "gi://GObject" +const meta = Symbol("meta") + +const { ParamSpec, ParamFlags } = GObject + +const kebabify = (str: string) => str + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replaceAll("_", "-") + .toLowerCase() + +type SignalDeclaration = { + flags?: GObject.SignalFlags + accumulator?: GObject.AccumulatorType + return_type?: GObject.GType + param_types?: Array +} + +type PropertyDeclaration = + | InstanceType + | { $gtype: GObject.GType } + | typeof String + | typeof Number + | typeof Boolean + | typeof Object + +type GObjectConstructor = { + [meta]?: { + Properties?: { [key: string]: GObject.ParamSpec } + Signals?: { [key: string]: GObject.SignalDefinition } + } + new(...args: any[]): any +} + +type MetaInfo = GObject.MetaInfo, never> + +export function register(options: MetaInfo = {}) { + return function (cls: GObjectConstructor) { + GObject.registerClass({ + Signals: { ...cls[meta]?.Signals }, + Properties: { ...cls[meta]?.Properties }, + ...options, + }, cls) + } +} + +export function property(declaration: PropertyDeclaration = Object) { + return function (target: any, prop: any, desc?: PropertyDescriptor) { + target.constructor[meta] ??= {} + target.constructor[meta].Properties ??= {} + + const name = kebabify(prop) + + if (!desc) { + let value = defaultValue(declaration) + + Object.defineProperty(target, prop, { + get() { + return value + }, + set(v) { + if (v !== value) { + value = v + this.notify(name) + } + }, + }) + + Object.defineProperty(target, `set_${name.replace("-", "_")}`, { + value: function (v: any) { + this[prop] = v + }, + }) + + Object.defineProperty(target, `get_${name.replace("-", "_")}`, { + value: function () { + return this[prop] + }, + }) + + target.constructor[meta].Properties[kebabify(prop)] = pspec(name, ParamFlags.READWRITE, declaration) + } + + else { + let flags = 0 + if (desc.get) flags |= ParamFlags.READABLE + if (desc.set) flags |= ParamFlags.WRITABLE + + target.constructor[meta].Properties[kebabify(prop)] = pspec(name, flags, declaration) + } + } +} + +export function signal(...params: Array<{ $gtype: GObject.GType } | typeof Object>): +(target: any, signal: any, desc?: PropertyDescriptor) => void + +export function signal(declaration?: SignalDeclaration): +(target: any, signal: any, desc?: PropertyDescriptor) => void + +export function signal( + declaration?: SignalDeclaration | { $gtype: GObject.GType } | typeof Object, + ...params: Array<{ $gtype: GObject.GType } | typeof Object> +) { + return function (target: any, signal: any, desc?: PropertyDescriptor) { + target.constructor[meta] ??= {} + target.constructor[meta].Signals ??= {} + + const name = kebabify(signal) + + if (declaration || params.length > 0) { + // @ts-expect-error TODO: type assert + const arr = [declaration, ...params].map(v => v.$gtype) + target.constructor[meta].Signals[name] = { + param_types: arr, + } + } + else { + target.constructor[meta].Signals[name] = declaration + } + + if (!desc) { + Object.defineProperty(target, signal, { + value: function (...args: any[]) { + this.emit(name, ...args) + }, + }) + } + else { + const og: ((...args: any[]) => void) = desc.value + desc.value = function (...args: any[]) { + // @ts-expect-error not typed + this.emit(name, ...args) + } + Object.defineProperty(target, `on_${name.replace("-", "_")}`, { + value: function (...args: any[]) { + return og(...args) + }, + }) + } + } +} + +function pspec(name: string, flags: number, declaration: PropertyDeclaration) { + if (declaration instanceof ParamSpec) + return declaration + + switch (declaration) { + case String: + return ParamSpec.string(name, "", "", flags, "") + case Number: + return ParamSpec.double(name, "", "", flags, -Number.MAX_VALUE, Number.MAX_VALUE, 0) + case Boolean: + return ParamSpec.boolean(name, "", "", flags, false) + case Object: + return ParamSpec.jsobject(name, "", "", flags) + default: + // @ts-expect-error misstyped + return ParamSpec.object(name, "", "", flags, declaration.$gtype) + } +} + +function defaultValue(declaration: PropertyDeclaration) { + if (declaration instanceof ParamSpec) + return declaration.get_default_value() + + switch (declaration) { + case String: + return "default-string" + case Number: + return 0 + case Boolean: + return false + case Object: + default: + return null + } +} diff --git a/lang/gjs/lib/process.ts b/lang/gjs/lib/process.ts new file mode 100644 index 0000000..2f7816b --- /dev/null +++ b/lang/gjs/lib/process.ts @@ -0,0 +1,68 @@ +import Astal from "gi://AstalIO" + +type Args = { + cmd: string | string[] + out?: (stdout: string) => void + err?: (stderr: string) => void +} + +export function subprocess(args: Args): Astal.Process + +export function subprocess( + cmd: string | string[], + onOut?: (stdout: string) => void, + onErr?: (stderr: string) => void, +): Astal.Process + +export function subprocess( + argsOrCmd: Args | string | string[], + onOut: (stdout: string) => void = print, + onErr: (stderr: string) => void = printerr, +) { + const args = Array.isArray(argsOrCmd) || typeof argsOrCmd === "string" + const { cmd, err, out } = { + cmd: args ? argsOrCmd : argsOrCmd.cmd, + err: args ? onErr : argsOrCmd.err || onErr, + out: args ? onOut : argsOrCmd.out || onOut, + } + + const proc = Array.isArray(cmd) + ? Astal.Process.subprocessv(cmd) + : Astal.Process.subprocess(cmd) + + proc.connect("stdout", (_, stdout: string) => out(stdout)) + proc.connect("stderr", (_, stderr: string) => err(stderr)) + return proc +} + +/** @throws {GLib.Error} Throws stderr */ +export function exec(cmd: string | string[]) { + return Array.isArray(cmd) + ? Astal.Process.execv(cmd) + : Astal.Process.exec(cmd) +} + +export function execAsync(cmd: string | string[]): Promise { + return new Promise((resolve, reject) => { + if (Array.isArray(cmd)) { + Astal.Process.exec_asyncv(cmd, (_, res) => { + try { + resolve(Astal.Process.exec_asyncv_finish(res)) + } + catch (error) { + reject(error) + } + }) + } + else { + Astal.Process.exec_async(cmd, (_, res) => { + try { + resolve(Astal.Process.exec_finish(res)) + } + catch (error) { + reject(error) + } + }) + } + }) +} diff --git a/lang/gjs/lib/time.ts b/lang/gjs/lib/time.ts new file mode 100644 index 0000000..a7e1e61 --- /dev/null +++ b/lang/gjs/lib/time.ts @@ -0,0 +1,13 @@ +import Astal from "gi://AstalIO" + +export function interval(interval: number, callback?: () => void) { + return Astal.Time.interval(interval, () => void callback?.()) +} + +export function timeout(timeout: number, callback?: () => void) { + return Astal.Time.timeout(timeout, () => void callback?.()) +} + +export function idle(callback?: () => void) { + return Astal.Time.idle(() => void callback?.()) +} diff --git a/lang/gjs/lib/variable.ts b/lang/gjs/lib/variable.ts new file mode 100644 index 0000000..9b3d3d2 --- /dev/null +++ b/lang/gjs/lib/variable.ts @@ -0,0 +1,230 @@ +import Astal from "gi://AstalIO" +import Binding, { type Connectable, type Subscribable } from "./binding.js" +import { interval } from "./time.js" +import { execAsync, subprocess } from "./process.js" + +class VariableWrapper extends Function { + private variable!: Astal.VariableBase + private errHandler? = console.error + + private _value: T + private _poll?: Astal.Time + private _watch?: Astal.Process + + private pollInterval = 1000 + private pollExec?: string[] | string + private pollTransform?: (stdout: string, prev: T) => T + private pollFn?: (prev: T) => T | Promise + + private watchTransform?: (stdout: string, prev: T) => T + private watchExec?: string[] | string + + constructor(init: T) { + super() + this._value = init + this.variable = new Astal.VariableBase() + this.variable.connect("dropped", () => { + this.stopWatch() + this.stopPoll() + }) + this.variable.connect("error", (_, err) => this.errHandler?.(err)) + return new Proxy(this, { + apply: (target, _, args) => target._call(args[0]), + }) + } + + private _call(transform?: (value: T) => R): Binding { + const b = Binding.bind(this) + return transform ? b.as(transform) : b as unknown as Binding + } + + toString() { + return String(`Variable<${this.get()}>`) + } + + get(): T { return this._value } + set(value: T) { + if (value !== this._value) { + this._value = value + this.variable.emit("changed") + } + } + + startPoll() { + if (this._poll) + return + + if (this.pollFn) { + this._poll = interval(this.pollInterval, () => { + const v = this.pollFn!(this.get()) + if (v instanceof Promise) { + v.then(v => this.set(v)) + .catch(err => this.variable.emit("error", err)) + } + else { + this.set(v) + } + }) + } + else if (this.pollExec) { + this._poll = interval(this.pollInterval, () => { + execAsync(this.pollExec!) + .then(v => this.set(this.pollTransform!(v, this.get()))) + .catch(err => this.variable.emit("error", err)) + }) + } + } + + startWatch() { + if (this._watch) + return + + this._watch = subprocess({ + cmd: this.watchExec!, + out: out => this.set(this.watchTransform!(out, this.get())), + err: err => this.variable.emit("error", err), + }) + } + + stopPoll() { + this._poll?.cancel() + delete this._poll + } + + stopWatch() { + this._watch?.kill() + delete this._watch + } + + isPolling() { return !!this._poll } + isWatching() { return !!this._watch } + + drop() { + this.variable.emit("dropped") + } + + onDropped(callback: () => void) { + this.variable.connect("dropped", callback) + return this as unknown as Variable + } + + onError(callback: (err: string) => void) { + delete this.errHandler + this.variable.connect("error", (_, err) => callback(err)) + return this as unknown as Variable + } + + subscribe(callback: (value: T) => void) { + const id = this.variable.connect("changed", () => { + callback(this.get()) + }) + return () => this.variable.disconnect(id) + } + + poll( + interval: number, + exec: string | string[], + transform?: (stdout: string, prev: T) => T + ): Variable + + poll( + interval: number, + callback: (prev: T) => T | Promise + ): Variable + + poll( + interval: number, + exec: string | string[] | ((prev: T) => T | Promise), + transform: (stdout: string, prev: T) => T = out => out as T, + ) { + this.stopPoll() + this.pollInterval = interval + this.pollTransform = transform + if (typeof exec === "function") { + this.pollFn = exec + delete this.pollExec + } + else { + this.pollExec = exec + delete this.pollFn + } + this.startPoll() + return this as unknown as Variable + } + + watch( + exec: string | string[], + transform: (stdout: string, prev: T) => T = out => out as T, + ) { + this.stopWatch() + this.watchExec = exec + this.watchTransform = transform + this.startWatch() + return this as unknown as Variable + } + + observe( + objs: Array<[obj: Connectable, signal: string]>, + callback: (...args: any[]) => T, + ): Variable + + observe( + obj: Connectable, + signal: string, + callback: (...args: any[]) => T, + ): Variable + + observe( + objs: Connectable | Array<[obj: Connectable, signal: string]>, + sigOrFn: string | ((obj: Connectable, ...args: any[]) => T), + callback?: (obj: Connectable, ...args: any[]) => T, + ) { + const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => this.get()) + const set = (obj: Connectable, ...args: any[]) => this.set(f(obj, ...args)) + + if (Array.isArray(objs)) { + for (const obj of objs) { + const [o, s] = obj + const id = o.connect(s, set) + this.onDropped(() => o.disconnect(id)) + } + } + else { + if (typeof sigOrFn === "string") { + const id = objs.connect(sigOrFn, set) + this.onDropped(() => objs.disconnect(id)) + } + } + + return this as unknown as Variable + } + + static derive< + const Deps extends Array>, + Args extends { + [K in keyof Deps]: Deps[K] extends Subscribable ? T : never + }, + V = Args, + >(deps: Deps, fn: (...args: Args) => V = (...args) => args as unknown as V) { + const update = () => fn(...deps.map(d => d.get()) as Args) + const derived = new Variable(update()) + const unsubs = deps.map(dep => dep.subscribe(() => derived.set(update()))) + derived.onDropped(() => unsubs.map(unsub => unsub())) + return derived + } +} + +export interface Variable extends Omit, "bind"> { + (transform: (value: T) => R): Binding + (): Binding +} + +export const Variable = new Proxy(VariableWrapper as any, { + apply: (_t, _a, args) => new VariableWrapper(args[0]), +}) as { + derive: typeof VariableWrapper["derive"] + (init: T): Variable + new(init: T): Variable +} + +export default Variable diff --git a/lang/gjs/package-lock.json b/lang/gjs/package-lock.json new file mode 100644 index 0000000..4d45c8f --- /dev/null +++ b/lang/gjs/package-lock.json @@ -0,0 +1,3823 @@ +{ + "name": "astal", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "astal", + "version": "0.1.0", + "license": "GPL", + "os": [ + "linux" + ], + "devDependencies": { + "@eslint/js": "^9.12.0", + "@stylistic/eslint-plugin": "^2.9.0", + "@ts-for-gir/cli": "^4.0.0-beta.16", + "@types/eslint__js": "^8.42.3", + "eslint": "^8.57.1", + "typescript": "^5.6.3", + "typescript-eslint": "^7.18.0" + }, + "engines": { + "gjs": ">=1.79.0" + }, + "funding": { + "type": "kofi", + "url": "https://ko-fi.com/aylur" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz", + "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@gi.ts/parser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@gi.ts/parser/-/parser-2.0.0.tgz", + "integrity": "sha512-Tz5T+3Ep+qY7rfBnYMGdVraCCUf1CKkDfxNd2fggfHLzjI7u5Th8a/piPgj0001jDs5czI+Ec3peh+6gkKPmHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^4.3.5" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@inquirer/figures": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", + "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@stylistic/eslint-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.9.0.tgz", + "integrity": "sha512-OrDyFAYjBT61122MIY1a3SfEgy3YCMgt2vL4eoPmvTwDBwyQhAXurxNQznlRD/jESNfYWfID8Ej+31LljvF7Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.8.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@ts-for-gir/cli": { + "version": "4.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@ts-for-gir/cli/-/cli-4.0.0-beta.16.tgz", + "integrity": "sha512-E5T6NOdmrkRw3b7SyaZco5g7udMcg8kbCNnxgASLQj7VC0cxiwxLi/z7ALYjmbB1W5xhuensauq1yh5rLUWAXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gi.ts/parser": "^2.0.0", + "@ts-for-gir/generator-base": "^4.0.0-beta.16", + "@ts-for-gir/generator-html-doc": "^4.0.0-beta.16", + "@ts-for-gir/generator-typescript": "^4.0.0-beta.16", + "@ts-for-gir/lib": "^4.0.0-beta.16", + "colorette": "^2.0.20", + "cosmiconfig": "^9.0.0", + "glob": "^11.0.0", + "inquirer": "^9.3.6", + "prettier": "^3.3.3", + "yargs": "^17.7.2" + }, + "bin": { + "ts-for-gir": "lib/start.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-for-gir/generator-base": { + "version": "4.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@ts-for-gir/generator-base/-/generator-base-4.0.0-beta.16.tgz", + "integrity": "sha512-qNahOOm2aRC5w6gqk5xjb0MvyxtYwhTFDvHwuWXEUJ+EoFeWSi95ZYkvivvowsoAiQ5W805FMXzJtt4P72O0Hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ts-for-gir/lib": "^4.0.0-beta.16" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-for-gir/generator-html-doc": { + "version": "4.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@ts-for-gir/generator-html-doc/-/generator-html-doc-4.0.0-beta.16.tgz", + "integrity": "sha512-2gCEUuSsvbUEdcAcU1LKFWawC56TYg4CmHCswzDjwPeEpOrzq1tweoaW2muYTg3COcF58Jax5/TWnJ88TmFdyA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ts-for-gir/generator-base": "^4.0.0-beta.16", + "@ts-for-gir/lib": "^4.0.0-beta.16" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-for-gir/generator-typescript": { + "version": "4.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@ts-for-gir/generator-typescript/-/generator-typescript-4.0.0-beta.16.tgz", + "integrity": "sha512-BHXqfaqH5yOXD9ckL6h8ZGn17U7SkajksS4/KuQCtAhURjpXT3xw7mKw5W3FObhvFSkiaonPkZwapRUgD8kZ6w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@ts-for-gir/generator-base": "^4.0.0-beta.16", + "@ts-for-gir/lib": "^4.0.0-beta.16", + "ejs": "^3.1.10", + "xml2js": "^0.6.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-for-gir/lib": { + "version": "4.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@ts-for-gir/lib/-/lib-4.0.0-beta.16.tgz", + "integrity": "sha512-6sFr4mHKoX+/4WT+dMpJ1EXnXSbx7lG6inQ2j6riVY0DkQu5y3ebaL3oUtpHi5twA54YWaOrP8NfFr3eg3Z3fA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gi.ts/parser": "^2.0.0", + "colorette": "^2.0.20", + "ejs": "^3.1.10", + "glob": "^11.0.0", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", + "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", + "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", + "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/visitor-keys": "8.8.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", + "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.8.1", + "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/typescript-estree": "8.8.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", + "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.8.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "9.3.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.3.7.tgz", + "integrity": "sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.3", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "license": "ISC" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.18.0.tgz", + "integrity": "sha512-PonBkP603E3tt05lDkbOMyaxJjvKqQrXsnow72sVeOFINDE/qNmnnd+f9b4N+U7W6MXnnYyrhtmF2t08QWwUbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", + "@typescript-eslint/utils": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/lang/gjs/package.json b/lang/gjs/package.json new file mode 100644 index 0000000..447ddcd --- /dev/null +++ b/lang/gjs/package.json @@ -0,0 +1,47 @@ +{ + "name": "astal", + "version": "0.1.0", + "description": "Building blocks for building linux desktop shell", + "type": "module", + "author": "Aylur", + "license": "GPL", + "repository": { + "type": "git", + "url": "https://github.com/aylur/astal.git", + "directory": "lang/gjs" + }, + "funding": { + "type": "kofi", + "url": "https://ko-fi.com/aylur" + }, + "exports": { + ".": "./index.ts", + "./gtk3": "./gtk3/index.ts", + "./gtk4": "./gtk3/index.ts", + "./lib/binding": "./lib/binding.ts", + "./lib/file": "./lib/file.ts", + "./lib/gobject": "./lib/gobject.ts", + "./lib/process": "./lib/process.ts", + "./lib/time": "./lib/time.ts", + "./lib/variable": "./lib/variable.ts" + }, + "engines": { + "gjs": ">=1.79.0" + }, + "os": [ + "linux" + ], + "devDependencies": { + "@eslint/js": "^9.12.0", + "@stylistic/eslint-plugin": "^2.9.0", + "@ts-for-gir/cli": "^4.0.0-beta.16", + "@types/eslint__js": "^8.42.3", + "eslint": "^8.57.1", + "typescript": "^5.6.3", + "typescript-eslint": "^7.18.0" + }, + "scripts": { + "lint": "eslint . --fix", + "types": "ts-for-gir generate -o @girs" + } +} diff --git a/lang/gjs/tsconfig.json b/lang/gjs/tsconfig.json new file mode 100644 index 0000000..71fd218 --- /dev/null +++ b/lang/gjs/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "target": "ES2023", + "outDir": "dist", + "strict": true, + "moduleResolution": "Bundler", + "skipLibCheck": true, + "baseUrl": ".", + }, + "include": [ + "@girs", + "lib/*", + // "gtk3/*", + // "gtk4/*", + "index.ts", + ] +} diff --git a/lang/lua/astal-dev-1.rockspec b/lang/lua/astal-dev-1.rockspec new file mode 100644 index 0000000..d392a79 --- /dev/null +++ b/lang/lua/astal-dev-1.rockspec @@ -0,0 +1,31 @@ +package = "astal" +version = "dev-1" + +source = { + url = "git+https://github.com/aylur/astal", +} + +description = { + summary = "lua bindings for libastal.", + homepage = "https://aylur.github.io/astal/", + license = "GPL-3", +} + +dependencies = { + "lua >= 5.1, < 5.4", + "lgi >= 0.9.2", +} + +build = { + type = "builtin", + modules = { + ["astal.application"] = "lib/application.lua", + ["astal.binding"] = "lib/binding.lua", + ["astal.init"] = "lib/init.lua", + ["astal.process"] = "lib/process.lua", + ["astal.time"] = "lib/time.lua", + ["astal.variable"] = "lib/variable.lua", + ["astal.widget"] = "lib/widget.lua", + ["astal.file"] = "lib/file.lua", + }, +} diff --git a/lang/lua/gtk3/app.lua b/lang/lua/gtk3/app.lua new file mode 100644 index 0000000..7895f69 --- /dev/null +++ b/lang/lua/gtk3/app.lua @@ -0,0 +1,96 @@ +local lgi = require("lgi") +local Astal = lgi.require("Astal", "3.0") +local AstalIO = lgi.require("AstalIO", "0.1") + +local AstalLua = Astal.Application:derive("AstalLua") +local request_handler + +function AstalLua:do_request(msg, conn) + if type(request_handler) == "function" then + request_handler(msg, function(response) + AstalIO.write_sock(conn, tostring(response), function(_, res) + AstalIO.write_sock_finish(res) + end) + end) + else + Astal.Application.do_request(self, msg, conn) + end +end + +function AstalLua:quit(code) + Astal.Application.quit(self) + os.exit(code) +end + +local app = AstalLua() + +---@class StartConfig +---@field icons? string +---@field instance_name? string +---@field gtk_theme? string +---@field icon_theme? string +---@field cursor_theme? string +---@field css? string +---@field hold? boolean +---@field request_handler? fun(msg: string, response: fun(res: any)) +---@field main? fun(...): unknown +---@field client? fun(message: fun(msg: string): string, ...): unknown + +---@param config StartConfig | nil +function Astal.Application:start(config) + if config == nil then + config = {} + end + + if config.client == nil then + config.client = function() + print('Astal instance "' .. app.instance_name .. '" is already running') + os.exit(1) + end + end + + if config.hold == nil then + config.hold = true + end + + request_handler = config.request_handler + + if config.css then + self:apply_css(config.css) + end + if config.icons then + self:add_icons(config.icons) + end + if config.instance_name then + self.instance_name = config.instance_name + end + if config.gtk_theme then + self.gtk_theme = config.gtk_theme + end + if config.icon_theme then + self.icon_theme = config.icon_theme + end + if config.cursor_theme then + self.cursor_theme = config.cursor_theme + end + + app.on_activate = function() + if type(config.main) == "function" then + config.main(table.unpack(arg)) + end + if config.hold then + self:hold() + end + end + + local _, err = app:acquire_socket() + if err ~= nil then + return config.client(function(msg) + return AstalIO.send_message(self.instance_name, msg) + end, table.unpack(arg)) + end + + self:run(nil) +end + +return app diff --git a/lang/lua/gtk3/astalify.lua b/lang/lua/gtk3/astalify.lua new file mode 100644 index 0000000..065de40 --- /dev/null +++ b/lang/lua/gtk3/astalify.lua @@ -0,0 +1,236 @@ +local lgi = require("lgi") +local Astal = lgi.require("Astal", "3.0") +local Gtk = lgi.require("Gtk", "3.0") +local GObject = lgi.require("GObject", "2.0") +local Binding = require("astal.lib.binding") +local Variable = require("astal.lib.variable") +local exec_async = require("astal.lib.process").exec_async + +local function filter(tbl, fn) + local copy = {} + for key, value in pairs(tbl) do + if fn(value, key) then + if type(key) == "number" then + table.insert(copy, value) + else + copy[key] = value + end + end + end + return copy +end + +local function map(tbl, fn) + local copy = {} + for key, value in pairs(tbl) do + copy[key] = fn(value) + end + return copy +end + +local flatten +flatten = function(tbl) + local copy = {} + for _, value in pairs(tbl) do + if type(value) == "table" and getmetatable(value) == nil then + for _, inner in pairs(flatten(value)) do + table.insert(copy, inner) + end + else + table.insert(copy, value) + end + end + return copy +end + +local function includes(tbl, elem) + for _, value in pairs(tbl) do + if value == elem then + return true + end + end + return false +end + +local function set_children(parent, children) + children = map(flatten(children), function(item) + if Gtk.Widget:is_type_of(item) then + return item + end + return Gtk.Label({ + visible = true, + label = tostring(item), + }) + end) + + -- remove + if Gtk.Bin:is_type_of(parent) then + local ch = parent:get_child() + if ch ~= nil then + parent:remove(ch) + end + if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then + ch:destroy() + end + elseif Gtk.Container:is_type_of(parent) then + for _, ch in ipairs(parent:get_children()) do + parent:remove(ch) + if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then + ch:destroy() + end + end + end + + -- TODO: add more container types + if Astal.Box:is_type_of(parent) then + parent:set_children(children) + elseif Astal.Stack:is_type_of(parent) then + parent:set_children(children) + elseif Astal.CenterBox:is_type_of(parent) then + parent.start_widget = children[1] + parent.center_widget = children[2] + parent.end_widget = children[3] + elseif Astal.Overlay:is_type_of(parent) then + parent:set_child(children[1]) + children[1] = nil + parent:set_overlays(children) + elseif Gtk.Container:is_type_of(parent) then + for _, child in pairs(children) do + if Gtk.Widget:is_type_of(child) then + parent:add(child) + end + end + end +end + +local function merge_bindings(array) + local function get_values(...) + local args = { ... } + local i = 0 + return map(array, function(value) + if getmetatable(value) == Binding then + i = i + 1 + return args[i] + else + return value + end + end) + end + + local bindings = filter(array, function(v) + return getmetatable(v) == Binding + end) + + if #bindings == 0 then + return array + end + + if #bindings == 1 then + return bindings[1]:as(get_values) + end + + return Variable.derive(bindings, get_values)() +end + +return function(ctor) + function ctor:hook(object, signalOrCallback, callback) + if GObject.Object:is_type_of(object) and type(signalOrCallback) == "string" then + local id + if string.sub(signalOrCallback, 1, 8) == "notify::" then + local prop = string.gsub(signalOrCallback, "notify::", "") + id = object.on_notify:connect(function() + callback(self, object[prop]) + end, prop, false) + else + id = object["on_" .. signalOrCallback]:connect(function(_, ...) + callback(self, ...) + end) + end + self.on_destroy = function() + GObject.signal_handler_disconnect(object, id) + end + elseif type(object.subscribe) == "function" then + local unsub = object.subscribe(function(...) + signalOrCallback(self, ...) + end) + self.on_destroy = unsub + else + error("can not hook: not gobject+signal or subscribable") + end + end + + function ctor:toggle_class_name(name, on) + Astal.widget_toggle_class_name(self, name, on) + end + + return function(tbl) + if tbl == nil then + tbl = {} + end + + local bindings = {} + local setup = tbl.setup + + -- collect children + local children = merge_bindings(flatten(filter(tbl, function(_, key) + return type(key) == "number" + end))) + + -- default visible to true + if type(tbl.visible) ~= "boolean" then + tbl.visible = true + end + + -- collect props + local props = filter(tbl, function(_, key) + return type(key) == "string" and key ~= "setup" + end) + + -- collect signal handlers + for prop, value in pairs(props) do + if string.sub(prop, 0, 2) == "on" and type(value) ~= "function" then + props[prop] = function() + exec_async(value, print) + end + end + end + + -- collect bindings + for prop, value in pairs(props) do + if getmetatable(value) == Binding then + bindings[prop] = value + props[prop] = value:get() + end + end + + -- construct, attach bindings, add children + local widget = ctor() + + if getmetatable(children) == Binding then + set_children(widget, children:get()) + widget.on_destroy = children:subscribe(function(v) + set_children(widget, v) + end) + else + if #children > 0 then + set_children(widget, children) + end + end + + for prop, binding in pairs(bindings) do + widget.on_destroy = binding:subscribe(function(v) + widget[prop] = v + end) + end + + for prop, value in pairs(props) do + widget[prop] = value + end + + if type(setup) == "function" then + setup(widget) + end + + return widget + end +end diff --git a/lang/lua/gtk3/widget.lua b/lang/lua/gtk3/widget.lua new file mode 100644 index 0000000..beaad6c --- /dev/null +++ b/lang/lua/gtk3/widget.lua @@ -0,0 +1,90 @@ +local lgi = require("lgi") +local Astal = lgi.require("Astal", "3.0") +local Gtk = lgi.require("Gtk", "3.0") +local astalify = require("astal.gtk3.astalify") + +local Widget = { + astalify = astalify, + Box = astalify(Astal.Box), + Button = astalify(Astal.Button), + CenterBox = astalify(Astal.CenterBox), + CircularProgress = astalify(Astal.CircularProgress), + DrawingArea = astalify(Gtk.DrawingArea), + Entry = astalify(Gtk.Entry), + EventBox = astalify(Astal.EventBox), + -- TODO: Fixed + -- TODO: FlowBox + Icon = astalify(Astal.Icon), + Label = astalify(Gtk.Label), + LevelBar = astalify(Astal.LevelBar), + -- TODO: ListBox + Overlay = astalify(Astal.Overlay), + Revealer = astalify(Gtk.Revealer), + Scrollable = astalify(Astal.Scrollable), + Slider = astalify(Astal.Slider), + Stack = astalify(Astal.Stack), + Switch = astalify(Gtk.Switch), + Window = astalify(Astal.Window), +} + +Gtk.Widget._attribute.css = { + get = Astal.widget_get_css, + set = Astal.widget_set_css, +} + +Gtk.Widget._attribute.class_name = { + get = function(self) + local result = "" + local strings = Astal.widget_get_class_names(self) + for i, str in ipairs(strings) do + result = result .. str + if i < #strings then + result = result .. " " + end + end + return result + end, + set = function(self, class_name) + local names = {} + for word in class_name:gmatch("%S+") do + table.insert(names, word) + end + Astal.widget_set_class_names(self, names) + end, +} + +Gtk.Widget._attribute.cursor = { + get = Astal.widget_get_cursor, + set = Astal.widget_set_cursor, +} + +Gtk.Widget._attribute.click_through = { + get = Astal.widget_get_click_through, + set = Astal.widget_set_click_through, +} + +local no_implicit_destroy = {} +Gtk.Widget._attribute.no_implicit_destroy = { + get = function(self) + return no_implicit_destroy[self] or false + end, + set = function(self, v) + if no_implicit_destroy[self] == nil then + self.on_destroy = function() + no_implicit_destroy[self] = nil + end + end + no_implicit_destroy[self] = v + end, +} + +Astal.Box._attribute.children = { + get = Astal.Box.get_children, + set = Astal.Box.set_children, +} + +return setmetatable(Widget, { + __call = function(_, ctor) + return astalify(ctor) + end, +}) diff --git a/lang/lua/init.lua b/lang/lua/init.lua new file mode 100644 index 0000000..b6ab30c --- /dev/null +++ b/lang/lua/init.lua @@ -0,0 +1,27 @@ +local lgi = require("lgi") +local Binding = require("astal.lib.binding") +local File = require("astal.lib.file") +local Process = require("astal.lib.process") +local Time = require("astal.lib.time") +local Variable = require("astal.lib.variable") + +return { + Variable = Variable, + bind = Binding.new, + + interval = Time.interval, + timeout = Time.timeout, + idle = Time.idle, + + subprocess = Process.subprocess, + exec = Process.exec, + exec_async = Process.exec_async, + + read_file = File.read_file, + read_file_async = File.read_file_async, + write_file = File.write_file, + write_file_async = File.write_file_async, + monitor_file = File.monitor_file, + + require = lgi.require, +} diff --git a/lang/lua/lib/binding.lua b/lang/lua/lib/binding.lua new file mode 100644 index 0000000..ba1e6e4 --- /dev/null +++ b/lang/lua/lib/binding.lua @@ -0,0 +1,71 @@ +local lgi = require("lgi") +local GObject = lgi.require("GObject", "2.0") + +---@class Binding +---@field emitter table|Variable +---@field property? string +---@field transformFn function +local Binding = {} + +---@param emitter table +---@param property? string +---@return Binding +function Binding.new(emitter, property) + return setmetatable({ + emitter = emitter, + property = property, + transformFn = function(v) + return v + end, + }, Binding) +end + +function Binding:__tostring() + local str = "Binding<" .. tostring(self.emitter) + if self.property ~= nil then + str = str .. ", " .. self.property + end + return str .. ">" +end + +function Binding:get() + if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then + return self.transformFn(self.emitter[self.property]) + end + if type(self.emitter.get) == "function" then + return self.transformFn(self.emitter:get()) + end + error("can not get: Not a GObject or a Variable " + self) +end + +---@param transform fun(value: any): any +---@return Binding +function Binding:as(transform) + local b = Binding.new(self.emitter, self.property) + b.transformFn = function(v) + return transform(self.transformFn(v)) + end + return b +end + +---@param callback fun(value: any) +---@return function +function Binding:subscribe(callback) + if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then + local id = self.emitter.on_notify:connect(function() + callback(self:get()) + end, self.property, false) + return function() + GObject.signal_handler_disconnect(self.emitter, id) + end + end + if type(self.emitter.subscribe) == "function" then + return self.emitter:subscribe(function() + callback(self:get()) + end) + end + error("can not subscribe: Not a GObject or a Variable " + self) +end + +Binding.__index = Binding +return Binding diff --git a/lang/lua/lib/file.lua b/lang/lua/lib/file.lua new file mode 100644 index 0000000..e3be783 --- /dev/null +++ b/lang/lua/lib/file.lua @@ -0,0 +1,45 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") +local GObject = lgi.require("GObject", "2.0") + +local M = {} + +---@param path string +---@return string +function M.read_file(path) + return Astal.read_file(path) +end + +---@param path string +---@param callback fun(content: string, err: string): nil +function M.read_file_async(path, callback) + Astal.read_file_async(path, function(_, res) + local content, err = Astal.read_file_finish(res) + callback(content, err) + end) +end + +---@param path string +---@param content string +function M.write_file(path, content) + Astal.write_file(path, content) +end + +---@param path string +---@param content string +---@param callback? fun(err: string): nil +function M.write_file_async(path, content, callback) + Astal.write_file_async(path, content, function(_, res) + if type(callback) == "function" then + callback(Astal.write_file_finish(res)) + end + end) +end + +---@param path string +---@param callback fun(file: string, event: integer): nil +function M.monitor_file(path, callback) + return Astal.monitor_file(path, GObject.Closure(callback)) +end + +return M diff --git a/lang/lua/lib/process.lua b/lang/lua/lib/process.lua new file mode 100644 index 0000000..b8b7436 --- /dev/null +++ b/lang/lua/lib/process.lua @@ -0,0 +1,78 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") + +local M = {} + +---@param commandline string | string[] +---@param on_stdout? fun(out: string): nil +---@param on_stderr? fun(err: string): nil +---@return { kill: function } | nil proc +function M.subprocess(commandline, on_stdout, on_stderr) + if on_stdout == nil then + on_stdout = function(out) + io.stdout:write(tostring(out) .. "\n") + end + end + + if on_stderr == nil then + on_stderr = function(err) + io.stderr:write(tostring(err) .. "\n") + end + end + + local proc, err + if type(commandline) == "table" then + proc, err = Astal.Process.subprocessv(commandline) + else + proc, err = Astal.Process.subprocess(commandline) + end + if err ~= nil then + err(err) + return nil + end + proc.on_stdout = function(_, stdoud) + on_stdout(stdoud) + end + proc.on_stderr = function(_, stderr) + on_stderr(stderr) + end + return proc +end + +---@param commandline string | string[] +---@return string, string +function M.exec(commandline) + if type(commandline) == "table" then + return Astal.Process.execv(commandline) + else + return Astal.Process.exec(commandline) + end +end + +---@param commandline string | string[] +---@param callback? fun(out: string, err: string): nil +function M.exec_async(commandline, callback) + if callback == nil then + callback = function(out, err) + if err ~= nil then + io.stdout:write(tostring(out) .. "\n") + else + io.stderr:write(tostring(err) .. "\n") + end + end + end + + if type(commandline) == "table" then + Astal.Process.exec_asyncv(commandline, function(_, res) + local out, err = Astal.Process.exec_asyncv_finish(res) + callback(out, err) + end) + else + Astal.Process.exec_async(commandline, function(_, res) + local out, err = Astal.Process.exec_finish(res) + callback(out, err) + end) + end +end + +return M diff --git a/lang/lua/lib/time.lua b/lang/lua/lib/time.lua new file mode 100644 index 0000000..7719da9 --- /dev/null +++ b/lang/lua/lib/time.lua @@ -0,0 +1,27 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") +local GObject = lgi.require("GObject", "2.0") + +local M = {} + +---@param interval number +---@param fn function +---@return { cancel: function, on_now: function } +function M.interval(interval, fn) + return Astal.Time.interval(interval, GObject.Closure(fn)) +end + +---@param timeout number +---@param fn function +---@return { cancel: function, on_now: function } +function M.timeout(timeout, fn) + return Astal.Time.timeout(timeout, GObject.Closure(fn)) +end + +---@param fn function +---@return { cancel: function, on_now: function } +function M.idle(fn) + return Astal.Time.idle(GObject.Closure(fn)) +end + +return M diff --git a/lang/lua/lib/variable.lua b/lang/lua/lib/variable.lua new file mode 100644 index 0000000..c93d04d --- /dev/null +++ b/lang/lua/lib/variable.lua @@ -0,0 +1,276 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") +local GObject = lgi.require("GObject", "2.0") +local Binding = require("astal.lib.binding") +local Time = require("astal.lib.time") +local Process = require("astal.lib.process") + +---@class Variable +---@field private variable table +---@field private err_handler? function +---@field private _value any +---@field private _poll? table +---@field private _watch? table +---@field private poll_interval number +---@field private poll_exec? string[] | string +---@field private poll_transform? fun(next: any, prev: any): any +---@field private poll_fn? function +---@field private watch_transform? fun(next: any, prev: any): any +---@field private watch_exec? string[] | string +local Variable = {} +Variable.__index = Variable + +---@param value any +---@return Variable +function Variable.new(value) + local v = Astal.VariableBase() + local variable = setmetatable({ + variable = v, + _value = value, + }, Variable) + v.on_dropped = function() + variable:stop_watch() + variable:stop_watch() + end + v.on_error = function(_, err) + if variable.err_handler then + variable.err_handler(err) + end + end + return variable +end + +---@param transform function +---@return Binding +function Variable:__call(transform) + if transform == nil then + transform = function(v) + return v + end + return Binding.new(self) + end + return Binding.new(self):as(transform) +end + +function Variable:__tostring() + return "Variable<" .. tostring(self:get()) .. ">" +end + +function Variable:get() + return self._value or nil +end + +function Variable:set(value) + if value ~= self:get() then + self._value = value + self.variable:emit_changed() + end +end + +function Variable:start_poll() + if self._poll ~= nil then + return + end + + if self.poll_fn then + self._poll = Time.interval(self.poll_interval, function() + self:set(self.poll_fn(self:get())) + end) + elseif self.poll_exec then + self._poll = Time.interval(self.poll_interval, function() + Process.exec_async(self.poll_exec, function(out, err) + if err ~= nil then + return self.variable.emit_error(err) + end + self:set(self.poll_transform(out, self:get())) + end) + end) + end +end + +function Variable:start_watch() + if self._watch then + return + end + + self._watch = Process.subprocess(self.watch_exec, function(out) + self:set(self.watch_transform(out, self:get())) + end, function(err) + self.variable.emit_error(err) + end) +end + +function Variable:stop_poll() + if self._poll then + self._poll.cancel() + end + self._poll = nil +end + +function Variable:stop_watch() + if self._watch then + self._watch.kill() + end + self._watch = nil +end + +function Variable:is_polling() + return self._poll ~= nil +end + +function Variable:is_watching() + return self._watch ~= nil +end + +function Variable:drop() + self.variable.emit_dropped() +end + +---@param callback function +---@return Variable +function Variable:on_dropped(callback) + self.variable.on_dropped = callback + return self +end + +---@param callback function +---@return Variable +function Variable:on_error(callback) + self.err_handler = nil + self.variable.on_eror = function(_, err) + callback(err) + end + return self +end + +---@param callback fun(value: any) +---@return function +function Variable:subscribe(callback) + local id = self.variable.on_changed:connect(function() + callback(self:get()) + end) + return function() + GObject.signal_handler_disconnect(self.variable, id) + end +end + +---@param interval number +---@param exec string | string[] | function +---@param transform? fun(next: any, prev: any): any +function Variable:poll(interval, exec, transform) + if transform == nil then + transform = function(next) + return next + end + end + self:stop_poll() + self.poll_interval = interval + self.poll_transform = transform + + if type(exec) == "function" then + self.poll_fn = exec + self.poll_exec = nil + else + self.poll_exec = exec + self.poll_fn = nil + end + self:start_poll() + return self +end + +---@param exec string | string[] +---@param transform? fun(next: any, prev: any): any +function Variable:watch(exec, transform) + if transform == nil then + transform = function(next) + return next + end + end + self:stop_poll() + self.watch_exec = exec + self.watch_transform = transform + self:start_watch() + return self +end + +---@param object table | table[] +---@param sigOrFn string | fun(...): any +---@param callback fun(...): any +---@return Variable +function Variable:observe(object, sigOrFn, callback) + local f + if type(sigOrFn) == "function" then + f = sigOrFn + elseif type(callback) == "function" then + f = callback + else + f = function() + return self:get() + end + end + local set = function(...) + self:set(f(...)) + end + + if type(sigOrFn) == "string" then + object["on_" .. sigOrFn]:connect(set) + else + for _, obj in ipairs(object) do + obj[1]["on_" .. obj[2]]:connect(set) + end + end + return self +end + +---@param deps Variable | (Binding | Variable)[] +---@param transform? fun(...): any +---@return Variable +function Variable.derive(deps, transform) + if type(transform) == "nil" then + transform = function(...) + return { ... } + end + end + + if getmetatable(deps) == Variable then + local var = Variable.new(transform(deps:get())) + deps:subscribe(function(v) + var:set(transform(v)) + end) + return var + end + + for i, var in ipairs(deps) do + if getmetatable(var) == Variable then + deps[i] = Binding.new(var) + end + end + + local update = function() + local params = {} + for i, binding in ipairs(deps) do + params[i] = binding:get() + end + return transform(table.unpack(params), 1, #deps) + end + + local var = Variable.new(update()) + + local unsubs = {} + for i, b in ipairs(deps) do + unsubs[i] = b:subscribe(update) + end + + var.variable.on_dropped = function() + for _, unsub in ipairs(unsubs) do + unsub() + end + end + return var +end + +return setmetatable(Variable, { + __call = function(_, v) + return Variable.new(v) + end, +}) diff --git a/lang/lua/stylua.toml b/lang/lua/stylua.toml new file mode 100644 index 0000000..d4a4951 --- /dev/null +++ b/lang/lua/stylua.toml @@ -0,0 +1,3 @@ +indent_type = "Spaces" +indent_width = 4 +column_width = 100 diff --git a/lib/astal/gtk3/src/application.vala b/lib/astal/gtk3/src/application.vala index 8539aa0..2255333 100644 --- a/lib/astal/gtk3/src/application.vala +++ b/lib/astal/gtk3/src/application.vala @@ -3,8 +3,9 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { private List css_providers = new List(); private SocketService service; private DBusConnection conn; - private string socket_path { get; set; } - private string _instance_name; + private string _instance_name = "astal"; + + public string socket_path { get; private set; } [DBus (visible=false)] public signal void monitor_added(Gdk.Monitor monitor); @@ -34,8 +35,8 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { public string instance_name { owned get { return _instance_name; } construct set { - application_id = "io.Astal." + value; - _instance_name = value; + _instance_name = value != null ? value : "astal"; + application_id = @"io.Astal.$_instance_name"; } } @@ -138,54 +139,39 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { AstalIO.write_sock.begin(conn, @"missing response implementation on $application_id"); } - /** - * should be called before `run()` - * the return value indicates if instance is already running - */ [DBus (visible=false)] - public void acquire_socket() { - try { - service = AstalIO.acquire_socket(this); - - Bus.own_name( - BusType.SESSION, - "io.Astal." + instance_name, - BusNameOwnerFlags.NONE, - (conn) => { - try { - this.conn = conn; - conn.register_object("/io/Astal/Application", this); - } catch (Error err) { - critical(err.message); - } - }, - () => {}, - () => {} - ); - } catch (Error err) { - critical("could not acquire socket %s\n", application_id); - critical(err.message); - } - } + public void acquire_socket() throws Error { + string path; + service = AstalIO.acquire_socket(this, out path); + socket_path = path; - public new void quit() throws DBusError, IOError { - if (service != null) { - if (FileUtils.test(socket_path, GLib.FileTest.EXISTS)){ + Bus.own_name( + BusType.SESSION, + application_id, + BusNameOwnerFlags.NONE, + (conn) => { try { - File.new_for_path(socket_path).delete(null); + this.conn = conn; + conn.register_object("/io/Astal/Application", this); } catch (Error err) { - warning(err.message); + critical(err.message); } - } + }, + () => {}, + () => {} + ); + } + + public new void quit() throws DBusError, IOError { + if (service != null) { + service.stop(); + service.close(); } base.quit(); } construct { - if (instance_name == null) - instance_name = "astal"; - activate.connect(() => { var display = Gdk.Display.get_default(); display.monitor_added.connect((mon) => { diff --git a/lib/astal/io/application.vala b/lib/astal/io/application.vala index 00bef57..60318ed 100644 --- a/lib/astal/io/application.vala +++ b/lib/astal/io/application.vala @@ -14,7 +14,7 @@ public interface Application : Object { public abstract void request(string msg, SocketConnection conn) throws Error; } -public SocketService acquire_socket(Application app) throws Error { +public SocketService acquire_socket(Application app, out string sock) throws Error { var name = app.instance_name; foreach (var instance in get_instances()) { if (instance == name) { @@ -23,7 +23,13 @@ public SocketService acquire_socket(Application app) throws Error { } var rundir = Environment.get_user_runtime_dir(); - var path = @"$rundir/$name.sock"; + var dir = @"$rundir/astal"; + var path = @"$dir/$name.sock"; + sock = path; + + if (!FileUtils.test(dir, FileTest.IS_DIR)) { + File.new_for_path(path).make_directory_with_parents(null); + } if (FileUtils.test(path, FileTest.EXISTS)) { try { @@ -123,7 +129,7 @@ public static void toggle_window_by_name(string instance, string window) { public static string send_message(string instance_name, string msg) { var rundir = Environment.get_user_runtime_dir(); - var socket_path = @"$rundir/$instance_name.sock"; + var socket_path = @"$rundir/astal/$instance_name.sock"; var client = new SocketClient(); try { diff --git a/nix/devshell.nix b/nix/devshell.nix index 936f4b4..f5aa18c 100644 --- a/nix/devshell.nix +++ b/nix/devshell.nix @@ -6,11 +6,11 @@ ps.lgi (ps.luaPackages.toLuaModule (pkgs.stdenv.mkDerivation { name = "astal"; - src = "${self}/core/lua"; + src = "${self}/lang/lua"; dontBuild = true; installPhase = '' mkdir -p $out/share/lua/${ps.lua.luaversion}/astal - cp -r astal/* $out/share/lua/${ps.lua.luaversion}/astal + cp -r * $out/share/lua/${ps.lua.luaversion}/astal ''; })) ]); @@ -28,7 +28,9 @@ ninja vala gtk3 + gtk4 gtk-layer-shell + gtk4-layer-shell json-glib pam gvfs diff --git a/nix/lua.nix b/nix/lua.nix index 6b0b802..549c6c3 100644 --- a/nix/lua.nix +++ b/nix/lua.nix @@ -12,11 +12,11 @@ defaults: { ps.lgi (ps.luaPackages.toLuaModule (pkgs.stdenvNoCC.mkDerivation { name = "astal"; - src = "${astal}/core/lua"; + src = "${astal}/lang/lua"; dontBuild = true; installPhase = '' mkdir -p $out/share/lua/${ps.lua.luaversion}/astal - cp -r astal/* $out/share/lua/${ps.lua.luaversion}/astal + cp -r * $out/share/lua/${ps.lua.luaversion}/astal ''; })) (ps.luaPackages.toLuaModule (pkgs.stdenvNoCC.mkDerivation { @@ -46,7 +46,8 @@ in extraPackages ++ [ lua - astal.packages.${pkgs.system}.default + astal.packages.${pkgs.system}.io + astal.packages.${pkgs.system}.astal3 ]; installPhase = '' -- cgit v1.2.3 From d63332b533b390e7e68f8f1fc2432958c4d36a4f Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 01:26:40 +0200 Subject: update examples --- examples/js/.gitignore | 3 ++ examples/js/simple-bar/app.ts | 7 +++- examples/js/simple-bar/widget/Bar.tsx | 4 ++- examples/lua/simple-bar/init.lua | 7 +++- examples/lua/simple-bar/widget/Bar.lua | 12 ++++--- examples/py/.gitignore | 2 -- examples/py/simple-bar/app.py | 20 ++++++----- examples/py/simple-bar/versions.py | 3 +- examples/py/simple-bar/widget/Bar.py | 8 +++-- examples/vala/simple-bar/app.in.vala | 11 +++--- examples/vala/simple-bar/flake.lock | 62 -------------------------------- examples/vala/simple-bar/flake.nix | 3 +- examples/vala/simple-bar/meson.build | 3 +- examples/vala/simple-bar/widget/Bar.vala | 4 +-- 14 files changed, 56 insertions(+), 93 deletions(-) delete mode 100644 examples/vala/simple-bar/flake.lock diff --git a/examples/js/.gitignore b/examples/js/.gitignore index b0d983b..261d669 100644 --- a/examples/js/.gitignore +++ b/examples/js/.gitignore @@ -1,3 +1,6 @@ @girs/ tsconfig.json env.d.ts +dist/ +package.json +package-lock.json diff --git a/examples/js/simple-bar/app.ts b/examples/js/simple-bar/app.ts index 05f043a..4b7ea48 100644 --- a/examples/js/simple-bar/app.ts +++ b/examples/js/simple-bar/app.ts @@ -1,8 +1,13 @@ -import { App } from "astal" +import { App } from "astal/gtk3" import style from "./style.scss" import Bar from "./widget/Bar" App.start({ css: style, + instanceName: "js", + requestHandler(request, res) { + print(request) + res("ok") + }, main: () => App.get_monitors().map(Bar), }) diff --git a/examples/js/simple-bar/widget/Bar.tsx b/examples/js/simple-bar/widget/Bar.tsx index 644e835..efc065a 100644 --- a/examples/js/simple-bar/widget/Bar.tsx +++ b/examples/js/simple-bar/widget/Bar.tsx @@ -1,4 +1,6 @@ -import { App, Variable, Astal, Gtk, Gdk, GLib, bind } from "astal" +import { App } from "astal/gtk3" +import { Variable, GLib, bind } from "astal" +import { Astal, Gtk, Gdk } from "astal/gtk3" import Hyprland from "gi://AstalHyprland" import Mpris from "gi://AstalMpris" import Battery from "gi://AstalBattery" diff --git a/examples/lua/simple-bar/init.lua b/examples/lua/simple-bar/init.lua index aecf7a6..8c412fb 100644 --- a/examples/lua/simple-bar/init.lua +++ b/examples/lua/simple-bar/init.lua @@ -1,5 +1,5 @@ local astal = require("astal") -local App = astal.App +local App = require("astal.gtk3.app") local Bar = require("widget.Bar") local src = require("lib").src @@ -10,7 +10,12 @@ local css = "/tmp/style.css" astal.exec("sass " .. scss .. " " .. css) App:start({ + instance_name = "lua", css = css, + request_handler = function(msg, res) + print(msg) + res("ok") + end, main = function() for _, mon in pairs(App.monitors) do Bar(mon) diff --git a/examples/lua/simple-bar/widget/Bar.lua b/examples/lua/simple-bar/widget/Bar.lua index d340cba..bf230bb 100644 --- a/examples/lua/simple-bar/widget/Bar.lua +++ b/examples/lua/simple-bar/widget/Bar.lua @@ -1,9 +1,9 @@ local astal = require("astal") -local App = astal.App -local Widget = astal.Widget +local App = require("astal.gtk3.app") +local Widget = require("astal.gtk3.widget") local Variable = astal.Variable -local Gdk = astal.Gdk -local GLib = astal.GLib +local Gdk = astal.require("Gdk", "3.0") +local GLib = astal.require("GLib") local bind = astal.bind local Mpris = astal.require("AstalMpris") local Battery = astal.require("AstalBattery") @@ -170,10 +170,12 @@ local function Time(format) end return function(gdkmonitor) + local WindowAnchor = astal.require("Astal", "3.0").WindowAnchor + return Widget.Window({ class_name = "Bar", gdkmonitor = gdkmonitor, - anchor = astal.Astal.WindowAnchor.TOP + astal.Astal.WindowAnchor.LEFT + astal.Astal.WindowAnchor.RIGHT, + anchor = WindowAnchor.TOP + WindowAnchor.LEFT + WindowAnchor.RIGHT, exclusivity = "EXCLUSIVE", Widget.CenterBox({ diff --git a/examples/py/.gitignore b/examples/py/.gitignore index b0bff8b..c18dd8d 100644 --- a/examples/py/.gitignore +++ b/examples/py/.gitignore @@ -1,3 +1 @@ -pygobject-stubs/ -*.pyi __pycache__/ diff --git a/examples/py/simple-bar/app.py b/examples/py/simple-bar/app.py index f5a8a80..17b6782 100755 --- a/examples/py/simple-bar/app.py +++ b/examples/py/simple-bar/app.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import sys import versions -from gi.repository import Astal, Gio +from gi.repository import AstalIO, Astal, Gio from widget.Bar import Bar from pathlib import Path @@ -10,23 +10,27 @@ css = "/tmp/style.css" class App(Astal.Application): - def do_request(self, msg: str, conn: Gio.SocketConnection) -> None: + def do_astal_application_request( + self, msg: str, conn: Gio.SocketConnection + ) -> None: print(msg) - Astal.write_sock(conn, "hello") + AstalIO.write_sock(conn, "hello") def do_activate(self) -> None: self.hold() - Astal.Process.execv(["sass", scss, css]) + AstalIO.Process.execv(["sass", scss, css]) self.apply_css(css, True) + print("hello") for mon in self.get_monitors(): self.add_window(Bar(mon)) -instance_name = "simple-bar" +instance_name = "python" app = App(instance_name=instance_name) if __name__ == "__main__": - if app.acquire_socket(): + try: + print(app.acquire_socket()) app.run(None) - else: - print(Astal.Application.send_message(instance_name, "".join(sys.argv[1:]))) + except Exception as e: + print(AstalIO.send_message(instance_name, "".join(sys.argv[1:]))) diff --git a/examples/py/simple-bar/versions.py b/examples/py/simple-bar/versions.py index a8a1ab8..0e57708 100644 --- a/examples/py/simple-bar/versions.py +++ b/examples/py/simple-bar/versions.py @@ -1,6 +1,7 @@ import gi -gi.require_version("Astal", "0.1") +gi.require_version("AstalIO", "0.1") +gi.require_version("Astal", "3.0") gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") gi.require_version("Gio", "2.0") diff --git a/examples/py/simple-bar/widget/Bar.py b/examples/py/simple-bar/widget/Bar.py index 89581f1..3b09dce 100644 --- a/examples/py/simple-bar/widget/Bar.py +++ b/examples/py/simple-bar/widget/Bar.py @@ -1,5 +1,6 @@ import math from gi.repository import ( + AstalIO, Astal, Gtk, Gdk, @@ -125,8 +126,8 @@ class SysTray(Gtk.Box): app.add_icons(theme) menu = item.create_menu() - btn = Astal.Button() - icon = Astal.Icon() + btn = Astal.Button(visible=True) + icon = Astal.Icon(visible=True) def on_clicked(btn): if menu: @@ -143,6 +144,7 @@ class SysTray(Gtk.Box): item.bind_property("gicon", icon, "gicon", SYNC) self.add(btn) self.items[id] = btn + self.show_all() def remove_item(self, _: Tray.Tray, id: str): if id in self.items: @@ -203,7 +205,7 @@ class Time(Astal.Label): def __init__(self, format="%H:%M - %A %e."): super().__init__() self.format = format - self.interval = Astal.Time.interval(1000, self.sync) + self.interval = AstalIO.Time.interval(1000, self.sync) self.connect("destroy", self.interval.cancel) Astal.widget_set_class_names(self, ["Time"]) diff --git a/examples/vala/simple-bar/app.in.vala b/examples/vala/simple-bar/app.in.vala index aece979..b04a1fa 100644 --- a/examples/vala/simple-bar/app.in.vala +++ b/examples/vala/simple-bar/app.in.vala @@ -3,7 +3,7 @@ class App : Astal.Application { public override void request (string msg, SocketConnection conn) { print(@"$msg\n"); - Astal.write_sock.begin(conn, "hello"); + AstalIO.write_sock.begin(conn, "ok"); } public override void activate () { @@ -14,16 +14,17 @@ class App : Astal.Application { } public static void main(string[] args) { - var instance_name = "simple-bar"; + var instance_name = "vala"; App.instance = new App() { instance_name = instance_name }; - if (App.instance.acquire_socket()) { + try { + App.instance.acquire_socket(); App.instance.run(null); - } else { - print(Astal.Application.send_message(instance_name, string.joinv(" ", args))); + } catch (Error err) { + print(AstalIO.send_message(instance_name, string.joinv(" ", args))); } } } diff --git a/examples/vala/simple-bar/flake.lock b/examples/vala/simple-bar/flake.lock deleted file mode 100644 index 06f572f..0000000 --- a/examples/vala/simple-bar/flake.lock +++ /dev/null @@ -1,62 +0,0 @@ -{ - "nodes": { - "astal": { - "inputs": { - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1727022015, - "narHash": "sha256-ka7aRbReUE6ImjQV8KabMHoojUgb3gtn1/9drMFTtBk=", - "owner": "aylur", - "repo": "astal", - "rev": "8cab7d039e2cf783033a5f1f26cf8be42b0d158e", - "type": "github" - }, - "original": { - "owner": "aylur", - "repo": "astal", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1716293225, - "narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1726937504, - "narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "9357f4f23713673f310988025d9dc261c20e70c6", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "astal": "astal", - "nixpkgs": "nixpkgs_2" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/examples/vala/simple-bar/flake.nix b/examples/vala/simple-bar/flake.nix index 78bfb62..d13c649 100644 --- a/examples/vala/simple-bar/flake.nix +++ b/examples/vala/simple-bar/flake.nix @@ -27,7 +27,8 @@ ]; buildInputs = [ - astal.packages.${system}.astal + astal.packages.${system}.io + astal.packages.${system}.astal3 astal.packages.${system}.battery astal.packages.${system}.wireplumber astal.packages.${system}.network diff --git a/examples/vala/simple-bar/meson.build b/examples/vala/simple-bar/meson.build index d0ef209..10f5dd2 100644 --- a/examples/vala/simple-bar/meson.build +++ b/examples/vala/simple-bar/meson.build @@ -8,7 +8,8 @@ pkgconfig_deps = [ dependency('gobject-2.0'), dependency('gtk+-3.0'), dependency('libnm'), - dependency('astal-0.1'), + dependency('astal-io-0.1'), + dependency('astal-3.0'), dependency('astal-battery-0.1'), dependency('astal-wireplumber-0.1'), dependency('astal-network-0.1'), diff --git a/examples/vala/simple-bar/widget/Bar.vala b/examples/vala/simple-bar/widget/Bar.vala index 6e99327..17db831 100644 --- a/examples/vala/simple-bar/widget/Bar.vala +++ b/examples/vala/simple-bar/widget/Bar.vala @@ -191,7 +191,7 @@ class Battery : Gtk.Box { class Time : Astal.Label { string format; - Astal.Time interval; + AstalIO.Time interval; void sync() { label = new DateTime.now_local().format(format); @@ -199,7 +199,7 @@ class Time : Astal.Label { public Time(string format = "%H:%M - %A %e.") { this.format = format; - interval = Astal.Time.interval(1000, null); + interval = AstalIO.Time.interval(1000, null); interval.now.connect(sync); destroy.connect(interval.cancel); Astal.widget_set_class_names(this, {"Time"}); -- cgit v1.2.3 From ede8890a08b3fbbb1f6df3b8c277ab6424d1befd Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 01:22:24 +0000 Subject: docs: better explain ags --- docs/default.nix | 19 +- docs/guide/ags/binding.md | 234 ------------ docs/guide/ags/cli-app.md | 167 --------- docs/guide/ags/faq.md | 266 -------------- docs/guide/ags/first-widgets.md | 402 --------------------- docs/guide/ags/gobject.md | 166 --------- docs/guide/ags/installation.md | 65 ---- docs/guide/ags/theming.md | 161 --------- docs/guide/ags/utilities.md | 174 --------- docs/guide/ags/variable.md | 153 -------- docs/guide/ags/widget.md | 208 ----------- docs/guide/getting-started/installation.md | 14 +- docs/guide/getting-started/introduction.md | 19 +- docs/guide/getting-started/nix.md | 20 +- docs/guide/getting-started/supported-languages.md | 25 +- docs/guide/lua/first-widgets.md | 3 + docs/guide/lua/installation.md | 3 + docs/guide/typescript/binding.md | 235 ++++++++++++ docs/guide/typescript/cli-app.md | 174 +++++++++ docs/guide/typescript/faq.md | 266 ++++++++++++++ docs/guide/typescript/first-widgets.md | 412 ++++++++++++++++++++++ docs/guide/typescript/gobject.md | 165 +++++++++ docs/guide/typescript/installation.md | 89 +++++ docs/guide/typescript/theming.md | 179 ++++++++++ docs/guide/typescript/utilities.md | 168 +++++++++ docs/guide/typescript/variable.md | 153 ++++++++ docs/guide/typescript/widget.md | 214 +++++++++++ docs/index.md | 7 +- docs/result-dev | 1 + docs/vitepress.config.ts | 17 +- lang/gjs/meson.build | 9 + 31 files changed, 2156 insertions(+), 2032 deletions(-) delete mode 100644 docs/guide/ags/binding.md delete mode 100644 docs/guide/ags/cli-app.md delete mode 100644 docs/guide/ags/faq.md delete mode 100644 docs/guide/ags/first-widgets.md delete mode 100644 docs/guide/ags/gobject.md delete mode 100644 docs/guide/ags/installation.md delete mode 100644 docs/guide/ags/theming.md delete mode 100644 docs/guide/ags/utilities.md delete mode 100644 docs/guide/ags/variable.md delete mode 100644 docs/guide/ags/widget.md create mode 100644 docs/guide/lua/first-widgets.md create mode 100644 docs/guide/lua/installation.md create mode 100644 docs/guide/typescript/binding.md create mode 100644 docs/guide/typescript/cli-app.md create mode 100644 docs/guide/typescript/faq.md create mode 100644 docs/guide/typescript/first-widgets.md create mode 100644 docs/guide/typescript/gobject.md create mode 100644 docs/guide/typescript/installation.md create mode 100644 docs/guide/typescript/theming.md create mode 100644 docs/guide/typescript/utilities.md create mode 100644 docs/guide/typescript/variable.md create mode 100644 docs/guide/typescript/widget.md create mode 120000 docs/result-dev create mode 100644 lang/gjs/meson.build diff --git a/docs/default.nix b/docs/default.nix index 3a75834..3d91a63 100644 --- a/docs/default.nix +++ b/docs/default.nix @@ -10,6 +10,7 @@ genRefForPkg = { name, pkg, + version ? "0.1", outPath, metaData, }: let @@ -18,13 +19,14 @@ in '' mkdir -p $out/${outPath} cat ${urlmap} > urlmap.js - gi-docgen generate -C ${data} ${output}/share/gir-1.0/${name}-0.1.gir - cp -r ${name}-0.1/* $out/${outPath} + gi-docgen generate -C ${data} ${output}/share/gir-1.0/${name}-${version}.gir + cp -r ${name}-${version}/* $out/${outPath} ''; genLib = name: namespace: { description, version, + api-ver ? "0.1", authors ? "Aylur", dependencies ? {}, out ? "libastal/${name}", @@ -33,6 +35,7 @@ name = "Astal${namespace}"; pkg = name; outPath = out; + version = api-ver; metaData = { library = { inherit description authors; @@ -106,10 +109,14 @@ in installPhase = '' runHook preInstall - ${genLib "astal" "" { - out = "libastal"; - description = "Astal core library"; - version = ../core/version; + ${genLib "io" "IO" { + description = "Astal IO library"; + version = ../lib/astal/io/version; + }} + ${genLib "astal3" "" { + api-ver = "3.0"; + description = "Astal GTK3 library"; + version = ../lib/astal/gtk3/version; dependencies = {inherit (dependency) "Gtk-3.0";}; }} ${genLib "apps" "Apps" { diff --git a/docs/guide/ags/binding.md b/docs/guide/ags/binding.md deleted file mode 100644 index f1592a0..0000000 --- a/docs/guide/ags/binding.md +++ /dev/null @@ -1,234 +0,0 @@ -# Binding - -As mentioned before binding an object's state to another - -so in most cases a `Variable` or a `GObject.Object` property to a widget's property - -is done through the `bind` function which returns a `Binding` object. - -`Binding` objects simply hold information about the source and how it should be transformed -which Widget constructors can use to setup a connection between themselves and the source. - -```ts -class Binding { - private transformFn: (v: any) => unknown - private emitter: Subscribable | Connectable - private prop?: string - - as(fn: (v: Value) => T): Binding - get(): Value - subscribe(callback: (value: Value) => void): () => void -} -``` - -A `Binding` can be constructed from an object implementing -the `Subscribable` interface (usually a `Variable`) -or an object implementing the `Connectable` interface and one of its properties -(usually a `GObject.Object` instance). - -```ts -function bind(obj: Subscribable): Binding - -function bind< - Obj extends Connectable, - Prop extends keyof Obj, ->(obj: Obj, prop: Prop): Binding -``` - -## Subscribable and Connectable interface - -Any object implementing one of these interfaces can be used with `bind`. - -```ts -interface Subscribable { - subscribe(callback: (value: T) => void): () => void - get(): T -} - -interface Connectable { - connect(signal: string, callback: (...args: any[]) => unknown): number - disconnect(id: number): void -} -``` - -## Example Custom Subscribable - -When binding the children of a box from an array, usually not all elements -of the array changes each time, so it would make sense to not destroy -the widget which represents the element. - -::: code-group - -```ts :line-numbers [varmap.ts] -import { type Subscribable } from "astal/binding" -import { Gtk } from "astal" - -export class VarMap implements Subscribable { - #subs = new Set<(v: Array<[K, T]>) => void>() - #map: Map - - #notifiy() { - const value = this.get() - for (const sub of this.#subs) { - sub(value) - } - } - - #delete(key: K) { - const v = this.#map.get(key) - - if (v instanceof Gtk.Widget) { - v.destroy() - } - - this.#map.delete(key) - } - - constructor(initial?: Iterable<[K, T]>) { - this.#map = new Map(initial) - } - - set(key: K, value: T) { - this.#delete(key) - this.#map.set(key, value) - this.#notifiy() - } - - delete(key: K) { - this.#delete(key) - this.#notifiy() - } - - get() { - return [...this.#map.entries()] - } - - subscribe(callback: (v: Array<[K, T]>) => void) { - this.#subs.add(callback) - return () => this.#subs.delete(callback) - } -} -``` - -::: - -And this `VarMap` can be used as an alternative to `Variable>`. - -```tsx -function MappedBox() { - const map = new VarMap([ - [1, ] - [2, ] - ]) - - const conns = [ - gobject.connect("added", (_, id) => map.set(id, MyWidget({ id }))), - gobject.connect("removed", (_, id) => map.delete(id, MyWidget({ id }))), - ] - - return conns.map(id => gobject.disconnect(id))}> - {bind(map).as(arr => arr.sort(([a], [b]) => a - b).map(([,w]) => w))} - -} -``` - -## Example Custom Connectable - -This was formerly known as a "Service" in AGS. -Astal provides [decorator functions](./gobject#example-usage) that make it easy to subclass gobjects, however -you can read more about GObjects and subclassing on [gjs.guide](https://gjs.guide/guides/gobject/subclassing.html#gobject-subclassing). - -Objects coming from [libraries](../libraries/references#astal-libraries) -usually have a singleton gobject you can access with `.get_default()`. - -Here is an example of a Brightness library by wrapping the `brightnessctl` cli utility -and by monitoring `/sys/class/backlight` - -::: code-group - -```ts :line-numbers [brightness.ts] -import GObject, { register, property } from "astal/gobject" -import { monitorFile, readFileAsync } from "astal/file" -import { exec, execAsync } from "astal/process" - -const get = (args: string) => Number(exec(`brightnessctl ${args}`)) -const screen = exec(`bash -c "ls -w1 /sys/class/backlight | head -1"`) -const kbd = exec(`bash -c "ls -w1 /sys/class/leds | head -1"`) - -@register({ GTypeName: "Brightness" }) -export default class Brightness extends GObject.Object { - static instance: Brightness - static get_default() { - if (!this.instance) - this.instance = new Brightness() - - return this.instance - } - - #kbdMax = get(`--device ${kbd} max`) - #kbd = get(`--device ${kbd} get`) - #screenMax = get("max") - #screen = get("get") / (get("max") || 1) - - @property(Number) - get kbd() { return this.#kbd } - - set kbd(value) { - if (value < 0 || value > this.#kbdMax) - return - - execAsync(`brightnessctl -d ${kbd} s ${value} -q`).then(() => { - this.#kbd = value - this.notify("kbd") - }) - } - - @property(Number) - get screen() { return this.#screen } - - set screen(percent) { - if (percent < 0) - percent = 0 - - if (percent > 1) - percent = 1 - - execAsync(`brightnessctl set ${Math.floor(percent * 100)}% -q`).then(() => { - this.#screen = percent - this.notify("screen") - }) - } - - constructor() { - super() - - const screenPath = `/sys/class/backlight/${screen}/brightness` - const kbdPath = `/sys/class/leds/${kbd}/brightness` - - monitorFile(screenPath, async f => { - const v = await readFileAsync(f) - this.#screen = Number(v) / this.#screenMax - this.notify("screen") - }) - - monitorFile(kbdPath, async f => { - const v = await readFileAsync(f) - this.#kbd = Number(v) / this.#kbdMax - this.notify("kbd") - }) - } -} -``` - -::: - -And it can be used like any other library object. - -```tsx -function BrightnessSlider() { - const brightness = Brightness.get_default() - - return brightness.screen = value} - /> -} -``` diff --git a/docs/guide/ags/cli-app.md b/docs/guide/ags/cli-app.md deleted file mode 100644 index ec6d174..0000000 --- a/docs/guide/ags/cli-app.md +++ /dev/null @@ -1,167 +0,0 @@ -# CLI and App - -`App` is a singleton **instance** of [Astal.Application](https://aylur.github.io/libastal/class.Application.html). - -```ts -import { App } from "astal" -``` - -## Entry point - -:::code-group - -```ts [app.ts] -App.start({ - main() { - // setup anything - // instantiate widgets - }, -}) -``` - -::: - -:::warning -You can not instantiate widgets outside of the main function. -::: - -## Instance identifier - -You can run multiple instance by defining a unique instance name. - -```ts -App.start({ - instanceName: "my-instance", // defaults to "astal" - main() {}, -}) -``` - -## Messaging from CLI - -If you want to interact with an instance from the cli, you can do so by sending a message. - -```ts -App.start({ - main() {}, - requestHandler(request: string, res: (response: any) => void) { - if (request == "say hi") { - res("hi cli") - } - res("unknown command") - }, -}) -``` - -:::code-group - -```sh [ags] -ags -m "say hi" -# hi cli -``` - -```sh [astal] -astal say hi -# hi cli -``` - -::: - -If you want to run arbitrary JavaScript from cli, you can use `App.eval`. -It will evaluate the passed string as the body of an `async` function. - -```ts -App.start({ - main() {}, - requestHandler(js: string, res) { - App.eval(js).then(res).catch(res) - }, -}) -``` - -If the string does not contain a semicolon, a single expression is assumed and returned implicity. - -```sh -ags -m "'hello'" -# hello -``` - -If the string contains a semicolon, you have to return explicitly - -```sh -ags -m "'hello';" -# undefined - -ags -m "return 'hello';" -# hello -``` - -## Toggling Windows by their name - -In order for AGS to know about your windows, you have to register them. -You can do this by specifying a **unique** `name` and calling `App.add_window` - -```tsx {4} -import { App } from "astal" - -function Bar() { - return App.add_window(self)}> - - -} -``` - -You can also invoke `App.add_window` by simply passing the `App` to the `application` prop. - -```tsx {4} -import { App } from "astal" - -function Bar() { - return - - -} -``` - -:::warning -When assigning the `application` prop make sure `name` comes before. -Props are set sequentially and if name is applied after application it won't work. -::: - -```sh -ags -t Bar -``` - -## App without AGS - -As mentioned before AGS is only a scaffolding tool. You can setup -a dev environment and a bundler yourself. In which case you won't be using -the ags cli to run the bundled scripts. The produced script can run as the main instance -and a "client" instance. - -The first time you run your bundled script the `main` function gets executed. -While that instance is running any subsequent execution of the script will call -the `client` function. - -:::code-group - -```ts [main.ts] -App.start({ - // main instance - main(...args: Array) { - print(...args) - }, - - // every subsequent calls - client(message: (msg: string) => string, ...args: Array) { - const res = message("you can message the main instance") - console.log(res) - }, - - // this runs in the main instance - requestHandler(request: string, res: (response: any) => void) { - res("response from main") - }, -}) -``` - -::: diff --git a/docs/guide/ags/faq.md b/docs/guide/ags/faq.md deleted file mode 100644 index 76d8e72..0000000 --- a/docs/guide/ags/faq.md +++ /dev/null @@ -1,266 +0,0 @@ -# Frequently asked question, common issues, tips and tricks - -## Monitor id does not match compositor - -The monitor id property that windows expect is mapped by Gdk, which is not always -the same as the compositor. Instead use the `gdkmonitor` property which expects -a `Gdk.Monitor` object. - -```tsx -import { App } from "astal" - -function Bar(gdkmonitor) { - return -} - -function main() { - for (const monitor of App.get_monitors()) { - if (monitor.model == "your-desired-model") { - Bar(monitor) - } - } -} - -App.start({ main }) -``` - -## Environment variables - -JavaScript is **not** an bash. - -```ts -const HOME = exec("echo $HOME") // does not work -``` - -`exec` and `execAsync` runs the passed program as is, its **not** run in a -shell environment, so the above example just passes `$HOME` as a string literal -to the `echo` program. - -:::danger Please don't do this -You could pass it to bash, but that is a horrible approach. - -```ts -const HOME = exec("bash -c 'echo $HOME'") -``` - -::: - -You can read environment variables with [GLib.getenv](https://gjs-docs.gnome.org/glib20~2.0/glib.getenv). - -```ts -import GLib from "gi://GLib" - -const HOME = GLib.getenv("HOME") -``` - -## Custom SVG symbolic icons - -Put the svgs in a directory, named `-symbolic.svg` -and use `App.add_icons` or `icons` parameter in `App.start` - -:::code-group - -```ts [app.ts] -App.start({ - icons: `${SRC}/icons`, - main() { - Widget.Icon({ - icon: "custom-symbolic", // custom-symbolic.svg - css: "color: green;", // can be colored, like other named icons - }) - }, -}) -``` - -::: - -:::info -If there is a name clash with an icon from your current icon pack -the icon pack will take precedence -::: - -## Logging - -The `console` API in gjs uses glib logging functions. -If you just want to print some text as is to stdout -use the globally available `print` function or `printerr` for stderr. - -```ts -print("print this line to stdout") -printerr("print this line to stderr") -``` - -## Populate the global scope with frequently accessed variables - -It might be annoying to always import Gtk only for `Gtk.Align` enums. - -:::code-group - -```ts [globals.ts] -import Gtk from "gi://Gtk" - -declare global { - const START: number - const CENTER: number - const END: number - const FILL: number -} - -Object.assign(globalThis, { - START: Gtk.Align.START, - CENTER: Gtk.Align.CENTER, - END: Gtk.Align.END, - FILL: Gtk.Align.FILL, -}) -``` - -::: - -:::code-group - -```tsx [Bar.tsx] -export default function Bar() { - return - - -} -``` - -::: - -:::code-group - -```ts [app.ts] -import "./globals" -import Bar from "./Bar" - -App.start({ - main: Bar -}) -``` - -::: - -:::info -It is considered bad practice to populate the global scope, but its your code, not a public library. -::: - -## Auto create Window for each Monitor - -To have Window widgets appear on a monitor when its plugged in, listen to `App.monitor_added`. - -:::code-group - -```tsx [Bar.tsx] -export default function Bar(gdkmonitor: Gdk.Monitor) { - return -} -``` - -::: - -:::code-group - -```ts [app.ts] -import { Gdk, Gtk } from "astal" -import Bar from "./Bar" - -function main() { - const bars = new Map() - - // initialize - for (const gdkmonitor of App.get_monitors()) { - bars.set(gdkmonitor, Bar(gdkmonitor)) - } - - App.connect("monitor-added", (_, gdkmonitor) => { - bars.set(gdkmonitor, Bar(gdkmonitor)) - }) - - App.connect("monitor-removed", (_, gdkmonitor) => { - bars.get(gdkmonitor)?.destroy() - bars.delete(gdkmonitor) - }) -} - -App.start({ main }) -``` - -::: - -## Error: Can't convert non-null pointer to JS value - -These happen when accessing list type properties. Gjs fails to correctly bind -`List` and other array like types of Vala as a property. - -```ts -import Notifd from "gi://AstalNotifd" -const notifd = Notifd.get_default() - -notifd.notifications // ❌ // [!code error] - -notifd.get_notifications() // ✅ -``` - -## How to create regular floating windows - -Use `Gtk.Window` with [Widget.astalify](/guide/ags/widget#how-to-use-non-builtin-gtk-widgets). - -By default `Gtk.Window` is destroyed on close. To prevent this add a handler for `delete-event`. - -```tsx {4-7} -const RegularWindow = Widget.astalify(Gtk.Window) - -return { - self.hide() - return true - }} -> - {child} - -``` - -## Is there a way to limit the width/height of a widget? - -Unfortunately not. You can set a minimum size with `min-width` and `min-heigth` css attributes, -but you can not set max size. - -## Custom widgets with bindable properties - -In function components you can wrap any primitive to handle both -binding and value cases as one. - -```tsx -function MyWidget(props: { prop: string | Binding }) { - const prop = props.prop instanceof Binding - ? props.prop - : bind({ get: () => props.prop, subscribe: () => () => {} }) - - function setup(self: Widget.Box) { - self.hook(prop, () => { - const value = prop.get() - // handler - }) - } - - return - -} -``` - -You can pass the prop the super constructor in subclasses - -```tsx -@register() -class MyWidget extends Widget.Box { - @property(String) - set prop(v: string) { - // handler - } - - constructor(props: { prop: string | Binding }) { - super(props) - } -} -``` diff --git a/docs/guide/ags/first-widgets.md b/docs/guide/ags/first-widgets.md deleted file mode 100644 index 6dba7b3..0000000 --- a/docs/guide/ags/first-widgets.md +++ /dev/null @@ -1,402 +0,0 @@ -# First Widgets - -AGS is the predecessor of Astal, which was written purely in TypeScript and so only supported -JavaScript/TypeScript. Now it serves as a scaffolding tool for Astal projects in TypeScript. -While what made AGS what it is, is now part of the Astal project, for simplicity we will -refer to the Astal TypeScript lib as AGS. - -:::tip -If you are not familiar with the JavaScript syntax [MDN](https://developer.mozilla.org/en-US/) -and [javascript.info](https://javascript.info/) have great references. -::: - -## Getting Started - -Start by initializing a project - -```sh -ags --init -``` - -then run `ags` in the terminal - -```sh -ags -``` - -Done! You have now a custom written bar using Gtk. - -:::tip -AGS will transpile every `.ts`, `.jsx` and `.tsx` files into regular javascript then -it will bundle everything into a single javascript file which then GJS can execute. -The bundler used is [esbuild](https://esbuild.github.io/). -::: - -## Root of every shell component: Window - -Astal apps are composed of widgets. A widget is a piece of UI that has its own logic and style. -A widget can be as small as a button or an entire bar. -The top level widget is always a [Window](https://aylur.github.io/libastal/class.Window.html) which will hold all widgets. - -::: code-group - -```tsx [widget/Bar.tsx] -function Bar(monitor = 0) { - return - Content of the widget - -} -``` - -::: - -::: code-group - -```ts [app.ts] -import Bar from "./widget/Bar" - -App.start({ - main() { - Bar(0) - Bar(1) // instantiate for each monitor - }, -}) -``` - -::: - -## Creating and nesting widgets - -Widgets are JavaScript functions which return Gtk widgets, -either by using JSX or using a widget constructor. - -:::code-group - -```tsx [MyButton.tsx] -function MyButton(): JSX.Element { - return -} -``` - -```ts [MyButton.ts] -import { Widget } from "astal" - -function MyButton(): Widget.Button { - return new Widget.Button( - { onClicked: "echo hello" }, - new Widget.Label({ label: "Click me!" }), - ) -} -``` - -::: - -:::info -The only difference between the two is the return type. -Using markup the return type is always `Gtk.Widget` (globally available as `JSX.Element`), -while using constructors the return type is the actual type of the widget. -It is rare to need the actual return type, so most if not all of the time, you can use markup. -::: - -Now that you have declared `MyButton`, you can nest it into another component. - -```tsx -function MyBar() { - return - - Click The button - - - -} -``` - -Notice that widgets you defined start with a capital letter ``. -Lowercase tags are builtin widgets, while capital letter is for custom widgets. - -## Displaying Data - -JSX lets you put markup into JavaScript. -Curly braces let you “escape back” into JavaScript so that you can embed some variable -from your code and display it. - -```tsx -function MyWidget() { - const label = "hello" - - return -} -``` - -You can also pass JavaScript to markup attributes - -```tsx -function MyWidget() { - const label = "hello" - - return -} -``` - -## Builtin Widgets - -You can check the [source code](https://github.com/aylur/astal/blob/main/core/gjs/src/widgets.ts) to have a full list of builtin widgets. - -These widgets are available by default in JSX. - -- box: [Astal.Box](https://aylur.github.io/libastal/class.Box.html) -- button: [Astal.Button](https://aylur.github.io/libastal/class.Button.html) -- centerbox: [Astal.CenterBox](https://aylur.github.io/libastal/class.CenterBox.html) -- circularprogress: [Astal.CircularProgress](https://aylur.github.io/libastal/class.CircularProgress.html) -- drawingarea: [Gtk.DrawingArea](https://docs.gtk.org/gtk3/class.DrawingArea.html) -- entry: [Gtk.Entry](https://docs.gtk.org/gtk3/class.Entry.html) -- eventbox: [Astal.EventBox](https://aylur.github.io/libastal/class.EventBox.html) -- icon: [Astal.Icon](https://aylur.github.io/libastal/class.Icon.html) -- label: [Astal.Label](https://aylur.github.io/libastal/class.Label.html) -- levelbar: [Astal.LevelBar](https://aylur.github.io/libastal/class.LevelBar.html) -- overlay: [Astal.Overlay](https://aylur.github.io/libastal/class.Overlay.html) -- revealer: [Gtk.Revealer](https://docs.gtk.org/gtk3/class.Revealer.html) -- scrollable: [Astal.Scrollable](https://aylur.github.io/libastal/class.Scrollable.html) -- slider: [Astal.Slider](https://aylur.github.io/libastal/class.Slider.html) -- stack: [Astal.Stack](https://aylur.github.io/libastal/class.Stack.html) -- switch: [Gtk.Switch](https://docs.gtk.org/gtk3/class.Switch.html) -- window: [Astal.Window](https://aylur.github.io/libastal/class.Window.html) diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md index af82cf5..6ad951a 100644 --- a/docs/guide/getting-started/installation.md +++ b/docs/guide/getting-started/installation.md @@ -10,6 +10,8 @@ Read more about it on the [nix page](./nix#astal) maintainer: [@kotontrion](https://github.com/kotontrion) + + :::code-group ```sh [Core Library] @@ -28,7 +30,6 @@ yay -S libastal-meta ```sh git clone https://github.com/aylur/astal.git -cd astal/core ``` 2. Install the following dependencies @@ -51,7 +52,18 @@ sudo apt install meson valac libgtk-3-dev libgtk-layer-shell-dev gobject-introsp 3. Build and install with `meson` +- astal-io + +```sh +cd lib/astal/io +meson setup build +meson install -C build +``` + +- astal3 + ```sh +cd lib/astal/gtk3 meson setup build meson install -C build ``` diff --git a/docs/guide/getting-started/introduction.md b/docs/guide/getting-started/introduction.md index 92d2ff1..af176c3 100644 --- a/docs/guide/getting-started/introduction.md +++ b/docs/guide/getting-started/introduction.md @@ -2,22 +2,25 @@ ## What is Astal? -Astal (_meaning "desk"_) is a bundle of libraries built using [GLib](https://docs.gtk.org/glib/) in Vala and C. -The core library [libastal](https://aylur.github.io/libastal) has some Gtk widgets that come packaged, -the most important one is the [Window](https://aylur.github.io/libastal/class.Window.html) which is the main toplevel component using [gtk-layer-shell](https://github.com/wmww/gtk-layer-shell). +Astal (_meaning "desk"_) is a suite of libraries in Vala and C. +The core library [astal3](https://aylur.github.io/libastal/astal3) and +[astal4](https://aylur.github.io/libastal/astal4) (not yet available) +has some Gtk widgets that come packaged, +the most important one being the [Window](https://aylur.github.io/libastal/class.Window.html) which is the main toplevel component using [gtk-layer-shell](https://github.com/wmww/gtk-layer-shell). This is what allows us to use Gtk as shell components on Wayland. -libastal also comes with some utility functions such as running external processes, -reading, writing and monitoring files. +The other part of the core library [astal-io](https://aylur.github.io/libastal/astal-io) +which contains some utility GLib shortcut for running external processes, +reading, writing and monitoring files, timeout and interval functions. ## Why Astal? -What makes Astal convenient to use is not the core library, as it could easily be replaced +What makes Astal convenient to use is not the core libraries, as they can easily be replaced by the standard library of any of your favorite language that has bindings to Gtk, it is the -accompanying libraries (_formerly known as "services" in AGS_) +accompanying libraries (_formerly known as "services" in AGS_). Have you ever wanted to write a custom bar, custom notification popups or an applauncher, but gave up because writing a workspace widget, implementing the notification daemon or handling a search filter was too much of a hassle? -Astal libraries have you [covered](/guide/libraries/references), you don't have to worry about these, +Astal libraries have you [covered](../libraries/references#astal-libraries), you don't have to worry about these, you just define the layout, style it with CSS and that's it. diff --git a/docs/guide/getting-started/nix.md b/docs/guide/getting-started/nix.md index 81f4e4d..5c0db28 100644 --- a/docs/guide/getting-started/nix.md +++ b/docs/guide/getting-started/nix.md @@ -1,3 +1,8 @@ +--- +next: + link: '/guide/getting-started/supported-languages' + text: 'Supported Languages' +--- # Nix ## Astal @@ -74,11 +79,16 @@ Using Astal on Nix will require you to package your project. } ``` +```nix [ TypeScript] +# The usage of AGS (read below) is recommended +# Usage without AGS is not yet documented +``` + ::: ## AGS -The recommended way to use AGS on NixOS is through the home-manager module. +The recommended way to use [AGS](../ags/first-widgets#first-widgets) on NixOS is through the home-manager module. Example content of a `flake.nix` file that contains your `homeConfigurations`. @@ -144,7 +154,7 @@ Example content of `home.nix` file ::: -AGS by default only includes the core `libastal` library. +AGS by default only includes the core `astal3/astal4` and `astal-io` libraries. If you want to include any other [library](../libraries/references) you have to add them to `extraPackages`. You can also add binaries which will be added to the gjs runtime. @@ -158,7 +168,11 @@ The AGS flake does not expose the `astal` cli to the home environment, you have :::code-group ```nix [ home.nix] -home.packages = [ inputs.ags.packages.${pkgs.system}.astal ]; +home.packages = [ inputs.ags.packages.${pkgs.system}.default ]; +``` + +```sh [ sh] +astal --help ``` ::: diff --git a/docs/guide/getting-started/supported-languages.md b/docs/guide/getting-started/supported-languages.md index 4cb7f3a..47d5dbd 100644 --- a/docs/guide/getting-started/supported-languages.md +++ b/docs/guide/getting-started/supported-languages.md @@ -1,9 +1,15 @@ # Supported Languages +There are currently two languages that have an additional +Astal package: Lua and Gjs. Their purpose is to abstract away +Gtk by implementing a state management and UI declaring solution. + ## JavaScript -The main intended usage of Astal is in TypeScript with [AGS](/guide/ags/first-widgets). -It supports JSX and has a state management solution similar to web frameworks. +The main intended usage of Astal is in TypeScript+JSX. +It is recommended to use [AGS](/guide/ags/first-widgets) to scaffold and run projects in TypeScript. +However, if you are familiar with JavaScript's tooling +ecosystem you can also setup an environment yourself. Only a minimal knowledge of JavaScript's syntax is needed to get started. :::info @@ -17,11 +23,7 @@ Examples: ## Lua -Similar to how there is a [TypeScript](https://github.com/Aylur/astal/tree/main/core/gjs) lib for GJS, there is also an accompanying library for [Lua](https://github.com/Aylur/astal/tree/main/core/lua). - -Unfortunately, I have encountered very heavy [performance issues](https://github.com/aylur/astal) with [lgi](https://github.com/lgi-devs/lgi), -and so currently I don't recommend using Lua for full desktop shells, but only for static -components that don't render child nodes dynamically, bars and panels for example. +Lua is well-supported, but I would still recommend TypeScript, as Lua lacks a type system, which in turn limits editor support. Examples: @@ -30,10 +32,9 @@ Examples: ## Python -There was a WIP [library for python](https://github.com/aylur/astal/tree/feat/python), to make it behave similar to the above two -but I don't plan on finishing it, because I'm not a fan of python. -If you are interested in picking it up, feel free to open a PR. -Nonetheless you can still use python the OOP way [pygobject](https://pygobject.gnome.org/tutorials/gobject/subclassing.html) intended it. +There is a WIP [package for python](https://github.com/aylur/astal/tree/feat/python), +to bring declaritivity to Python similar to the above two languages. +However, you can still use python the OOP way [pygobject](https://pygobject.gnome.org/tutorials/gobject/subclassing.html) intended it in the meantime. Examples: @@ -44,7 +45,7 @@ Examples: Vala is a language that simply put uses C# syntax and compiles to C. It is the language most of Astal is written in. I would still recommend -using TypeScript or Lua over Vala as they don't need a build step. +using TypeScript or Lua over Vala as they are simpler to work with. Examples: diff --git a/docs/guide/lua/first-widgets.md b/docs/guide/lua/first-widgets.md new file mode 100644 index 0000000..2abe7c5 --- /dev/null +++ b/docs/guide/lua/first-widgets.md @@ -0,0 +1,3 @@ +# First Widgets + +🚧 Lua documentation is in Progress 🚧 diff --git a/docs/guide/lua/installation.md b/docs/guide/lua/installation.md new file mode 100644 index 0000000..48241f9 --- /dev/null +++ b/docs/guide/lua/installation.md @@ -0,0 +1,3 @@ +# Installation + +🚧 Lua documentation is in Progress 🚧 diff --git a/docs/guide/typescript/binding.md b/docs/guide/typescript/binding.md new file mode 100644 index 0000000..05645ab --- /dev/null +++ b/docs/guide/typescript/binding.md @@ -0,0 +1,235 @@ +# Binding + +As mentioned before binding an object's state to another - +so in most cases a `Variable` or a `GObject.Object` property to a widget's property - +is done through the `bind` function which returns a `Binding` object. + +`Binding` objects simply hold information about the source and how it should be transformed +which Widget constructors can use to setup a connection between themselves and the source. + +```ts +class Binding { + private transformFn: (v: any) => unknown + private emitter: Subscribable | Connectable + private prop?: string + + as(fn: (v: Value) => T): Binding + get(): Value + subscribe(callback: (value: Value) => void): () => void +} +``` + +A `Binding` can be constructed from an object implementing +the `Subscribable` interface (usually a `Variable`) +or an object implementing the `Connectable` interface and one of its properties +(usually a `GObject.Object` instance). + +```ts +function bind(obj: Subscribable): Binding + +function bind< + Obj extends Connectable, + Prop extends keyof Obj, +>(obj: Obj, prop: Prop): Binding +``` + +## Subscribable and Connectable interface + +Any object implementing one of these interfaces can be used with `bind`. + +```ts +interface Subscribable { + subscribe(callback: (value: T) => void): () => void + get(): T +} + +interface Connectable { + connect(signal: string, callback: (...args: any[]) => unknown): number + disconnect(id: number): void +} +``` + +## Example Custom Subscribable + +When binding the children of a box from an array, usually not all elements +of the array changes each time, so it would make sense to not destroy +the widget which represents the element. + +::: code-group + +```ts :line-numbers [varmap.ts] +import { type Subscribable } from "astal/binding" +import { Gtk } from "astal" + +export class VarMap implements Subscribable { + #subs = new Set<(v: Array<[K, T]>) => void>() + #map: Map + + #notifiy() { + const value = this.get() + for (const sub of this.#subs) { + sub(value) + } + } + + #delete(key: K) { + const v = this.#map.get(key) + + if (v instanceof Gtk.Widget) { + v.destroy() + } + + this.#map.delete(key) + } + + constructor(initial?: Iterable<[K, T]>) { + this.#map = new Map(initial) + } + + set(key: K, value: T) { + this.#delete(key) + this.#map.set(key, value) + this.#notifiy() + } + + delete(key: K) { + this.#delete(key) + this.#notifiy() + } + + get() { + return [...this.#map.entries()] + } + + subscribe(callback: (v: Array<[K, T]>) => void) { + this.#subs.add(callback) + return () => this.#subs.delete(callback) + } +} +``` + +::: + +And this `VarMap` can be used as an alternative to `Variable>`. + +```tsx +function MappedBox() { + const map = new VarMap([ + [1, ] + [2, ] + ]) + + const conns = [ + gobject.connect("added", (_, id) => map.set(id, MyWidget({ id }))), + gobject.connect("removed", (_, id) => map.delete(id, MyWidget({ id }))), + ] + + return conns.map(id => gobject.disconnect(id))}> + {bind(map).as(arr => arr.sort(([a], [b]) => a - b).map(([,w]) => w))} + +} +``` + +## Example Custom Connectable + +Astal provides [decorator functions](./gobject#example-usage) +that make it easy to subclass gobjects, however +you can read more about GObjects and subclassing +on [gjs.guide](https://gjs.guide/guides/gobject/subclassing.html#gobject-subclassing). + +Objects coming from [libraries](../libraries/references#astal-libraries) +usually have a singleton gobject you can access with `.get_default()`. + +Here is an example of a Brightness library by wrapping the `brightnessctl` cli utility +and by monitoring `/sys/class/backlight` + +::: code-group + +```ts :line-numbers [brightness.ts] +import GObject, { register, property } from "astal/gobject" +import { monitorFile, readFileAsync } from "astal/file" +import { exec, execAsync } from "astal/process" + +const get = (args: string) => Number(exec(`brightnessctl ${args}`)) +const screen = exec(`bash -c "ls -w1 /sys/class/backlight | head -1"`) +const kbd = exec(`bash -c "ls -w1 /sys/class/leds | head -1"`) + +@register({ GTypeName: "Brightness" }) +export default class Brightness extends GObject.Object { + static instance: Brightness + static get_default() { + if (!this.instance) + this.instance = new Brightness() + + return this.instance + } + + #kbdMax = get(`--device ${kbd} max`) + #kbd = get(`--device ${kbd} get`) + #screenMax = get("max") + #screen = get("get") / (get("max") || 1) + + @property(Number) + get kbd() { return this.#kbd } + + set kbd(value) { + if (value < 0 || value > this.#kbdMax) + return + + execAsync(`brightnessctl -d ${kbd} s ${value} -q`).then(() => { + this.#kbd = value + this.notify("kbd") + }) + } + + @property(Number) + get screen() { return this.#screen } + + set screen(percent) { + if (percent < 0) + percent = 0 + + if (percent > 1) + percent = 1 + + execAsync(`brightnessctl set ${Math.floor(percent * 100)}% -q`).then(() => { + this.#screen = percent + this.notify("screen") + }) + } + + constructor() { + super() + + const screenPath = `/sys/class/backlight/${screen}/brightness` + const kbdPath = `/sys/class/leds/${kbd}/brightness` + + monitorFile(screenPath, async f => { + const v = await readFileAsync(f) + this.#screen = Number(v) / this.#screenMax + this.notify("screen") + }) + + monitorFile(kbdPath, async f => { + const v = await readFileAsync(f) + this.#kbd = Number(v) / this.#kbdMax + this.notify("kbd") + }) + } +} +``` + +::: + +And it can be used like any other library object. + +```tsx +function BrightnessSlider() { + const brightness = Brightness.get_default() + + return brightness.screen = value} + /> +} +``` diff --git a/docs/guide/typescript/cli-app.md b/docs/guide/typescript/cli-app.md new file mode 100644 index 0000000..3407e06 --- /dev/null +++ b/docs/guide/typescript/cli-app.md @@ -0,0 +1,174 @@ +# CLI and App + +`App` is a singleton **instance** of an [Astal.Application](https://aylur.github.io/libastal/class.Application.html). + +Depending on gtk version import paths will differ + +```ts +import { App } from "astal/gtk3" + +import { App } from "astal/gtk4" +``` + +## Entry point + +:::code-group + +```ts [app.ts] +App.start({ + main() { + // setup anything + // instantiate widgets + }, +}) +``` + +::: + +## Instance identifier + +You can run multiple instance by defining a unique instance name. + +```ts +App.start({ + instanceName: "my-instance", // defaults to "astal" + main() { }, +}) +``` + +## Messaging from CLI + +If you want to interact with an instance from the CLI, +you can do so by sending a message. + +```ts +App.start({ + requestHandler(request: string, res: (response: any) => void) { + if (request == "say hi") { + res("hi cli") + } + res("unknown command") + }, + main() { }, +}) +``` + +:::code-group + +```sh [astal] +astal say hi +# hi cli +``` + +```sh [ags] +ags -m "say hi" +# hi cli +``` + +::: + +If you want to run arbitrary JavaScript from CLI, you can use `App.eval` +which will evaluate the passed string as the body of an `async` function. + +```ts +App.start({ + main() {}, + requestHandler(js, res) { + App.eval(js).then(res).catch(res) + }, +}) +``` + +If the string does not contain a semicolon, a single expression is assumed and returned implicity. + +```sh +astal "'hello'" +# hello +``` + +If the string contains a semicolon, you have to return explicitly + +```sh +astal "'hello';" +# undefined + +astal "return 'hello';" +# hello +``` + +## Toggling Windows by their name + +In order for Astal to know about your windows, you have to register them. +You can do this by specifying a **unique** `name` and calling `App.add_window` + +```tsx {4} +import { App } from "astal" + +function Bar() { + return App.add_window(self)}> + + +} +``` + +You can also invoke `App.add_window` by simply passing the `App` to the `application` prop. + +```tsx {4} +import { App } from "astal" + +function Bar() { + return + + +} +``` + +:::warning +When assigning the `application` prop make sure `name` comes before. +Props are set sequentially and if name is applied after application it won't work. +::: + +:::code-group + +```sh [astal] +astal -t Bar +``` + +```sh [ags] +ags -t Bar +``` + +::: + +## Bundled scripts + +The produced scripts when bundling can run as the main instance +and a "client" instance. + +The first time you execute your bundled script the `main` function gets called. +While that instance is running any subsequent execution of the script will call +the `client` function. + +:::code-group + +```ts [main.ts] +App.start({ + // main instance + main(...args: Array) { + print(...args) + }, + + // every subsequent calls + client(message: (msg: string) => string, ...args: Array) { + const res = message("you can message the main instance") + console.log(res) + }, + + // this runs in the main instance + requestHandler(request: string, res: (response: any) => void) { + res("response from main") + }, +}) +``` + +::: diff --git a/docs/guide/typescript/faq.md b/docs/guide/typescript/faq.md new file mode 100644 index 0000000..76d8e72 --- /dev/null +++ b/docs/guide/typescript/faq.md @@ -0,0 +1,266 @@ +# Frequently asked question, common issues, tips and tricks + +## Monitor id does not match compositor + +The monitor id property that windows expect is mapped by Gdk, which is not always +the same as the compositor. Instead use the `gdkmonitor` property which expects +a `Gdk.Monitor` object. + +```tsx +import { App } from "astal" + +function Bar(gdkmonitor) { + return +} + +function main() { + for (const monitor of App.get_monitors()) { + if (monitor.model == "your-desired-model") { + Bar(monitor) + } + } +} + +App.start({ main }) +``` + +## Environment variables + +JavaScript is **not** an bash. + +```ts +const HOME = exec("echo $HOME") // does not work +``` + +`exec` and `execAsync` runs the passed program as is, its **not** run in a +shell environment, so the above example just passes `$HOME` as a string literal +to the `echo` program. + +:::danger Please don't do this +You could pass it to bash, but that is a horrible approach. + +```ts +const HOME = exec("bash -c 'echo $HOME'") +``` + +::: + +You can read environment variables with [GLib.getenv](https://gjs-docs.gnome.org/glib20~2.0/glib.getenv). + +```ts +import GLib from "gi://GLib" + +const HOME = GLib.getenv("HOME") +``` + +## Custom SVG symbolic icons + +Put the svgs in a directory, named `-symbolic.svg` +and use `App.add_icons` or `icons` parameter in `App.start` + +:::code-group + +```ts [app.ts] +App.start({ + icons: `${SRC}/icons`, + main() { + Widget.Icon({ + icon: "custom-symbolic", // custom-symbolic.svg + css: "color: green;", // can be colored, like other named icons + }) + }, +}) +``` + +::: + +:::info +If there is a name clash with an icon from your current icon pack +the icon pack will take precedence +::: + +## Logging + +The `console` API in gjs uses glib logging functions. +If you just want to print some text as is to stdout +use the globally available `print` function or `printerr` for stderr. + +```ts +print("print this line to stdout") +printerr("print this line to stderr") +``` + +## Populate the global scope with frequently accessed variables + +It might be annoying to always import Gtk only for `Gtk.Align` enums. + +:::code-group + +```ts [globals.ts] +import Gtk from "gi://Gtk" + +declare global { + const START: number + const CENTER: number + const END: number + const FILL: number +} + +Object.assign(globalThis, { + START: Gtk.Align.START, + CENTER: Gtk.Align.CENTER, + END: Gtk.Align.END, + FILL: Gtk.Align.FILL, +}) +``` + +::: + +:::code-group + +```tsx [Bar.tsx] +export default function Bar() { + return + + +} +``` + +::: + +:::code-group + +```ts [app.ts] +import "./globals" +import Bar from "./Bar" + +App.start({ + main: Bar +}) +``` + +::: + +:::info +It is considered bad practice to populate the global scope, but its your code, not a public library. +::: + +## Auto create Window for each Monitor + +To have Window widgets appear on a monitor when its plugged in, listen to `App.monitor_added`. + +:::code-group + +```tsx [Bar.tsx] +export default function Bar(gdkmonitor: Gdk.Monitor) { + return +} +``` + +::: + +:::code-group + +```ts [app.ts] +import { Gdk, Gtk } from "astal" +import Bar from "./Bar" + +function main() { + const bars = new Map() + + // initialize + for (const gdkmonitor of App.get_monitors()) { + bars.set(gdkmonitor, Bar(gdkmonitor)) + } + + App.connect("monitor-added", (_, gdkmonitor) => { + bars.set(gdkmonitor, Bar(gdkmonitor)) + }) + + App.connect("monitor-removed", (_, gdkmonitor) => { + bars.get(gdkmonitor)?.destroy() + bars.delete(gdkmonitor) + }) +} + +App.start({ main }) +``` + +::: + +## Error: Can't convert non-null pointer to JS value + +These happen when accessing list type properties. Gjs fails to correctly bind +`List` and other array like types of Vala as a property. + +```ts +import Notifd from "gi://AstalNotifd" +const notifd = Notifd.get_default() + +notifd.notifications // ❌ // [!code error] + +notifd.get_notifications() // ✅ +``` + +## How to create regular floating windows + +Use `Gtk.Window` with [Widget.astalify](/guide/ags/widget#how-to-use-non-builtin-gtk-widgets). + +By default `Gtk.Window` is destroyed on close. To prevent this add a handler for `delete-event`. + +```tsx {4-7} +const RegularWindow = Widget.astalify(Gtk.Window) + +return { + self.hide() + return true + }} +> + {child} + +``` + +## Is there a way to limit the width/height of a widget? + +Unfortunately not. You can set a minimum size with `min-width` and `min-heigth` css attributes, +but you can not set max size. + +## Custom widgets with bindable properties + +In function components you can wrap any primitive to handle both +binding and value cases as one. + +```tsx +function MyWidget(props: { prop: string | Binding }) { + const prop = props.prop instanceof Binding + ? props.prop + : bind({ get: () => props.prop, subscribe: () => () => {} }) + + function setup(self: Widget.Box) { + self.hook(prop, () => { + const value = prop.get() + // handler + }) + } + + return + +} +``` + +You can pass the prop the super constructor in subclasses + +```tsx +@register() +class MyWidget extends Widget.Box { + @property(String) + set prop(v: string) { + // handler + } + + constructor(props: { prop: string | Binding }) { + super(props) + } +} +``` diff --git a/docs/guide/typescript/first-widgets.md b/docs/guide/typescript/first-widgets.md new file mode 100644 index 0000000..3664bfa --- /dev/null +++ b/docs/guide/typescript/first-widgets.md @@ -0,0 +1,412 @@ +# First Widgets + +## Getting Started + +Start by initializing a project + +```sh +ags --init +``` + +then run `ags` in the terminal + +```sh +ags +``` + +:::details Usage without AGS +🚧 Not yet documented. 🚧 +::: + +That's it! You have now a custom written bar using Gtk. + +:::tip +AGS will transpile every `.ts`, `.jsx` and `.tsx` files into regular JavaScript, then +it will bundle everything into a single JavaScript file which then GJS can execute. +::: + +The AGS init command will generate the following files + +```txt +. +├── @girs/ # generated types +├── widget/ +│ └── Bar.tsx +├── app.ts # entry proint +├── env.d.ts # additional types +├── style.css +└── tsconfig.json # needed by LSPs +``` + +## Root of every shell component: Window + +Astal apps are composed of widgets. A widget is a piece of UI that has its own logic and style. +A widget can be as small as a button or an entire bar. +The top level widget is always a [Window](https://aylur.github.io/libastal/class.Window.html) which will hold all widgets. + +::: code-group + +```tsx [widget/Bar.tsx] +function Bar(monitor = 0) { + return + Content of the widget + +} +``` + +::: + +::: code-group + +```ts [app.ts] +import Bar from "./widget/Bar" + +App.start({ + main() { + Bar(0) + Bar(1) // instantiate for each monitor + }, +}) +``` + +::: + +## Creating and nesting widgets + +Widgets are JavaScript functions which return Gtk widgets, +either by using JSX or using a widget constructor. + +:::code-group + +```tsx [MyButton.tsx] +function MyButton(): JSX.Element { + return +} +``` + +```ts [MyButton.ts] +import { Widget } from "astal/gtk3" + +function MyButton(): Widget.Button { + return new Widget.Button( + { onClicked: "echo hello" }, + new Widget.Label({ label: "Click me!" }), + ) +} +``` + +::: + +:::info +The only difference between the two is the return type. +Using markup the return type is always `Gtk.Widget` (globally available as `JSX.Element`), +while using constructors the return type is the actual type of the widget. +It is rare to need the actual return type, so most if not all of the time, you can use markup. +::: + +Now that you have declared `MyButton`, you can nest it into another component. + +```tsx +function MyBar() { + return + + Click The button + + + +} +``` + +Notice that widgets you defined start with a capital letter ``. +Lowercase tags are builtin widgets, while capital letter is for custom widgets. + +## Displaying Data + +JSX lets you put markup into JavaScript. +Curly braces let you “escape back” into JavaScript so that you can embed some variable +from your code and display it. + +```tsx +function MyWidget() { + const label = "hello" + + return +} +``` + +You can also pass JavaScript to markup attributes + +```tsx +function MyWidget() { + const label = "hello" + + return +} +``` + +### Builtin Widgets + +You can check the [source code](https://github.com/aylur/astal/blob/main/lang/gjs/src/gtk3/index.ts) to have a full list of builtin widgets. + +These widgets are available by default in JSX. + +- box: [Astal.Box](https://aylur.github.io/libastal/class.Box.html) +- button: [Astal.Button](https://aylur.github.io/libastal/class.Button.html) +- centerbox: [Astal.CenterBox](https://aylur.github.io/libastal/class.CenterBox.html) +- circularprogress: [Astal.CircularProgress](https://aylur.github.io/libastal/class.CircularProgress.html) +- drawingarea: [Gtk.DrawingArea](https://docs.gtk.org/gtk3/class.DrawingArea.html) +- entry: [Gtk.Entry](https://docs.gtk.org/gtk3/class.Entry.html) +- eventbox: [Astal.EventBox](https://aylur.github.io/libastal/class.EventBox.html) +- icon: [Astal.Icon](https://aylur.github.io/libastal/class.Icon.html) +- label: [Astal.Label](https://aylur.github.io/libastal/class.Label.html) +- levelbar: [Astal.LevelBar](https://aylur.github.io/libastal/class.LevelBar.html) +- overlay: [Astal.Overlay](https://aylur.github.io/libastal/class.Overlay.html) +- revealer: [Gtk.Revealer](https://docs.gtk.org/gtk3/class.Revealer.html) +- scrollable: [Astal.Scrollable](https://aylur.github.io/libastal/class.Scrollable.html) +- slider: [Astal.Slider](https://aylur.github.io/libastal/class.Slider.html) +- stack: [Astal.Stack](https://aylur.github.io/libastal/class.Stack.html) +- switch: [Gtk.Switch](https://docs.gtk.org/gtk3/class.Switch.html) +- window: [Astal.Window](https://aylur.github.io/libastal/class.Window.html) + +## Gtk4 + +🚧 Work in Progress 🚧 diff --git a/docs/index.md b/docs/index.md index f22ae19..8f5287b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,7 +5,7 @@ pageClass: home-page hero: name: "Astal" text: "Create Beautiful Widgets With Ease" - tagline: "The Framework to Craft Desktop Shells and beautiful functional Wayland Widgets with GTK!" + tagline: "The Linux Suite and Framework to Craft Desktop Shells and beautiful functional Wayland Widgets with GTK!" image: /icon.svg actions: - theme: brand @@ -21,10 +21,10 @@ hero: features: - title: Use Your Preferred Language icon: - details: The main focus of Astal is TypeScript using JSX. But you can use the libraries in any language that supports Gobject Introspection. + details: The main focus of Astal is TypeScript+JSX. But you can use the libraries in any language that supports Gobject Introspection. - title: No bash scripts needed icon: - details: Includes modules to work with Network, Bluetooth, Battery, Audio and more. + details: Includes modules to work with Network, Bluetooth, Battery, Audio and more. - title: Use any Gtk widget icon: details: With Astal you work with Gtk directly. You are not limited to only a set of them. @@ -56,6 +56,7 @@ features: .VPFeature a { font-weight: bold; + color: var(--vp-c-brand-2); } .VPFooter { diff --git a/docs/result-dev b/docs/result-dev new file mode 120000 index 0000000..52fdae0 --- /dev/null +++ b/docs/result-dev @@ -0,0 +1 @@ +/nix/store/080gmk15wbbjx258rxzsq19ykjdrvl39-astal-0.1.0-dev \ No newline at end of file diff --git a/docs/vitepress.config.ts b/docs/vitepress.config.ts index 47ed81e..843d3d3 100644 --- a/docs/vitepress.config.ts +++ b/docs/vitepress.config.ts @@ -59,8 +59,8 @@ export default defineConfig({ ], }, { - text: "AGS", - base: "/guide/ags", + text: "TypeScript", + base: "/guide/typescript", collapsed: false, items: [ { text: "Installation", link: "/installation" }, @@ -75,12 +75,23 @@ export default defineConfig({ { text: "FAQ", link: "/faq" }, ], }, + { + text: "Lua", + base: "/guide/lua", + collapsed: false, + items: [ + { text: "Installation", link: "/installation" }, + { text: "First Widgets", link: "/first-widgets" }, + ], + }, { text: "Libraries", collapsed: true, items: [ { text: "References", link: "/guide/libraries/references" }, - { text: "Astal", link: "https://aylur.github.io/libastal" }, + { text: "AstalIO", link: "https://aylur.github.io/libastal/astal-io" }, + { text: "Astal3", link: "https://aylur.github.io/libastal/astal3" }, + { text: "Astal4", link: "https://aylur.github.io/libastal/astal4" }, { text: "Apps", link: "/guide/libraries/apps" }, { text: "Auth", link: "/guide/libraries/auth" }, { text: "Battery", link: "/guide/libraries/battery" }, diff --git a/lang/gjs/meson.build b/lang/gjs/meson.build new file mode 100644 index 0000000..8f3058f --- /dev/null +++ b/lang/gjs/meson.build @@ -0,0 +1,9 @@ +project('astal-gjs') + +datadir = get_option('prefix') / get_option('datadir') +pkgdata = datadir / 'astal' / 'gjs' + +install_data('index.ts', install_dir: pkgdata) +install_subdir('lib', install_dir: pkgdata) +install_subdir('gtk3', install_dir: pkgdata) +install_subdir('gtk4', install_dir: pkgdata) -- cgit v1.2.3 From dde50e333a237efb4ddaf522398dedf1a69999d1 Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 11:47:26 +0000 Subject: docs: update references flake --- .gitignore | 1 + docs/default.nix | 135 +++++++++++++++++++++--------------- lib/astal/gtk3/src/application.vala | 9 +-- lib/astal/io/application.vala | 4 +- 4 files changed, 87 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index f047207..5459a17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build/ result +result-dev .cache/ test.sh tmp/ diff --git a/docs/default.nix b/docs/default.nix index 3d91a63..5a85fc3 100644 --- a/docs/default.nix +++ b/docs/default.nix @@ -7,50 +7,39 @@ toTOML = (pkgs.formats.toml {}).generate; - genRefForPkg = { - name, - pkg, - version ? "0.1", - outPath, - metaData, - }: let - data = toTOML name metaData; - output = self.packages.${pkgs.system}.${pkg}.dev; - in '' - mkdir -p $out/${outPath} - cat ${urlmap} > urlmap.js - gi-docgen generate -C ${data} ${output}/share/gir-1.0/${name}-${version}.gir - cp -r ${name}-${version}/* $out/${outPath} - ''; - - genLib = name: namespace: { - description, + genLib = { + flakepkg, + gir, version, + description, api-ver ? "0.1", authors ? "Aylur", dependencies ? {}, - out ? "libastal/${name}", - }: - genRefForPkg { - name = "Astal${namespace}"; - pkg = name; - outPath = out; - version = api-ver; - metaData = { - library = { - inherit description authors; - version = readVer version; - license = "LGPL-2.1"; - browse_url = "https://github.com/aylur/astal"; - repository_url = "https://github.com/aylur/aylur.git"; - website_url = "https://aylur.github.io/astal"; - dependencies = ["GObject-2.0"] ++ (builtins.attrNames dependencies); - }; + out ? "libastal/${flakepkg}", + }: let + name = "Astal${gir}-${api-ver}"; + src = self.packages.${pkgs.system}.${flakepkg}.dev; - extra.urlmap_file = "urlmap.js"; - dependencies = {inherit (dependency) "GObject-2.0";} // dependencies; + data = toTOML gir { + library = { + inherit description authors; + version = readVer version; + license = "LGPL-2.1"; + browse_url = "https://github.com/aylur/astal"; + repository_url = "https://github.com/aylur/aylur.git"; + website_url = "https://aylur.github.io/astal"; + dependencies = ["GObject-2.0"] ++ (builtins.attrNames dependencies); }; + + extra.urlmap_file = "urlmap.js"; + dependencies = {inherit (dependency) "GObject-2.0";} // dependencies; }; + in '' + mkdir -p $out/${out} + cat ${urlmap} > urlmap.js + gi-docgen generate -C ${data} ${src}/share/gir-1.0/${name}.gir + cp -r ${name}/* $out/${out} + ''; dependency = { "GObject-2.0" = { @@ -63,6 +52,11 @@ description = "The GTK toolkit"; docs_url = "https://docs.gtk.org/gtk3/"; }; + "AstalIO-0.1" = { + name = "AstalIO"; + description = "Astal Core library"; + docs_url = "https://aylur.github.io/libastal/io"; + }; "NM-1.0" = { name = "NetworkManager"; description = "The standard Linux network configuration tool suite"; @@ -83,6 +77,7 @@ ["Gdk" "https://docs.gtk.org/gdk3/"] ["Gtk" "https://docs.gtk.org/gtk3/"] ["GdkPixbuf" "https://docs.gtk.org/gdk-pixbuf/"] + ["AstalIO" "https://aylur.github.io/libastal/io"] # FIXME: these are not gi-docgen generated, therefore links are broken ["NM" "https://networkmanager.dev/docs/libnm/latest/"] @@ -91,7 +86,7 @@ ''; in pkgs.stdenvNoCC.mkDerivation { - name = "library-reference"; + name = "reference"; src = ./.; nativeBuildInputs = with pkgs; [ @@ -105,69 +100,99 @@ in libdbusmenu-gtk3 wireplumber networkmanager + self.packages.${system}.io ]; installPhase = '' runHook preInstall - ${genLib "io" "IO" { - description = "Astal IO library"; + ${genLib { + flakepkg = "io"; + gir = "IO"; + api-ver = "0.1"; + description = "Astal Core library"; version = ../lib/astal/io/version; }} - ${genLib "astal3" "" { + ${genLib { + flakepkg = "astal3"; + gir = ""; api-ver = "3.0"; - description = "Astal GTK3 library"; + description = "Astal GTK3 widget library"; version = ../lib/astal/gtk3/version; - dependencies = {inherit (dependency) "Gtk-3.0";}; + dependencies = {inherit (dependency) "AstalIO-0.1" "Gtk-3.0";}; }} - ${genLib "apps" "Apps" { + ${genLib { + flakepkg = "apps"; + gir = "Apps"; description = "Application query library"; version = ../lib/apps/version; }} - ${genLib "auth" "Auth" { + ${genLib { + flakepkg = "auth"; + gir = "Auth"; authors = "kotontrion"; description = "Authentication using pam"; version = ../lib/auth/version; }} - ${genLib "battery" "Battery" { + ${genLib { + flakepkg = "battery"; + gir = "Battery"; description = "DBus proxy for upowerd devices"; version = ../lib/battery/version; }} - ${genLib "bluetooth" "Bluetooth" { + ${genLib { + flakepkg = "bluetooth"; + gir = "Bluetooth"; description = "DBus proxy for bluez"; version = ../lib/bluetooth/version; }} - ${genLib "hyprland" "Hyprland" { + ${genLib { + flakepkg = "hyprland"; + gir = "Hyprland"; description = "IPC client for Hyprland"; version = ../lib/hyprland/version; }} - ${genLib "mpris" "Mpris" { + ${genLib { + flakepkg = "mpris"; + gir = "Mpris"; description = "Control mpris players"; version = ../lib/mpris/version; }} - ${genLib "network" "Network" { + ${genLib { + flakepkg = "network"; + gir = "Network"; description = "NetworkManager wrapper library"; version = ../lib/network/version; dependencies = {inherit (dependency) "NM-1.0";}; # FIXME: why does this not work? }} - ${genLib "notifd" "Notifd" { + ${genLib { + flakepkg = "notifd"; + gir = "Notifd"; description = "Notification daemon library"; version = ../lib/notifd/version; }} - ${genLib "powerprofiles" "PowerProfiles" { + ${genLib { + flakepkg = "powerprofiles"; + gir = "PowerProfiles"; description = "DBus proxy for upowerd profiles"; version = ../lib/powerprofiles/version; }} - ${genLib "river" "River" { + ${genLib { + flakepkg = "river"; + gir = "River"; description = "IPC client for River"; version = ../lib/river/version; authors = "kotontrion"; }} - ${genLib "tray" "Tray" { + ${genLib { + flakepkg = "tray"; + gir = "Tray"; description = "StatusNotifierItem implementation"; version = ../lib/tray/version; authors = "kotontrion"; }} - ${genLib "wireplumber" "Wp" { + ${genLib { + flakepkg = "wireplumber"; + gir = "Wp"; description = "Wrapper library over the wireplumber API"; version = ../lib/wireplumber/version; authors = "kotontrion"; diff --git a/lib/astal/gtk3/src/application.vala b/lib/astal/gtk3/src/application.vala index 2255333..1210d88 100644 --- a/lib/astal/gtk3/src/application.vala +++ b/lib/astal/gtk3/src/application.vala @@ -4,8 +4,7 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { private SocketService service; private DBusConnection conn; private string _instance_name = "astal"; - - public string socket_path { get; private set; } + private string socket_path { get; private set; } [DBus (visible=false)] public signal void monitor_added(Gdk.Monitor monitor); @@ -45,13 +44,11 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { get { return get_windows(); } } - [DBus (visible=false)] - public Gtk.Settings settings { + private Gtk.Settings settings { get { return Gtk.Settings.get_default(); } } - [DBus (visible=false)] - public Gdk.Screen screen { + private Gdk.Screen screen { get { return Gdk.Screen.get_default(); } } diff --git a/lib/astal/io/application.vala b/lib/astal/io/application.vala index 60318ed..b32de34 100644 --- a/lib/astal/io/application.vala +++ b/lib/astal/io/application.vala @@ -11,7 +11,9 @@ public interface Application : Object { public abstract string instance_name { owned get; construct set; } public abstract void acquire_socket() throws Error; - public abstract void request(string msg, SocketConnection conn) throws Error; + public virtual void request(string msg, SocketConnection conn) throws Error { + write_sock.begin(conn, @"missing response implementation on $instance_name"); + } } public SocketService acquire_socket(Application app, out string sock) throws Error { -- cgit v1.2.3 From fe11c037bad45697451b7264ff93fa37f1fac78f Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 11:55:53 +0000 Subject: docs: recommend /usr prefix by default --- docs/guide/getting-started/installation.md | 14 +------------- docs/guide/libraries/apps.md | 18 +----------------- docs/guide/libraries/auth.md | 13 +------------ docs/guide/libraries/battery.md | 13 +------------ docs/guide/libraries/bluetooth.md | 13 +------------ docs/guide/libraries/hyprland.md | 13 +------------ docs/guide/libraries/mpris.md | 13 +------------ docs/guide/libraries/network.md | 13 +------------ docs/guide/libraries/notifd.md | 13 +------------ docs/guide/libraries/powerprofiles.md | 13 +------------ docs/guide/libraries/references.md | 1 - docs/guide/libraries/river.md | 13 +------------ docs/guide/libraries/tray.md | 13 +------------ docs/guide/libraries/wireplumber.md | 13 +------------ docs/guide/typescript/installation.md | 4 ++-- docs/vitepress.config.ts | 4 ++-- 16 files changed, 17 insertions(+), 167 deletions(-) diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md index 6ad951a..96cbdfa 100644 --- a/docs/guide/getting-started/installation.md +++ b/docs/guide/getting-started/installation.md @@ -56,7 +56,7 @@ sudo apt install meson valac libgtk-3-dev libgtk-layer-shell-dev gobject-introsp ```sh cd lib/astal/io -meson setup build +meson setup --prefix /usr build meson install -C build ``` @@ -64,18 +64,6 @@ meson install -C build ```sh cd lib/astal/gtk3 -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - -```sh meson setup --prefix /usr build meson install -C build ``` - -::: diff --git a/docs/guide/libraries/apps.md b/docs/guide/libraries/apps.md index c53daf0..7f9ee6e 100644 --- a/docs/guide/libraries/apps.md +++ b/docs/guide/libraries/apps.md @@ -32,22 +32,11 @@ cd astal/lib/apps 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Apps reference](https://aylur.github.io/libastal/apps). @@ -106,8 +95,3 @@ end ``` ::: - -:::info -The fuzzy query uses [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance). I am not a mathematician, but if you know how to reimplement -the logic of [fzf](https://github.com/junegunn/fzf) to make it better feel free to open PRs. -::: diff --git a/docs/guide/libraries/auth.md b/docs/guide/libraries/auth.md index 1f07a17..d5f0a49 100644 --- a/docs/guide/libraries/auth.md +++ b/docs/guide/libraries/auth.md @@ -42,22 +42,11 @@ cd astal/lib/auth 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Auth reference](https://aylur.github.io/libastal/auth). diff --git a/docs/guide/libraries/battery.md b/docs/guide/libraries/battery.md index b42d747..7f94297 100644 --- a/docs/guide/libraries/battery.md +++ b/docs/guide/libraries/battery.md @@ -36,22 +36,11 @@ cd astal/lib/battery 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Battery reference](https://aylur.github.io/libastal/battery). diff --git a/docs/guide/libraries/bluetooth.md b/docs/guide/libraries/bluetooth.md index 04d9db2..672f66d 100644 --- a/docs/guide/libraries/bluetooth.md +++ b/docs/guide/libraries/bluetooth.md @@ -36,22 +36,11 @@ cd astal/lib/bluetooth 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Bluetooth reference](https://aylur.github.io/libastal/bluetooth). diff --git a/docs/guide/libraries/hyprland.md b/docs/guide/libraries/hyprland.md index faf9e50..672faad 100644 --- a/docs/guide/libraries/hyprland.md +++ b/docs/guide/libraries/hyprland.md @@ -31,22 +31,11 @@ cd astal/lib/hyprland 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Hyprland reference](https://aylur.github.io/libastal/hyprland). diff --git a/docs/guide/libraries/mpris.md b/docs/guide/libraries/mpris.md index dfe7956..4f8c017 100644 --- a/docs/guide/libraries/mpris.md +++ b/docs/guide/libraries/mpris.md @@ -35,22 +35,11 @@ cd astal/lib/mpris 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Mpris reference](https://aylur.github.io/libastal/mpris). diff --git a/docs/guide/libraries/network.md b/docs/guide/libraries/network.md index afeb5d2..e076950 100644 --- a/docs/guide/libraries/network.md +++ b/docs/guide/libraries/network.md @@ -31,22 +31,11 @@ cd astal/lib/network 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Network reference](https://aylur.github.io/libastal/network). diff --git a/docs/guide/libraries/notifd.md b/docs/guide/libraries/notifd.md index 7e02149..094b770 100644 --- a/docs/guide/libraries/notifd.md +++ b/docs/guide/libraries/notifd.md @@ -35,22 +35,11 @@ cd astal/lib/notifd 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Notifd reference](https://aylur.github.io/libastal/notifd). diff --git a/docs/guide/libraries/powerprofiles.md b/docs/guide/libraries/powerprofiles.md index 8571c29..159f3ff 100644 --- a/docs/guide/libraries/powerprofiles.md +++ b/docs/guide/libraries/powerprofiles.md @@ -36,22 +36,11 @@ cd astal/lib/powerprofiles 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [PowerProfiles reference](https://aylur.github.io/libastal/powerprofiles). diff --git a/docs/guide/libraries/references.md b/docs/guide/libraries/references.md index 8f2bd02..3a85d73 100644 --- a/docs/guide/libraries/references.md +++ b/docs/guide/libraries/references.md @@ -29,7 +29,6 @@ Reading their documentation will vary depending on the language they are used in ## Astal Libraries -- [Astal](https://aylur.github.io/libastal): libastal the core library, which has the widgets and utilites - [Apps](https://aylur.github.io/libastal/apps): Library and cli tool for querying applications - [Auth](https://aylur.github.io/libastal/auth): Authentication library using PAM - [Battery](https://aylur.github.io/libastal/battery): DBus proxy library for upower daemon diff --git a/docs/guide/libraries/river.md b/docs/guide/libraries/river.md index 4818d0b..299aa8c 100644 --- a/docs/guide/libraries/river.md +++ b/docs/guide/libraries/river.md @@ -31,22 +31,11 @@ cd astal/lib/river 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [River reference](https://aylur.github.io/libastal/river). diff --git a/docs/guide/libraries/tray.md b/docs/guide/libraries/tray.md index c8d093b..b5cccc7 100644 --- a/docs/guide/libraries/tray.md +++ b/docs/guide/libraries/tray.md @@ -31,22 +31,11 @@ cd astal/lib/tray 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Tray reference](https://aylur.github.io/libastal/tray). diff --git a/docs/guide/libraries/wireplumber.md b/docs/guide/libraries/wireplumber.md index 5f1daab..0592628 100644 --- a/docs/guide/libraries/wireplumber.md +++ b/docs/guide/libraries/wireplumber.md @@ -31,22 +31,11 @@ cd astal/lib/wireplumber 3. install -```sh -meson setup build -meson install -C build -``` - -:::tip -Most distros recommend manual installs in `/usr/local`, -which is what `meson` defaults to. If you want to install to `/usr` -instead which most package managers do, set the `prefix` option: - ```sh meson setup --prefix /usr build +meson install -C build ``` -::: - ## Usage You can browse the [Wireplumber reference](https://aylur.github.io/libastal/wireplumber). diff --git a/docs/guide/typescript/installation.md b/docs/guide/typescript/installation.md index 4c31b6e..f085194 100644 --- a/docs/guide/typescript/installation.md +++ b/docs/guide/typescript/installation.md @@ -9,7 +9,7 @@ It lets you - generate a tsconfig which is used by LSPs - bundle your TypeScript and JavaScript code using [esbuild](https://esbuild.github.io/). -::: details Trivia +:::details Trivia AGS is the predecessor of Astal, which was written purely in TypeScript and so only supported JavaScript/TypeScript. Now it serves as a scaffolding tool for Astal+TypeScript+JSX projects. ::: @@ -43,7 +43,7 @@ meson install -C build :::tip You might be wondering why it is recommended to install a JavaScript package on the system instead of installing it as a node module. -It is solely to keep it in **sync** with the core `astal-sh` and `astal3`/`astal4` package. +It is solely to keep it in **sync** with the core `astal-io` and `astal3`/`astal4` package. ::: 3. Install the following dependencies diff --git a/docs/vitepress.config.ts b/docs/vitepress.config.ts index 843d3d3..2880477 100644 --- a/docs/vitepress.config.ts +++ b/docs/vitepress.config.ts @@ -89,9 +89,9 @@ export default defineConfig({ collapsed: true, items: [ { text: "References", link: "/guide/libraries/references" }, - { text: "AstalIO", link: "https://aylur.github.io/libastal/astal-io" }, + { text: "IO", link: "https://aylur.github.io/libastal/io" }, { text: "Astal3", link: "https://aylur.github.io/libastal/astal3" }, - { text: "Astal4", link: "https://aylur.github.io/libastal/astal4" }, + // { text: "Astal4", link: "https://aylur.github.io/libastal/astal4" }, { text: "Apps", link: "/guide/libraries/apps" }, { text: "Auth", link: "/guide/libraries/auth" }, { text: "Battery", link: "/guide/libraries/battery" }, -- cgit v1.2.3 From bafd48d3df9b43a1d49ec015eff30619d595468b Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 13:25:45 +0000 Subject: update lua and gjs layout installing the gjs package through meson or npm now results in the same exposed structure lua: fix rockspec docs: aur package --- docs/guide/getting-started/installation.md | 4 +- lang/gjs/gtk3/app.ts | 105 ---------- lang/gjs/gtk3/astalify.ts | 325 ----------------------------- lang/gjs/gtk3/index.ts | 9 - lang/gjs/gtk3/jsx-runtime.ts | 96 --------- lang/gjs/gtk3/widget.ts | 154 -------------- lang/gjs/gtk4/app.ts | 1 - lang/gjs/gtk4/astalify.ts | 1 - lang/gjs/gtk4/index.ts | 1 - lang/gjs/gtk4/jsx-runtime.ts | 1 - lang/gjs/index.ts | 7 +- lang/gjs/lib/binding.ts | 89 -------- lang/gjs/lib/file.ts | 45 ---- lang/gjs/lib/gobject.ts | 180 ---------------- lang/gjs/lib/process.ts | 68 ------ lang/gjs/lib/time.ts | 13 -- lang/gjs/lib/variable.ts | 230 -------------------- lang/gjs/meson.build | 25 ++- lang/gjs/package.json | 16 +- lang/gjs/src/binding.ts | 89 ++++++++ lang/gjs/src/file.ts | 45 ++++ lang/gjs/src/gobject.ts | 180 ++++++++++++++++ lang/gjs/src/gtk3/app.ts | 105 ++++++++++ lang/gjs/src/gtk3/astalify.ts | 325 +++++++++++++++++++++++++++++ lang/gjs/src/gtk3/index.ts | 9 + lang/gjs/src/gtk3/jsx-runtime.ts | 96 +++++++++ lang/gjs/src/gtk3/widget.ts | 154 ++++++++++++++ lang/gjs/src/gtk4/app.ts | 1 + lang/gjs/src/gtk4/astalify.ts | 1 + lang/gjs/src/gtk4/index.ts | 1 + lang/gjs/src/gtk4/jsx-runtime.ts | 1 + lang/gjs/src/index.ts | 6 + lang/gjs/src/process.ts | 68 ++++++ lang/gjs/src/time.ts | 13 ++ lang/gjs/src/variable.ts | 230 ++++++++++++++++++++ lang/gjs/tsconfig.json | 4 +- lang/lua/astal-dev-1.rockspec | 22 +- lang/lua/astal/binding.lua | 71 +++++++ lang/lua/astal/file.lua | 45 ++++ lang/lua/astal/gtk3/app.lua | 96 +++++++++ lang/lua/astal/gtk3/astalify.lua | 236 +++++++++++++++++++++ lang/lua/astal/gtk3/init.lua | 5 + lang/lua/astal/gtk3/widget.lua | 90 ++++++++ lang/lua/astal/init.lua | 27 +++ lang/lua/astal/process.lua | 78 +++++++ lang/lua/astal/time.lua | 27 +++ lang/lua/astal/variable.lua | 276 ++++++++++++++++++++++++ lang/lua/gtk3/app.lua | 96 --------- lang/lua/gtk3/astalify.lua | 236 --------------------- lang/lua/gtk3/widget.lua | 90 -------- lang/lua/init.lua | 27 --- lang/lua/lib/binding.lua | 71 ------- lang/lua/lib/file.lua | 45 ---- lang/lua/lib/process.lua | 78 ------- lang/lua/lib/time.lua | 27 --- lang/lua/lib/variable.lua | 276 ------------------------ 56 files changed, 2319 insertions(+), 2298 deletions(-) delete mode 100644 lang/gjs/gtk3/app.ts delete mode 100644 lang/gjs/gtk3/astalify.ts delete mode 100644 lang/gjs/gtk3/index.ts delete mode 100644 lang/gjs/gtk3/jsx-runtime.ts delete mode 100644 lang/gjs/gtk3/widget.ts delete mode 100644 lang/gjs/gtk4/app.ts delete mode 100644 lang/gjs/gtk4/astalify.ts delete mode 100644 lang/gjs/gtk4/index.ts delete mode 100644 lang/gjs/gtk4/jsx-runtime.ts delete mode 100644 lang/gjs/lib/binding.ts delete mode 100644 lang/gjs/lib/file.ts delete mode 100644 lang/gjs/lib/gobject.ts delete mode 100644 lang/gjs/lib/process.ts delete mode 100644 lang/gjs/lib/time.ts delete mode 100644 lang/gjs/lib/variable.ts create mode 100644 lang/gjs/src/binding.ts create mode 100644 lang/gjs/src/file.ts create mode 100644 lang/gjs/src/gobject.ts create mode 100644 lang/gjs/src/gtk3/app.ts create mode 100644 lang/gjs/src/gtk3/astalify.ts create mode 100644 lang/gjs/src/gtk3/index.ts create mode 100644 lang/gjs/src/gtk3/jsx-runtime.ts create mode 100644 lang/gjs/src/gtk3/widget.ts create mode 100644 lang/gjs/src/gtk4/app.ts create mode 100644 lang/gjs/src/gtk4/astalify.ts create mode 100644 lang/gjs/src/gtk4/index.ts create mode 100644 lang/gjs/src/gtk4/jsx-runtime.ts create mode 100644 lang/gjs/src/index.ts create mode 100644 lang/gjs/src/process.ts create mode 100644 lang/gjs/src/time.ts create mode 100644 lang/gjs/src/variable.ts create mode 100644 lang/lua/astal/binding.lua create mode 100644 lang/lua/astal/file.lua create mode 100644 lang/lua/astal/gtk3/app.lua create mode 100644 lang/lua/astal/gtk3/astalify.lua create mode 100644 lang/lua/astal/gtk3/init.lua create mode 100644 lang/lua/astal/gtk3/widget.lua create mode 100644 lang/lua/astal/init.lua create mode 100644 lang/lua/astal/process.lua create mode 100644 lang/lua/astal/time.lua create mode 100644 lang/lua/astal/variable.lua delete mode 100644 lang/lua/gtk3/app.lua delete mode 100644 lang/lua/gtk3/astalify.lua delete mode 100644 lang/lua/gtk3/widget.lua delete mode 100644 lang/lua/init.lua delete mode 100644 lang/lua/lib/binding.lua delete mode 100644 lang/lua/lib/file.lua delete mode 100644 lang/lua/lib/process.lua delete mode 100644 lang/lua/lib/time.lua delete mode 100644 lang/lua/lib/variable.lua diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md index 96cbdfa..aaa6d0d 100644 --- a/docs/guide/getting-started/installation.md +++ b/docs/guide/getting-started/installation.md @@ -10,12 +10,10 @@ Read more about it on the [nix page](./nix#astal) maintainer: [@kotontrion](https://github.com/kotontrion) - - :::code-group ```sh [Core Library] -yay -S libastal-git +yay -S libastal-io-git libastal-git ``` ```sh [Every Library] diff --git a/lang/gjs/gtk3/app.ts b/lang/gjs/gtk3/app.ts deleted file mode 100644 index 1191dc4..0000000 --- a/lang/gjs/gtk3/app.ts +++ /dev/null @@ -1,105 +0,0 @@ -import IO from "gi://AstalIO" -import GObject from "gi://GObject" -import Astal from "gi://Astal?version=3.0" -import Gio from "gi://Gio?version=2.0" -import Gtk from "gi://Gtk?version=3.0" - -Gtk.init(null) - -type RequestHandler = { - (request: string, res: (response: any) => void): void -} - -type Config = Partial<{ - icons: string - instanceName: string - gtkTheme: string - iconTheme: string - cursorTheme: string - css: string - requestHandler: RequestHandler - main(...args: string[]): void - client(message: (msg: string) => string, ...args: string[]): void - hold: boolean -}> - -import { setConsoleLogDomain } from "console" -import { exit, programArgs } from "system" - -export default new (class AstalJS extends Astal.Application { - static { GObject.registerClass({ GTypeName: "AstalJS" }, this) } - - eval(body: string): Promise { - return new Promise((res, rej) => { - try { - const fn = Function(`return (async function() { - ${body.includes(";") ? body : `return ${body};`} - })`) - fn()() - .then(res) - .catch(rej) - } - catch (error) { - rej(error) - } - }) - } - - requestHandler?: RequestHandler - - vfunc_request(msg: string, conn: Gio.SocketConnection): void { - if (typeof this.requestHandler === "function") { - this.requestHandler(msg, (response) => { - IO.write_sock(conn, String(response), (_, res) => - IO.write_sock_finish(res), - ) - }) - } - else { - super.vfunc_request(msg, conn) - } - } - - apply_css(style: string, reset = false) { - super.apply_css(style, reset) - } - - quit(code?: number): void { - super.quit() - exit(code ?? 0) - } - - start({ requestHandler, css, hold, main, client, icons, ...cfg }: Config = {}) { - client ??= () => { - print(`Astal instance "${this.instanceName}" already running`) - exit(1) - } - - Object.assign(this, cfg) - setConsoleLogDomain(this.instanceName) - - this.requestHandler = requestHandler - this.connect("activate", () => { - main?.(...programArgs) - }) - - try { - this.acquire_socket() - } - catch (error) { - return client(msg => IO.send_message(this.instanceName, msg)!, ...programArgs) - } - - if (css) - this.apply_css(css, false) - - if (icons) - this.add_icons(icons) - - hold ??= true - if (hold) - this.hold() - - this.runAsync([]) - } -}) diff --git a/lang/gjs/gtk3/astalify.ts b/lang/gjs/gtk3/astalify.ts deleted file mode 100644 index d31046c..0000000 --- a/lang/gjs/gtk3/astalify.ts +++ /dev/null @@ -1,325 +0,0 @@ -import Astal from "gi://Astal?version=3.0" -import Gtk from "gi://Gtk?version=3.0" -import Gdk from "gi://Gdk?version=3.0" -import GObject from "gi://GObject" -import { execAsync } from "../lib/process.js" -import Variable from "../lib/variable.js" -import Binding, { kebabify, snakeify, type Connectable, type Subscribable } from "../lib/binding.js" - -export function mergeBindings(array: any[]) { - function getValues(...args: any[]) { - let i = 0 - return array.map(value => value instanceof Binding - ? args[i++] - : value, - ) - } - - const bindings = array.filter(i => i instanceof Binding) - - if (bindings.length === 0) - return array - - if (bindings.length === 1) - return bindings[0].as(getValues) - - return Variable.derive(bindings, getValues)() -} - -function setProp(obj: any, prop: string, value: any) { - try { - // the setter method has to be used because - // array like properties are not bound correctly as props - const setter = `set_${snakeify(prop)}` - if (typeof obj[setter] === "function") - return obj[setter](value) - - return (obj[prop] = value) - } - catch (error) { - console.error(`could not set property "${prop}" on ${obj}:`, error) - } -} - -export default function astalify< - C extends { new(...args: any[]): Gtk.Widget }, ->(cls: C) { - class Widget extends cls { - get css(): string { return Astal.widget_get_css(this) } - set css(css: string) { Astal.widget_set_css(this, css) } - get_css(): string { return this.css } - set_css(css: string) { this.css = css } - - get className(): string { return Astal.widget_get_class_names(this).join(" ") } - set className(className: string) { Astal.widget_set_class_names(this, className.split(/\s+/)) } - get_class_name(): string { return this.className } - set_class_name(className: string) { this.className = className } - - get cursor(): Cursor { return Astal.widget_get_cursor(this) as Cursor } - set cursor(cursor: Cursor) { Astal.widget_set_cursor(this, cursor) } - get_cursor(): Cursor { return this.cursor } - set_cursor(cursor: Cursor) { this.cursor = cursor } - - get clickThrough(): boolean { return Astal.widget_get_click_through(this) } - set clickThrough(clickThrough: boolean) { Astal.widget_set_click_through(this, clickThrough) } - get_click_through(): boolean { return this.clickThrough } - set_click_through(clickThrough: boolean) { this.clickThrough = clickThrough } - - declare __no_implicit_destroy: boolean - get noImplicitDestroy(): boolean { return this.__no_implicit_destroy } - set noImplicitDestroy(value: boolean) { this.__no_implicit_destroy = value } - - _setChildren(children: Gtk.Widget[]) { - children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget - ? ch - : new Gtk.Label({ visible: true, label: String(ch) })) - - // remove - if (this instanceof Gtk.Bin) { - const ch = this.get_child() - if (ch) - this.remove(ch) - if (ch && !children.includes(ch) && !this.noImplicitDestroy) - ch?.destroy() - } - else if (this instanceof Gtk.Container) { - for (const ch of this.get_children()) { - this.remove(ch) - if (!children.includes(ch) && !this.noImplicitDestroy) - ch?.destroy() - } - } - - // TODO: add more container types - if (this instanceof Astal.Box) { - this.set_children(children) - } - - else if (this instanceof Astal.Stack) { - this.set_children(children) - } - - else if (this instanceof Astal.CenterBox) { - this.startWidget = children[0] - this.centerWidget = children[1] - this.endWidget = children[2] - } - - else if (this instanceof Astal.Overlay) { - const [child, ...overlays] = children - this.set_child(child) - this.set_overlays(overlays) - } - - else if (this instanceof Gtk.Container) { - for (const ch of children) - this.add(ch) - } - } - - toggleClassName(cn: string, cond = true) { - Astal.widget_toggle_class_name(this, cn, cond) - } - - hook( - object: Connectable, - signal: string, - callback: (self: this, ...args: any[]) => void, - ): this - hook( - object: Subscribable, - callback: (self: this, ...args: any[]) => void, - ): this - hook( - object: Connectable | Subscribable, - signalOrCallback: string | ((self: this, ...args: any[]) => void), - callback?: (self: this, ...args: any[]) => void, - ) { - if (typeof object.connect === "function" && callback) { - const id = object.connect(signalOrCallback, (_: any, ...args: unknown[]) => { - callback(this, ...args) - }) - this.connect("destroy", () => { - (object.disconnect as Connectable["disconnect"])(id) - }) - } - - else if (typeof object.subscribe === "function" && typeof signalOrCallback === "function") { - const unsub = object.subscribe((...args: unknown[]) => { - signalOrCallback(this, ...args) - }) - this.connect("destroy", unsub) - } - - return this - } - - constructor(...params: any[]) { - super() - const [config] = params - - const { setup, child, children = [], ...props } = config - props.visible ??= true - - if (child) - children.unshift(child) - - // collect bindings - const bindings = Object.keys(props).reduce((acc: any, prop) => { - if (props[prop] instanceof Binding) { - const binding = props[prop] - delete props[prop] - return [...acc, [prop, binding]] - } - return acc - }, []) - - // collect signal handlers - const onHandlers = Object.keys(props).reduce((acc: any, key) => { - if (key.startsWith("on")) { - const sig = kebabify(key).split("-").slice(1).join("-") - const handler = props[key] - delete props[key] - return [...acc, [sig, handler]] - } - return acc - }, []) - - // set children - const mergedChildren = mergeBindings(children.flat(Infinity)) - if (mergedChildren instanceof Binding) { - this._setChildren(mergedChildren.get()) - this.connect("destroy", mergedChildren.subscribe((v) => { - this._setChildren(v) - })) - } - else { - if (mergedChildren.length > 0) { - this._setChildren(mergedChildren) - } - } - - // setup signal handlers - for (const [signal, callback] of onHandlers) { - if (typeof callback === "function") { - this.connect(signal, callback) - } - else { - this.connect(signal, () => execAsync(callback) - .then(print).catch(console.error)) - } - } - - // setup bindings handlers - for (const [prop, binding] of bindings) { - if (prop === "child" || prop === "children") { - this.connect("destroy", binding.subscribe((v: any) => { - this._setChildren(v) - })) - } - this.connect("destroy", binding.subscribe((v: any) => { - setProp(this, prop, v) - })) - setProp(this, prop, binding.get()) - } - - Object.assign(this, props) - setup?.(this) - } - } - - GObject.registerClass({ - GTypeName: `Astal_${cls.name}`, - Properties: { - "class-name": GObject.ParamSpec.string( - "class-name", "", "", GObject.ParamFlags.READWRITE, "", - ), - "css": GObject.ParamSpec.string( - "css", "", "", GObject.ParamFlags.READWRITE, "", - ), - "cursor": GObject.ParamSpec.string( - "cursor", "", "", GObject.ParamFlags.READWRITE, "default", - ), - "click-through": GObject.ParamSpec.boolean( - "click-through", "", "", GObject.ParamFlags.READWRITE, false, - ), - "no-implicit-destroy": GObject.ParamSpec.boolean( - "no-implicit-destroy", "", "", GObject.ParamFlags.READWRITE, false, - ), - }, - }, Widget) - - return Widget -} - -type BindableProps = { - [K in keyof T]: Binding | T[K]; -} - -type SigHandler< - W extends InstanceType, - Args extends Array, -> = ((self: W, ...args: Args) => unknown) | string | string[] - -export type ConstructProps< - Self extends InstanceType, - Props extends Gtk.Widget.ConstructorProps, - Signals extends Record<`on${string}`, Array> = Record<`on${string}`, any[]>, -> = Partial<{ - // @ts-expect-error can't assign to unknown, but it works as expected though - [S in keyof Signals]: SigHandler -}> & Partial<{ - [Key in `on${string}`]: SigHandler -}> & BindableProps & { - className?: string - css?: string - cursor?: string - clickThrough?: boolean -}> & { - onDestroy?: (self: Self) => unknown - onDraw?: (self: Self) => unknown - onKeyPressEvent?: (self: Self, event: Gdk.Event) => unknown - onKeyReleaseEvent?: (self: Self, event: Gdk.Event) => unknown - onButtonPressEvent?: (self: Self, event: Gdk.Event) => unknown - onButtonReleaseEvent?: (self: Self, event: Gdk.Event) => unknown - onRealize?: (self: Self) => unknown - setup?: (self: Self) => void -} - -export type BindableChild = Gtk.Widget | Binding - -type Cursor = - | "default" - | "help" - | "pointer" - | "context-menu" - | "progress" - | "wait" - | "cell" - | "crosshair" - | "text" - | "vertical-text" - | "alias" - | "copy" - | "no-drop" - | "move" - | "not-allowed" - | "grab" - | "grabbing" - | "all-scroll" - | "col-resize" - | "row-resize" - | "n-resize" - | "e-resize" - | "s-resize" - | "w-resize" - | "ne-resize" - | "nw-resize" - | "sw-resize" - | "se-resize" - | "ew-resize" - | "ns-resize" - | "nesw-resize" - | "nwse-resize" - | "zoom-in" - | "zoom-out" diff --git a/lang/gjs/gtk3/index.ts b/lang/gjs/gtk3/index.ts deleted file mode 100644 index cfafbda..0000000 --- a/lang/gjs/gtk3/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Astal from "gi://Astal?version=3.0" -import Gtk from "gi://Gtk?version=3.0" -import Gdk from "gi://Gdk?version=3.0" -import astalify, { type ConstructProps } from "./astalify.js" - -export { Astal, Gtk, Gdk } -export { default as App } from "./app.js" -export { astalify, ConstructProps } -export * as Widget from "./widget.js" diff --git a/lang/gjs/gtk3/jsx-runtime.ts b/lang/gjs/gtk3/jsx-runtime.ts deleted file mode 100644 index 22dc424..0000000 --- a/lang/gjs/gtk3/jsx-runtime.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Gtk from "gi://Gtk?version=3.0" -import { mergeBindings, type BindableChild } from "./astalify.js" -import * as Widget from "./widget.js" - -function isArrowFunction(func: any): func is (args: any) => any { - return !Object.hasOwn(func, "prototype") -} - -export function Fragment({ children = [], child }: { - child?: BindableChild - children?: Array -}) { - return mergeBindings([...children, child]) -} - -export function jsx( - ctor: keyof typeof ctors | typeof Gtk.Widget, - { children, ...props }: any, -) { - children ??= [] - - if (!Array.isArray(children)) - children = [children] - - children = children.filter(Boolean) - - if (children.length === 1) - props.child = children[0] - else if (children.length > 1) - props.children = children - - if (typeof ctor === "string") { - return new ctors[ctor](props) - } - - if (isArrowFunction(ctor)) - return ctor(props) - - // @ts-expect-error can be class or function - return new ctor(props) -} - -const ctors = { - box: Widget.Box, - button: Widget.Button, - centerbox: Widget.CenterBox, - circularprogress: Widget.CircularProgress, - drawingarea: Widget.DrawingArea, - entry: Widget.Entry, - eventbox: Widget.EventBox, - // TODO: fixed - // TODO: flowbox - icon: Widget.Icon, - label: Widget.Label, - levelbar: Widget.LevelBar, - // TODO: listbox - overlay: Widget.Overlay, - revealer: Widget.Revealer, - scrollable: Widget.Scrollable, - slider: Widget.Slider, - stack: Widget.Stack, - switch: Widget.Switch, - window: Widget.Window, -} - -declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace JSX { - type Element = Gtk.Widget - type ElementClass = Gtk.Widget - interface IntrinsicElements { - box: Widget.BoxProps - button: Widget.ButtonProps - centerbox: Widget.CenterBoxProps - circularprogress: Widget.CircularProgressProps - drawingarea: Widget.DrawingAreaProps - entry: Widget.EntryProps - eventbox: Widget.EventBoxProps - // TODO: fixed - // TODO: flowbox - icon: Widget.IconProps - label: Widget.LabelProps - levelbar: Widget.LevelBarProps - // TODO: listbox - overlay: Widget.OverlayProps - revealer: Widget.RevealerProps - scrollable: Widget.ScrollableProps - slider: Widget.SliderProps - stack: Widget.StackProps - switch: Widget.SwitchProps - window: Widget.WindowProps - } - } -} - -export const jsxs = jsx diff --git a/lang/gjs/gtk3/widget.ts b/lang/gjs/gtk3/widget.ts deleted file mode 100644 index fd70ed6..0000000 --- a/lang/gjs/gtk3/widget.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* eslint-disable max-len */ -import Astal from "gi://Astal?version=3.0" -import Gtk from "gi://Gtk?version=3.0" -import GObject from "gi://GObject" -import astalify, { type ConstructProps, type BindableChild } from "./astalify.js" - -// Box -Object.defineProperty(Astal.Box.prototype, "children", { - get() { return this.get_children() }, - set(v) { this.set_children(v) }, -}) - -export type BoxProps = ConstructProps -export class Box extends astalify(Astal.Box) { - static { GObject.registerClass({ GTypeName: "Box" }, this) } - constructor(props?: BoxProps, ...children: Array) { super({ children, ...props } as any) } -} - -// Button -export type ButtonProps = ConstructProps -export class Button extends astalify(Astal.Button) { - static { GObject.registerClass({ GTypeName: "Button" }, this) } - constructor(props?: ButtonProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// CenterBox -export type CenterBoxProps = ConstructProps -export class CenterBox extends astalify(Astal.CenterBox) { - static { GObject.registerClass({ GTypeName: "CenterBox" }, this) } - constructor(props?: CenterBoxProps, ...children: Array) { super({ children, ...props } as any) } -} - -// CircularProgress -export type CircularProgressProps = ConstructProps -export class CircularProgress extends astalify(Astal.CircularProgress) { - static { GObject.registerClass({ GTypeName: "CircularProgress" }, this) } - constructor(props?: CircularProgressProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// DrawingArea -export type DrawingAreaProps = ConstructProps -export class DrawingArea extends astalify(Gtk.DrawingArea) { - static { GObject.registerClass({ GTypeName: "DrawingArea" }, this) } - constructor(props?: DrawingAreaProps) { super(props as any) } -} - -// Entry -export type EntryProps = ConstructProps -export class Entry extends astalify(Gtk.Entry) { - static { GObject.registerClass({ GTypeName: "Entry" }, this) } - constructor(props?: EntryProps) { super(props as any) } -} - -// EventBox -export type EventBoxProps = ConstructProps -export class EventBox extends astalify(Astal.EventBox) { - static { GObject.registerClass({ GTypeName: "EventBox" }, this) } - constructor(props?: EventBoxProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// // TODO: Fixed -// // TODO: FlowBox -// -// Icon -export type IconProps = ConstructProps -export class Icon extends astalify(Astal.Icon) { - static { GObject.registerClass({ GTypeName: "Icon" }, this) } - constructor(props?: IconProps) { super(props as any) } -} - -// Label -export type LabelProps = ConstructProps -export class Label extends astalify(Astal.Label) { - static { GObject.registerClass({ GTypeName: "Label" }, this) } - constructor(props?: LabelProps) { super(props as any) } -} - -// LevelBar -export type LevelBarProps = ConstructProps -export class LevelBar extends astalify(Astal.LevelBar) { - static { GObject.registerClass({ GTypeName: "LevelBar" }, this) } - constructor(props?: LevelBarProps) { super(props as any) } -} - -// TODO: ListBox - -// Overlay -export type OverlayProps = ConstructProps -export class Overlay extends astalify(Astal.Overlay) { - static { GObject.registerClass({ GTypeName: "Overlay" }, this) } - constructor(props?: OverlayProps, ...children: Array) { super({ children, ...props } as any) } -} - -// Revealer -export type RevealerProps = ConstructProps -export class Revealer extends astalify(Gtk.Revealer) { - static { GObject.registerClass({ GTypeName: "Revealer" }, this) } - constructor(props?: RevealerProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// Scrollable -export type ScrollableProps = ConstructProps -export class Scrollable extends astalify(Astal.Scrollable) { - static { GObject.registerClass({ GTypeName: "Scrollable" }, this) } - constructor(props?: ScrollableProps, child?: BindableChild) { super({ child, ...props } as any) } -} - -// Slider -export type SliderProps = ConstructProps -export class Slider extends astalify(Astal.Slider) { - static { GObject.registerClass({ GTypeName: "Slider" }, this) } - constructor(props?: SliderProps) { super(props as any) } -} - -// Stack -export type StackProps = ConstructProps -export class Stack extends astalify(Astal.Stack) { - static { GObject.registerClass({ GTypeName: "Stack" }, this) } - constructor(props?: StackProps, ...children: Array) { super({ children, ...props } as any) } -} - -// Switch -export type SwitchProps = ConstructProps -export class Switch extends astalify(Gtk.Switch) { - static { GObject.registerClass({ GTypeName: "Switch" }, this) } - constructor(props?: SwitchProps) { super(props as any) } -} - -// Window -export type WindowProps = ConstructProps -export class Window extends astalify(Astal.Window) { - static { GObject.registerClass({ GTypeName: "Window" }, this) } - constructor(props?: WindowProps, child?: BindableChild) { super({ child, ...props } as any) } -} diff --git a/lang/gjs/gtk4/app.ts b/lang/gjs/gtk4/app.ts deleted file mode 100644 index d931f73..0000000 --- a/lang/gjs/gtk4/app.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO: gtk4 diff --git a/lang/gjs/gtk4/astalify.ts b/lang/gjs/gtk4/astalify.ts deleted file mode 100644 index d931f73..0000000 --- a/lang/gjs/gtk4/astalify.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO: gtk4 diff --git a/lang/gjs/gtk4/index.ts b/lang/gjs/gtk4/index.ts deleted file mode 100644 index d931f73..0000000 --- a/lang/gjs/gtk4/index.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO: gtk4 diff --git a/lang/gjs/gtk4/jsx-runtime.ts b/lang/gjs/gtk4/jsx-runtime.ts deleted file mode 100644 index d931f73..0000000 --- a/lang/gjs/gtk4/jsx-runtime.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO: gtk4 diff --git a/lang/gjs/index.ts b/lang/gjs/index.ts index 4f52259..46e72b1 100644 --- a/lang/gjs/index.ts +++ b/lang/gjs/index.ts @@ -1,6 +1 @@ -export * from "./lib/process.js" -export * from "./lib/time.js" -export * from "./lib/file.js" -export * from "./lib/gobject.js" -export { bind, default as Binding } from "./lib/binding.js" -export { Variable } from "./lib/variable.js" +export * from "./src" diff --git a/lang/gjs/lib/binding.ts b/lang/gjs/lib/binding.ts deleted file mode 100644 index 95d905f..0000000 --- a/lang/gjs/lib/binding.ts +++ /dev/null @@ -1,89 +0,0 @@ -export const snakeify = (str: string) => str - .replace(/([a-z])([A-Z])/g, "$1_$2") - .replaceAll("-", "_") - .toLowerCase() - -export const kebabify = (str: string) => str - .replace(/([a-z])([A-Z])/g, "$1-$2") - .replaceAll("_", "-") - .toLowerCase() - -export interface Subscribable { - subscribe(callback: (value: T) => void): () => void - get(): T - [key: string]: any -} - -export interface Connectable { - connect(signal: string, callback: (...args: any[]) => unknown): number - disconnect(id: number): void - [key: string]: any -} - -export default class Binding { - private transformFn = (v: any) => v - - #emitter: Subscribable | Connectable - #prop?: string - - static bind< - T extends Connectable, - P extends keyof T, - >(object: T, property: P): Binding - - static bind(object: Subscribable): Binding - - static bind(emitter: Connectable | Subscribable, prop?: string) { - return new Binding(emitter, prop) - } - - private constructor(emitter: Connectable | Subscribable, prop?: string) { - this.#emitter = emitter - this.#prop = prop && kebabify(prop) - } - - toString() { - return `Binding<${this.#emitter}${this.#prop ? `, "${this.#prop}"` : ""}>` - } - - as(fn: (v: Value) => T): Binding { - const bind = new Binding(this.#emitter, this.#prop) - bind.transformFn = (v: Value) => fn(this.transformFn(v)) - return bind as unknown as Binding - } - - get(): Value { - if (typeof this.#emitter.get === "function") - return this.transformFn(this.#emitter.get()) - - if (typeof this.#prop === "string") { - const getter = `get_${snakeify(this.#prop)}` - if (typeof this.#emitter[getter] === "function") - return this.transformFn(this.#emitter[getter]()) - - return this.transformFn(this.#emitter[this.#prop]) - } - - throw Error("can not get value of binding") - } - - subscribe(callback: (value: Value) => void): () => void { - if (typeof this.#emitter.subscribe === "function") { - return this.#emitter.subscribe(() => { - callback(this.get()) - }) - } - else if (typeof this.#emitter.connect === "function") { - const signal = `notify::${this.#prop}` - const id = this.#emitter.connect(signal, () => { - callback(this.get()) - }) - return () => { - (this.#emitter.disconnect as Connectable["disconnect"])(id) - } - } - throw Error(`${this.#emitter} is not bindable`) - } -} - -export const { bind } = Binding diff --git a/lang/gjs/lib/file.ts b/lang/gjs/lib/file.ts deleted file mode 100644 index 7b9de3a..0000000 --- a/lang/gjs/lib/file.ts +++ /dev/null @@ -1,45 +0,0 @@ -import Astal from "gi://AstalIO" -import Gio from "gi://Gio" - -export function readFile(path: string): string { - return Astal.read_file(path) || "" -} - -export function readFileAsync(path: string): Promise { - return new Promise((resolve, reject) => { - Astal.read_file_async(path, (_, res) => { - try { - resolve(Astal.read_file_finish(res) || "") - } - catch (error) { - reject(error) - } - }) - }) -} - -export function writeFile(path: string, content: string): void { - Astal.write_file(path, content) -} - -export function writeFileAsync(path: string, content: string): Promise { - return new Promise((resolve, reject) => { - Astal.write_file_async(path, content, (_, res) => { - try { - resolve(Astal.write_file_finish(res)) - } - catch (error) { - reject(error) - } - }) - }) -} - -export function monitorFile( - path: string, - callback: (file: string, event: Gio.FileMonitorEvent) => void, -): Gio.FileMonitor { - return Astal.monitor_file(path, (file: string, event: Gio.FileMonitorEvent) => { - callback(file, event) - })! -} diff --git a/lang/gjs/lib/gobject.ts b/lang/gjs/lib/gobject.ts deleted file mode 100644 index 4740764..0000000 --- a/lang/gjs/lib/gobject.ts +++ /dev/null @@ -1,180 +0,0 @@ -export { default as GObject, default as default } from "gi://GObject" -export { default as Gio } from "gi://Gio" -export { default as GLib } from "gi://GLib" - -import GObject from "gi://GObject" -const meta = Symbol("meta") - -const { ParamSpec, ParamFlags } = GObject - -const kebabify = (str: string) => str - .replace(/([a-z])([A-Z])/g, "$1-$2") - .replaceAll("_", "-") - .toLowerCase() - -type SignalDeclaration = { - flags?: GObject.SignalFlags - accumulator?: GObject.AccumulatorType - return_type?: GObject.GType - param_types?: Array -} - -type PropertyDeclaration = - | InstanceType - | { $gtype: GObject.GType } - | typeof String - | typeof Number - | typeof Boolean - | typeof Object - -type GObjectConstructor = { - [meta]?: { - Properties?: { [key: string]: GObject.ParamSpec } - Signals?: { [key: string]: GObject.SignalDefinition } - } - new(...args: any[]): any -} - -type MetaInfo = GObject.MetaInfo, never> - -export function register(options: MetaInfo = {}) { - return function (cls: GObjectConstructor) { - GObject.registerClass({ - Signals: { ...cls[meta]?.Signals }, - Properties: { ...cls[meta]?.Properties }, - ...options, - }, cls) - } -} - -export function property(declaration: PropertyDeclaration = Object) { - return function (target: any, prop: any, desc?: PropertyDescriptor) { - target.constructor[meta] ??= {} - target.constructor[meta].Properties ??= {} - - const name = kebabify(prop) - - if (!desc) { - let value = defaultValue(declaration) - - Object.defineProperty(target, prop, { - get() { - return value - }, - set(v) { - if (v !== value) { - value = v - this.notify(name) - } - }, - }) - - Object.defineProperty(target, `set_${name.replace("-", "_")}`, { - value: function (v: any) { - this[prop] = v - }, - }) - - Object.defineProperty(target, `get_${name.replace("-", "_")}`, { - value: function () { - return this[prop] - }, - }) - - target.constructor[meta].Properties[kebabify(prop)] = pspec(name, ParamFlags.READWRITE, declaration) - } - - else { - let flags = 0 - if (desc.get) flags |= ParamFlags.READABLE - if (desc.set) flags |= ParamFlags.WRITABLE - - target.constructor[meta].Properties[kebabify(prop)] = pspec(name, flags, declaration) - } - } -} - -export function signal(...params: Array<{ $gtype: GObject.GType } | typeof Object>): -(target: any, signal: any, desc?: PropertyDescriptor) => void - -export function signal(declaration?: SignalDeclaration): -(target: any, signal: any, desc?: PropertyDescriptor) => void - -export function signal( - declaration?: SignalDeclaration | { $gtype: GObject.GType } | typeof Object, - ...params: Array<{ $gtype: GObject.GType } | typeof Object> -) { - return function (target: any, signal: any, desc?: PropertyDescriptor) { - target.constructor[meta] ??= {} - target.constructor[meta].Signals ??= {} - - const name = kebabify(signal) - - if (declaration || params.length > 0) { - // @ts-expect-error TODO: type assert - const arr = [declaration, ...params].map(v => v.$gtype) - target.constructor[meta].Signals[name] = { - param_types: arr, - } - } - else { - target.constructor[meta].Signals[name] = declaration - } - - if (!desc) { - Object.defineProperty(target, signal, { - value: function (...args: any[]) { - this.emit(name, ...args) - }, - }) - } - else { - const og: ((...args: any[]) => void) = desc.value - desc.value = function (...args: any[]) { - // @ts-expect-error not typed - this.emit(name, ...args) - } - Object.defineProperty(target, `on_${name.replace("-", "_")}`, { - value: function (...args: any[]) { - return og(...args) - }, - }) - } - } -} - -function pspec(name: string, flags: number, declaration: PropertyDeclaration) { - if (declaration instanceof ParamSpec) - return declaration - - switch (declaration) { - case String: - return ParamSpec.string(name, "", "", flags, "") - case Number: - return ParamSpec.double(name, "", "", flags, -Number.MAX_VALUE, Number.MAX_VALUE, 0) - case Boolean: - return ParamSpec.boolean(name, "", "", flags, false) - case Object: - return ParamSpec.jsobject(name, "", "", flags) - default: - // @ts-expect-error misstyped - return ParamSpec.object(name, "", "", flags, declaration.$gtype) - } -} - -function defaultValue(declaration: PropertyDeclaration) { - if (declaration instanceof ParamSpec) - return declaration.get_default_value() - - switch (declaration) { - case String: - return "default-string" - case Number: - return 0 - case Boolean: - return false - case Object: - default: - return null - } -} diff --git a/lang/gjs/lib/process.ts b/lang/gjs/lib/process.ts deleted file mode 100644 index 2f7816b..0000000 --- a/lang/gjs/lib/process.ts +++ /dev/null @@ -1,68 +0,0 @@ -import Astal from "gi://AstalIO" - -type Args = { - cmd: string | string[] - out?: (stdout: string) => void - err?: (stderr: string) => void -} - -export function subprocess(args: Args): Astal.Process - -export function subprocess( - cmd: string | string[], - onOut?: (stdout: string) => void, - onErr?: (stderr: string) => void, -): Astal.Process - -export function subprocess( - argsOrCmd: Args | string | string[], - onOut: (stdout: string) => void = print, - onErr: (stderr: string) => void = printerr, -) { - const args = Array.isArray(argsOrCmd) || typeof argsOrCmd === "string" - const { cmd, err, out } = { - cmd: args ? argsOrCmd : argsOrCmd.cmd, - err: args ? onErr : argsOrCmd.err || onErr, - out: args ? onOut : argsOrCmd.out || onOut, - } - - const proc = Array.isArray(cmd) - ? Astal.Process.subprocessv(cmd) - : Astal.Process.subprocess(cmd) - - proc.connect("stdout", (_, stdout: string) => out(stdout)) - proc.connect("stderr", (_, stderr: string) => err(stderr)) - return proc -} - -/** @throws {GLib.Error} Throws stderr */ -export function exec(cmd: string | string[]) { - return Array.isArray(cmd) - ? Astal.Process.execv(cmd) - : Astal.Process.exec(cmd) -} - -export function execAsync(cmd: string | string[]): Promise { - return new Promise((resolve, reject) => { - if (Array.isArray(cmd)) { - Astal.Process.exec_asyncv(cmd, (_, res) => { - try { - resolve(Astal.Process.exec_asyncv_finish(res)) - } - catch (error) { - reject(error) - } - }) - } - else { - Astal.Process.exec_async(cmd, (_, res) => { - try { - resolve(Astal.Process.exec_finish(res)) - } - catch (error) { - reject(error) - } - }) - } - }) -} diff --git a/lang/gjs/lib/time.ts b/lang/gjs/lib/time.ts deleted file mode 100644 index a7e1e61..0000000 --- a/lang/gjs/lib/time.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Astal from "gi://AstalIO" - -export function interval(interval: number, callback?: () => void) { - return Astal.Time.interval(interval, () => void callback?.()) -} - -export function timeout(timeout: number, callback?: () => void) { - return Astal.Time.timeout(timeout, () => void callback?.()) -} - -export function idle(callback?: () => void) { - return Astal.Time.idle(() => void callback?.()) -} diff --git a/lang/gjs/lib/variable.ts b/lang/gjs/lib/variable.ts deleted file mode 100644 index 9b3d3d2..0000000 --- a/lang/gjs/lib/variable.ts +++ /dev/null @@ -1,230 +0,0 @@ -import Astal from "gi://AstalIO" -import Binding, { type Connectable, type Subscribable } from "./binding.js" -import { interval } from "./time.js" -import { execAsync, subprocess } from "./process.js" - -class VariableWrapper extends Function { - private variable!: Astal.VariableBase - private errHandler? = console.error - - private _value: T - private _poll?: Astal.Time - private _watch?: Astal.Process - - private pollInterval = 1000 - private pollExec?: string[] | string - private pollTransform?: (stdout: string, prev: T) => T - private pollFn?: (prev: T) => T | Promise - - private watchTransform?: (stdout: string, prev: T) => T - private watchExec?: string[] | string - - constructor(init: T) { - super() - this._value = init - this.variable = new Astal.VariableBase() - this.variable.connect("dropped", () => { - this.stopWatch() - this.stopPoll() - }) - this.variable.connect("error", (_, err) => this.errHandler?.(err)) - return new Proxy(this, { - apply: (target, _, args) => target._call(args[0]), - }) - } - - private _call(transform?: (value: T) => R): Binding { - const b = Binding.bind(this) - return transform ? b.as(transform) : b as unknown as Binding - } - - toString() { - return String(`Variable<${this.get()}>`) - } - - get(): T { return this._value } - set(value: T) { - if (value !== this._value) { - this._value = value - this.variable.emit("changed") - } - } - - startPoll() { - if (this._poll) - return - - if (this.pollFn) { - this._poll = interval(this.pollInterval, () => { - const v = this.pollFn!(this.get()) - if (v instanceof Promise) { - v.then(v => this.set(v)) - .catch(err => this.variable.emit("error", err)) - } - else { - this.set(v) - } - }) - } - else if (this.pollExec) { - this._poll = interval(this.pollInterval, () => { - execAsync(this.pollExec!) - .then(v => this.set(this.pollTransform!(v, this.get()))) - .catch(err => this.variable.emit("error", err)) - }) - } - } - - startWatch() { - if (this._watch) - return - - this._watch = subprocess({ - cmd: this.watchExec!, - out: out => this.set(this.watchTransform!(out, this.get())), - err: err => this.variable.emit("error", err), - }) - } - - stopPoll() { - this._poll?.cancel() - delete this._poll - } - - stopWatch() { - this._watch?.kill() - delete this._watch - } - - isPolling() { return !!this._poll } - isWatching() { return !!this._watch } - - drop() { - this.variable.emit("dropped") - } - - onDropped(callback: () => void) { - this.variable.connect("dropped", callback) - return this as unknown as Variable - } - - onError(callback: (err: string) => void) { - delete this.errHandler - this.variable.connect("error", (_, err) => callback(err)) - return this as unknown as Variable - } - - subscribe(callback: (value: T) => void) { - const id = this.variable.connect("changed", () => { - callback(this.get()) - }) - return () => this.variable.disconnect(id) - } - - poll( - interval: number, - exec: string | string[], - transform?: (stdout: string, prev: T) => T - ): Variable - - poll( - interval: number, - callback: (prev: T) => T | Promise - ): Variable - - poll( - interval: number, - exec: string | string[] | ((prev: T) => T | Promise), - transform: (stdout: string, prev: T) => T = out => out as T, - ) { - this.stopPoll() - this.pollInterval = interval - this.pollTransform = transform - if (typeof exec === "function") { - this.pollFn = exec - delete this.pollExec - } - else { - this.pollExec = exec - delete this.pollFn - } - this.startPoll() - return this as unknown as Variable - } - - watch( - exec: string | string[], - transform: (stdout: string, prev: T) => T = out => out as T, - ) { - this.stopWatch() - this.watchExec = exec - this.watchTransform = transform - this.startWatch() - return this as unknown as Variable - } - - observe( - objs: Array<[obj: Connectable, signal: string]>, - callback: (...args: any[]) => T, - ): Variable - - observe( - obj: Connectable, - signal: string, - callback: (...args: any[]) => T, - ): Variable - - observe( - objs: Connectable | Array<[obj: Connectable, signal: string]>, - sigOrFn: string | ((obj: Connectable, ...args: any[]) => T), - callback?: (obj: Connectable, ...args: any[]) => T, - ) { - const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => this.get()) - const set = (obj: Connectable, ...args: any[]) => this.set(f(obj, ...args)) - - if (Array.isArray(objs)) { - for (const obj of objs) { - const [o, s] = obj - const id = o.connect(s, set) - this.onDropped(() => o.disconnect(id)) - } - } - else { - if (typeof sigOrFn === "string") { - const id = objs.connect(sigOrFn, set) - this.onDropped(() => objs.disconnect(id)) - } - } - - return this as unknown as Variable - } - - static derive< - const Deps extends Array>, - Args extends { - [K in keyof Deps]: Deps[K] extends Subscribable ? T : never - }, - V = Args, - >(deps: Deps, fn: (...args: Args) => V = (...args) => args as unknown as V) { - const update = () => fn(...deps.map(d => d.get()) as Args) - const derived = new Variable(update()) - const unsubs = deps.map(dep => dep.subscribe(() => derived.set(update()))) - derived.onDropped(() => unsubs.map(unsub => unsub())) - return derived - } -} - -export interface Variable extends Omit, "bind"> { - (transform: (value: T) => R): Binding - (): Binding -} - -export const Variable = new Proxy(VariableWrapper as any, { - apply: (_t, _a, args) => new VariableWrapper(args[0]), -}) as { - derive: typeof VariableWrapper["derive"] - (init: T): Variable - new(init: T): Variable -} - -export default Variable diff --git a/lang/gjs/meson.build b/lang/gjs/meson.build index 8f3058f..388b301 100644 --- a/lang/gjs/meson.build +++ b/lang/gjs/meson.build @@ -1,9 +1,22 @@ project('astal-gjs') -datadir = get_option('prefix') / get_option('datadir') -pkgdata = datadir / 'astal' / 'gjs' +dest = get_option('prefix') / get_option('datadir') / 'astal' / 'gjs' -install_data('index.ts', install_dir: pkgdata) -install_subdir('lib', install_dir: pkgdata) -install_subdir('gtk3', install_dir: pkgdata) -install_subdir('gtk4', install_dir: pkgdata) +dependency('astal-io-0.1') +dependency('astal-3.0') + +install_data( + [ + 'src/binding.ts', + 'src/file.ts', + 'src/gobject.ts', + 'src/index.ts', + 'src/process.ts', + 'src/time.ts', + 'src/variable.ts', + ], + install_dir: dest, +) + +install_subdir('src/gtk3', install_dir: dest) +# install_subdir('src/gtk4', install_dir: dest) diff --git a/lang/gjs/package.json b/lang/gjs/package.json index 447ddcd..9f44388 100644 --- a/lang/gjs/package.json +++ b/lang/gjs/package.json @@ -16,14 +16,14 @@ }, "exports": { ".": "./index.ts", - "./gtk3": "./gtk3/index.ts", - "./gtk4": "./gtk3/index.ts", - "./lib/binding": "./lib/binding.ts", - "./lib/file": "./lib/file.ts", - "./lib/gobject": "./lib/gobject.ts", - "./lib/process": "./lib/process.ts", - "./lib/time": "./lib/time.ts", - "./lib/variable": "./lib/variable.ts" + "./gtk3": "./src/gtk3/index.ts", + "./gtk4": "./src/gtk3/index.ts", + "./binding": "./src/binding.ts", + "./file": "./src/file.ts", + "./gobject": "./src/gobject.ts", + "./process": "./src/process.ts", + "./time": "./src/time.ts", + "./variable": "./src/variable.ts" }, "engines": { "gjs": ">=1.79.0" diff --git a/lang/gjs/src/binding.ts b/lang/gjs/src/binding.ts new file mode 100644 index 0000000..95d905f --- /dev/null +++ b/lang/gjs/src/binding.ts @@ -0,0 +1,89 @@ +export const snakeify = (str: string) => str + .replace(/([a-z])([A-Z])/g, "$1_$2") + .replaceAll("-", "_") + .toLowerCase() + +export const kebabify = (str: string) => str + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replaceAll("_", "-") + .toLowerCase() + +export interface Subscribable { + subscribe(callback: (value: T) => void): () => void + get(): T + [key: string]: any +} + +export interface Connectable { + connect(signal: string, callback: (...args: any[]) => unknown): number + disconnect(id: number): void + [key: string]: any +} + +export default class Binding { + private transformFn = (v: any) => v + + #emitter: Subscribable | Connectable + #prop?: string + + static bind< + T extends Connectable, + P extends keyof T, + >(object: T, property: P): Binding + + static bind(object: Subscribable): Binding + + static bind(emitter: Connectable | Subscribable, prop?: string) { + return new Binding(emitter, prop) + } + + private constructor(emitter: Connectable | Subscribable, prop?: string) { + this.#emitter = emitter + this.#prop = prop && kebabify(prop) + } + + toString() { + return `Binding<${this.#emitter}${this.#prop ? `, "${this.#prop}"` : ""}>` + } + + as(fn: (v: Value) => T): Binding { + const bind = new Binding(this.#emitter, this.#prop) + bind.transformFn = (v: Value) => fn(this.transformFn(v)) + return bind as unknown as Binding + } + + get(): Value { + if (typeof this.#emitter.get === "function") + return this.transformFn(this.#emitter.get()) + + if (typeof this.#prop === "string") { + const getter = `get_${snakeify(this.#prop)}` + if (typeof this.#emitter[getter] === "function") + return this.transformFn(this.#emitter[getter]()) + + return this.transformFn(this.#emitter[this.#prop]) + } + + throw Error("can not get value of binding") + } + + subscribe(callback: (value: Value) => void): () => void { + if (typeof this.#emitter.subscribe === "function") { + return this.#emitter.subscribe(() => { + callback(this.get()) + }) + } + else if (typeof this.#emitter.connect === "function") { + const signal = `notify::${this.#prop}` + const id = this.#emitter.connect(signal, () => { + callback(this.get()) + }) + return () => { + (this.#emitter.disconnect as Connectable["disconnect"])(id) + } + } + throw Error(`${this.#emitter} is not bindable`) + } +} + +export const { bind } = Binding diff --git a/lang/gjs/src/file.ts b/lang/gjs/src/file.ts new file mode 100644 index 0000000..7b9de3a --- /dev/null +++ b/lang/gjs/src/file.ts @@ -0,0 +1,45 @@ +import Astal from "gi://AstalIO" +import Gio from "gi://Gio" + +export function readFile(path: string): string { + return Astal.read_file(path) || "" +} + +export function readFileAsync(path: string): Promise { + return new Promise((resolve, reject) => { + Astal.read_file_async(path, (_, res) => { + try { + resolve(Astal.read_file_finish(res) || "") + } + catch (error) { + reject(error) + } + }) + }) +} + +export function writeFile(path: string, content: string): void { + Astal.write_file(path, content) +} + +export function writeFileAsync(path: string, content: string): Promise { + return new Promise((resolve, reject) => { + Astal.write_file_async(path, content, (_, res) => { + try { + resolve(Astal.write_file_finish(res)) + } + catch (error) { + reject(error) + } + }) + }) +} + +export function monitorFile( + path: string, + callback: (file: string, event: Gio.FileMonitorEvent) => void, +): Gio.FileMonitor { + return Astal.monitor_file(path, (file: string, event: Gio.FileMonitorEvent) => { + callback(file, event) + })! +} diff --git a/lang/gjs/src/gobject.ts b/lang/gjs/src/gobject.ts new file mode 100644 index 0000000..4740764 --- /dev/null +++ b/lang/gjs/src/gobject.ts @@ -0,0 +1,180 @@ +export { default as GObject, default as default } from "gi://GObject" +export { default as Gio } from "gi://Gio" +export { default as GLib } from "gi://GLib" + +import GObject from "gi://GObject" +const meta = Symbol("meta") + +const { ParamSpec, ParamFlags } = GObject + +const kebabify = (str: string) => str + .replace(/([a-z])([A-Z])/g, "$1-$2") + .replaceAll("_", "-") + .toLowerCase() + +type SignalDeclaration = { + flags?: GObject.SignalFlags + accumulator?: GObject.AccumulatorType + return_type?: GObject.GType + param_types?: Array +} + +type PropertyDeclaration = + | InstanceType + | { $gtype: GObject.GType } + | typeof String + | typeof Number + | typeof Boolean + | typeof Object + +type GObjectConstructor = { + [meta]?: { + Properties?: { [key: string]: GObject.ParamSpec } + Signals?: { [key: string]: GObject.SignalDefinition } + } + new(...args: any[]): any +} + +type MetaInfo = GObject.MetaInfo, never> + +export function register(options: MetaInfo = {}) { + return function (cls: GObjectConstructor) { + GObject.registerClass({ + Signals: { ...cls[meta]?.Signals }, + Properties: { ...cls[meta]?.Properties }, + ...options, + }, cls) + } +} + +export function property(declaration: PropertyDeclaration = Object) { + return function (target: any, prop: any, desc?: PropertyDescriptor) { + target.constructor[meta] ??= {} + target.constructor[meta].Properties ??= {} + + const name = kebabify(prop) + + if (!desc) { + let value = defaultValue(declaration) + + Object.defineProperty(target, prop, { + get() { + return value + }, + set(v) { + if (v !== value) { + value = v + this.notify(name) + } + }, + }) + + Object.defineProperty(target, `set_${name.replace("-", "_")}`, { + value: function (v: any) { + this[prop] = v + }, + }) + + Object.defineProperty(target, `get_${name.replace("-", "_")}`, { + value: function () { + return this[prop] + }, + }) + + target.constructor[meta].Properties[kebabify(prop)] = pspec(name, ParamFlags.READWRITE, declaration) + } + + else { + let flags = 0 + if (desc.get) flags |= ParamFlags.READABLE + if (desc.set) flags |= ParamFlags.WRITABLE + + target.constructor[meta].Properties[kebabify(prop)] = pspec(name, flags, declaration) + } + } +} + +export function signal(...params: Array<{ $gtype: GObject.GType } | typeof Object>): +(target: any, signal: any, desc?: PropertyDescriptor) => void + +export function signal(declaration?: SignalDeclaration): +(target: any, signal: any, desc?: PropertyDescriptor) => void + +export function signal( + declaration?: SignalDeclaration | { $gtype: GObject.GType } | typeof Object, + ...params: Array<{ $gtype: GObject.GType } | typeof Object> +) { + return function (target: any, signal: any, desc?: PropertyDescriptor) { + target.constructor[meta] ??= {} + target.constructor[meta].Signals ??= {} + + const name = kebabify(signal) + + if (declaration || params.length > 0) { + // @ts-expect-error TODO: type assert + const arr = [declaration, ...params].map(v => v.$gtype) + target.constructor[meta].Signals[name] = { + param_types: arr, + } + } + else { + target.constructor[meta].Signals[name] = declaration + } + + if (!desc) { + Object.defineProperty(target, signal, { + value: function (...args: any[]) { + this.emit(name, ...args) + }, + }) + } + else { + const og: ((...args: any[]) => void) = desc.value + desc.value = function (...args: any[]) { + // @ts-expect-error not typed + this.emit(name, ...args) + } + Object.defineProperty(target, `on_${name.replace("-", "_")}`, { + value: function (...args: any[]) { + return og(...args) + }, + }) + } + } +} + +function pspec(name: string, flags: number, declaration: PropertyDeclaration) { + if (declaration instanceof ParamSpec) + return declaration + + switch (declaration) { + case String: + return ParamSpec.string(name, "", "", flags, "") + case Number: + return ParamSpec.double(name, "", "", flags, -Number.MAX_VALUE, Number.MAX_VALUE, 0) + case Boolean: + return ParamSpec.boolean(name, "", "", flags, false) + case Object: + return ParamSpec.jsobject(name, "", "", flags) + default: + // @ts-expect-error misstyped + return ParamSpec.object(name, "", "", flags, declaration.$gtype) + } +} + +function defaultValue(declaration: PropertyDeclaration) { + if (declaration instanceof ParamSpec) + return declaration.get_default_value() + + switch (declaration) { + case String: + return "default-string" + case Number: + return 0 + case Boolean: + return false + case Object: + default: + return null + } +} diff --git a/lang/gjs/src/gtk3/app.ts b/lang/gjs/src/gtk3/app.ts new file mode 100644 index 0000000..1191dc4 --- /dev/null +++ b/lang/gjs/src/gtk3/app.ts @@ -0,0 +1,105 @@ +import IO from "gi://AstalIO" +import GObject from "gi://GObject" +import Astal from "gi://Astal?version=3.0" +import Gio from "gi://Gio?version=2.0" +import Gtk from "gi://Gtk?version=3.0" + +Gtk.init(null) + +type RequestHandler = { + (request: string, res: (response: any) => void): void +} + +type Config = Partial<{ + icons: string + instanceName: string + gtkTheme: string + iconTheme: string + cursorTheme: string + css: string + requestHandler: RequestHandler + main(...args: string[]): void + client(message: (msg: string) => string, ...args: string[]): void + hold: boolean +}> + +import { setConsoleLogDomain } from "console" +import { exit, programArgs } from "system" + +export default new (class AstalJS extends Astal.Application { + static { GObject.registerClass({ GTypeName: "AstalJS" }, this) } + + eval(body: string): Promise { + return new Promise((res, rej) => { + try { + const fn = Function(`return (async function() { + ${body.includes(";") ? body : `return ${body};`} + })`) + fn()() + .then(res) + .catch(rej) + } + catch (error) { + rej(error) + } + }) + } + + requestHandler?: RequestHandler + + vfunc_request(msg: string, conn: Gio.SocketConnection): void { + if (typeof this.requestHandler === "function") { + this.requestHandler(msg, (response) => { + IO.write_sock(conn, String(response), (_, res) => + IO.write_sock_finish(res), + ) + }) + } + else { + super.vfunc_request(msg, conn) + } + } + + apply_css(style: string, reset = false) { + super.apply_css(style, reset) + } + + quit(code?: number): void { + super.quit() + exit(code ?? 0) + } + + start({ requestHandler, css, hold, main, client, icons, ...cfg }: Config = {}) { + client ??= () => { + print(`Astal instance "${this.instanceName}" already running`) + exit(1) + } + + Object.assign(this, cfg) + setConsoleLogDomain(this.instanceName) + + this.requestHandler = requestHandler + this.connect("activate", () => { + main?.(...programArgs) + }) + + try { + this.acquire_socket() + } + catch (error) { + return client(msg => IO.send_message(this.instanceName, msg)!, ...programArgs) + } + + if (css) + this.apply_css(css, false) + + if (icons) + this.add_icons(icons) + + hold ??= true + if (hold) + this.hold() + + this.runAsync([]) + } +}) diff --git a/lang/gjs/src/gtk3/astalify.ts b/lang/gjs/src/gtk3/astalify.ts new file mode 100644 index 0000000..2cd6984 --- /dev/null +++ b/lang/gjs/src/gtk3/astalify.ts @@ -0,0 +1,325 @@ +import Astal from "gi://Astal?version=3.0" +import Gtk from "gi://Gtk?version=3.0" +import Gdk from "gi://Gdk?version=3.0" +import GObject from "gi://GObject" +import { execAsync } from "../process.js" +import Variable from "../variable.js" +import Binding, { kebabify, snakeify, type Connectable, type Subscribable } from "../binding.js" + +export function mergeBindings(array: any[]) { + function getValues(...args: any[]) { + let i = 0 + return array.map(value => value instanceof Binding + ? args[i++] + : value, + ) + } + + const bindings = array.filter(i => i instanceof Binding) + + if (bindings.length === 0) + return array + + if (bindings.length === 1) + return bindings[0].as(getValues) + + return Variable.derive(bindings, getValues)() +} + +function setProp(obj: any, prop: string, value: any) { + try { + // the setter method has to be used because + // array like properties are not bound correctly as props + const setter = `set_${snakeify(prop)}` + if (typeof obj[setter] === "function") + return obj[setter](value) + + return (obj[prop] = value) + } + catch (error) { + console.error(`could not set property "${prop}" on ${obj}:`, error) + } +} + +export default function astalify< + C extends { new(...args: any[]): Gtk.Widget }, +>(cls: C) { + class Widget extends cls { + get css(): string { return Astal.widget_get_css(this) } + set css(css: string) { Astal.widget_set_css(this, css) } + get_css(): string { return this.css } + set_css(css: string) { this.css = css } + + get className(): string { return Astal.widget_get_class_names(this).join(" ") } + set className(className: string) { Astal.widget_set_class_names(this, className.split(/\s+/)) } + get_class_name(): string { return this.className } + set_class_name(className: string) { this.className = className } + + get cursor(): Cursor { return Astal.widget_get_cursor(this) as Cursor } + set cursor(cursor: Cursor) { Astal.widget_set_cursor(this, cursor) } + get_cursor(): Cursor { return this.cursor } + set_cursor(cursor: Cursor) { this.cursor = cursor } + + get clickThrough(): boolean { return Astal.widget_get_click_through(this) } + set clickThrough(clickThrough: boolean) { Astal.widget_set_click_through(this, clickThrough) } + get_click_through(): boolean { return this.clickThrough } + set_click_through(clickThrough: boolean) { this.clickThrough = clickThrough } + + declare __no_implicit_destroy: boolean + get noImplicitDestroy(): boolean { return this.__no_implicit_destroy } + set noImplicitDestroy(value: boolean) { this.__no_implicit_destroy = value } + + _setChildren(children: Gtk.Widget[]) { + children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget + ? ch + : new Gtk.Label({ visible: true, label: String(ch) })) + + // remove + if (this instanceof Gtk.Bin) { + const ch = this.get_child() + if (ch) + this.remove(ch) + if (ch && !children.includes(ch) && !this.noImplicitDestroy) + ch?.destroy() + } + else if (this instanceof Gtk.Container) { + for (const ch of this.get_children()) { + this.remove(ch) + if (!children.includes(ch) && !this.noImplicitDestroy) + ch?.destroy() + } + } + + // TODO: add more container types + if (this instanceof Astal.Box) { + this.set_children(children) + } + + else if (this instanceof Astal.Stack) { + this.set_children(children) + } + + else if (this instanceof Astal.CenterBox) { + this.startWidget = children[0] + this.centerWidget = children[1] + this.endWidget = children[2] + } + + else if (this instanceof Astal.Overlay) { + const [child, ...overlays] = children + this.set_child(child) + this.set_overlays(overlays) + } + + else if (this instanceof Gtk.Container) { + for (const ch of children) + this.add(ch) + } + } + + toggleClassName(cn: string, cond = true) { + Astal.widget_toggle_class_name(this, cn, cond) + } + + hook( + object: Connectable, + signal: string, + callback: (self: this, ...args: any[]) => void, + ): this + hook( + object: Subscribable, + callback: (self: this, ...args: any[]) => void, + ): this + hook( + object: Connectable | Subscribable, + signalOrCallback: string | ((self: this, ...args: any[]) => void), + callback?: (self: this, ...args: any[]) => void, + ) { + if (typeof object.connect === "function" && callback) { + const id = object.connect(signalOrCallback, (_: any, ...args: unknown[]) => { + callback(this, ...args) + }) + this.connect("destroy", () => { + (object.disconnect as Connectable["disconnect"])(id) + }) + } + + else if (typeof object.subscribe === "function" && typeof signalOrCallback === "function") { + const unsub = object.subscribe((...args: unknown[]) => { + signalOrCallback(this, ...args) + }) + this.connect("destroy", unsub) + } + + return this + } + + constructor(...params: any[]) { + super() + const [config] = params + + const { setup, child, children = [], ...props } = config + props.visible ??= true + + if (child) + children.unshift(child) + + // collect bindings + const bindings = Object.keys(props).reduce((acc: any, prop) => { + if (props[prop] instanceof Binding) { + const binding = props[prop] + delete props[prop] + return [...acc, [prop, binding]] + } + return acc + }, []) + + // collect signal handlers + const onHandlers = Object.keys(props).reduce((acc: any, key) => { + if (key.startsWith("on")) { + const sig = kebabify(key).split("-").slice(1).join("-") + const handler = props[key] + delete props[key] + return [...acc, [sig, handler]] + } + return acc + }, []) + + // set children + const mergedChildren = mergeBindings(children.flat(Infinity)) + if (mergedChildren instanceof Binding) { + this._setChildren(mergedChildren.get()) + this.connect("destroy", mergedChildren.subscribe((v) => { + this._setChildren(v) + })) + } + else { + if (mergedChildren.length > 0) { + this._setChildren(mergedChildren) + } + } + + // setup signal handlers + for (const [signal, callback] of onHandlers) { + if (typeof callback === "function") { + this.connect(signal, callback) + } + else { + this.connect(signal, () => execAsync(callback) + .then(print).catch(console.error)) + } + } + + // setup bindings handlers + for (const [prop, binding] of bindings) { + if (prop === "child" || prop === "children") { + this.connect("destroy", binding.subscribe((v: any) => { + this._setChildren(v) + })) + } + this.connect("destroy", binding.subscribe((v: any) => { + setProp(this, prop, v) + })) + setProp(this, prop, binding.get()) + } + + Object.assign(this, props) + setup?.(this) + } + } + + GObject.registerClass({ + GTypeName: `Astal_${cls.name}`, + Properties: { + "class-name": GObject.ParamSpec.string( + "class-name", "", "", GObject.ParamFlags.READWRITE, "", + ), + "css": GObject.ParamSpec.string( + "css", "", "", GObject.ParamFlags.READWRITE, "", + ), + "cursor": GObject.ParamSpec.string( + "cursor", "", "", GObject.ParamFlags.READWRITE, "default", + ), + "click-through": GObject.ParamSpec.boolean( + "click-through", "", "", GObject.ParamFlags.READWRITE, false, + ), + "no-implicit-destroy": GObject.ParamSpec.boolean( + "no-implicit-destroy", "", "", GObject.ParamFlags.READWRITE, false, + ), + }, + }, Widget) + + return Widget +} + +type BindableProps = { + [K in keyof T]: Binding | T[K]; +} + +type SigHandler< + W extends InstanceType, + Args extends Array, +> = ((self: W, ...args: Args) => unknown) | string | string[] + +export type ConstructProps< + Self extends InstanceType, + Props extends Gtk.Widget.ConstructorProps, + Signals extends Record<`on${string}`, Array> = Record<`on${string}`, any[]>, +> = Partial<{ + // @ts-expect-error can't assign to unknown, but it works as expected though + [S in keyof Signals]: SigHandler +}> & Partial<{ + [Key in `on${string}`]: SigHandler +}> & BindableProps & { + className?: string + css?: string + cursor?: string + clickThrough?: boolean +}> & { + onDestroy?: (self: Self) => unknown + onDraw?: (self: Self) => unknown + onKeyPressEvent?: (self: Self, event: Gdk.Event) => unknown + onKeyReleaseEvent?: (self: Self, event: Gdk.Event) => unknown + onButtonPressEvent?: (self: Self, event: Gdk.Event) => unknown + onButtonReleaseEvent?: (self: Self, event: Gdk.Event) => unknown + onRealize?: (self: Self) => unknown + setup?: (self: Self) => void +} + +export type BindableChild = Gtk.Widget | Binding + +type Cursor = + | "default" + | "help" + | "pointer" + | "context-menu" + | "progress" + | "wait" + | "cell" + | "crosshair" + | "text" + | "vertical-text" + | "alias" + | "copy" + | "no-drop" + | "move" + | "not-allowed" + | "grab" + | "grabbing" + | "all-scroll" + | "col-resize" + | "row-resize" + | "n-resize" + | "e-resize" + | "s-resize" + | "w-resize" + | "ne-resize" + | "nw-resize" + | "sw-resize" + | "se-resize" + | "ew-resize" + | "ns-resize" + | "nesw-resize" + | "nwse-resize" + | "zoom-in" + | "zoom-out" diff --git a/lang/gjs/src/gtk3/index.ts b/lang/gjs/src/gtk3/index.ts new file mode 100644 index 0000000..cfafbda --- /dev/null +++ b/lang/gjs/src/gtk3/index.ts @@ -0,0 +1,9 @@ +import Astal from "gi://Astal?version=3.0" +import Gtk from "gi://Gtk?version=3.0" +import Gdk from "gi://Gdk?version=3.0" +import astalify, { type ConstructProps } from "./astalify.js" + +export { Astal, Gtk, Gdk } +export { default as App } from "./app.js" +export { astalify, ConstructProps } +export * as Widget from "./widget.js" diff --git a/lang/gjs/src/gtk3/jsx-runtime.ts b/lang/gjs/src/gtk3/jsx-runtime.ts new file mode 100644 index 0000000..22dc424 --- /dev/null +++ b/lang/gjs/src/gtk3/jsx-runtime.ts @@ -0,0 +1,96 @@ +import Gtk from "gi://Gtk?version=3.0" +import { mergeBindings, type BindableChild } from "./astalify.js" +import * as Widget from "./widget.js" + +function isArrowFunction(func: any): func is (args: any) => any { + return !Object.hasOwn(func, "prototype") +} + +export function Fragment({ children = [], child }: { + child?: BindableChild + children?: Array +}) { + return mergeBindings([...children, child]) +} + +export function jsx( + ctor: keyof typeof ctors | typeof Gtk.Widget, + { children, ...props }: any, +) { + children ??= [] + + if (!Array.isArray(children)) + children = [children] + + children = children.filter(Boolean) + + if (children.length === 1) + props.child = children[0] + else if (children.length > 1) + props.children = children + + if (typeof ctor === "string") { + return new ctors[ctor](props) + } + + if (isArrowFunction(ctor)) + return ctor(props) + + // @ts-expect-error can be class or function + return new ctor(props) +} + +const ctors = { + box: Widget.Box, + button: Widget.Button, + centerbox: Widget.CenterBox, + circularprogress: Widget.CircularProgress, + drawingarea: Widget.DrawingArea, + entry: Widget.Entry, + eventbox: Widget.EventBox, + // TODO: fixed + // TODO: flowbox + icon: Widget.Icon, + label: Widget.Label, + levelbar: Widget.LevelBar, + // TODO: listbox + overlay: Widget.Overlay, + revealer: Widget.Revealer, + scrollable: Widget.Scrollable, + slider: Widget.Slider, + stack: Widget.Stack, + switch: Widget.Switch, + window: Widget.Window, +} + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX { + type Element = Gtk.Widget + type ElementClass = Gtk.Widget + interface IntrinsicElements { + box: Widget.BoxProps + button: Widget.ButtonProps + centerbox: Widget.CenterBoxProps + circularprogress: Widget.CircularProgressProps + drawingarea: Widget.DrawingAreaProps + entry: Widget.EntryProps + eventbox: Widget.EventBoxProps + // TODO: fixed + // TODO: flowbox + icon: Widget.IconProps + label: Widget.LabelProps + levelbar: Widget.LevelBarProps + // TODO: listbox + overlay: Widget.OverlayProps + revealer: Widget.RevealerProps + scrollable: Widget.ScrollableProps + slider: Widget.SliderProps + stack: Widget.StackProps + switch: Widget.SwitchProps + window: Widget.WindowProps + } + } +} + +export const jsxs = jsx diff --git a/lang/gjs/src/gtk3/widget.ts b/lang/gjs/src/gtk3/widget.ts new file mode 100644 index 0000000..fd70ed6 --- /dev/null +++ b/lang/gjs/src/gtk3/widget.ts @@ -0,0 +1,154 @@ +/* eslint-disable max-len */ +import Astal from "gi://Astal?version=3.0" +import Gtk from "gi://Gtk?version=3.0" +import GObject from "gi://GObject" +import astalify, { type ConstructProps, type BindableChild } from "./astalify.js" + +// Box +Object.defineProperty(Astal.Box.prototype, "children", { + get() { return this.get_children() }, + set(v) { this.set_children(v) }, +}) + +export type BoxProps = ConstructProps +export class Box extends astalify(Astal.Box) { + static { GObject.registerClass({ GTypeName: "Box" }, this) } + constructor(props?: BoxProps, ...children: Array) { super({ children, ...props } as any) } +} + +// Button +export type ButtonProps = ConstructProps +export class Button extends astalify(Astal.Button) { + static { GObject.registerClass({ GTypeName: "Button" }, this) } + constructor(props?: ButtonProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// CenterBox +export type CenterBoxProps = ConstructProps +export class CenterBox extends astalify(Astal.CenterBox) { + static { GObject.registerClass({ GTypeName: "CenterBox" }, this) } + constructor(props?: CenterBoxProps, ...children: Array) { super({ children, ...props } as any) } +} + +// CircularProgress +export type CircularProgressProps = ConstructProps +export class CircularProgress extends astalify(Astal.CircularProgress) { + static { GObject.registerClass({ GTypeName: "CircularProgress" }, this) } + constructor(props?: CircularProgressProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// DrawingArea +export type DrawingAreaProps = ConstructProps +export class DrawingArea extends astalify(Gtk.DrawingArea) { + static { GObject.registerClass({ GTypeName: "DrawingArea" }, this) } + constructor(props?: DrawingAreaProps) { super(props as any) } +} + +// Entry +export type EntryProps = ConstructProps +export class Entry extends astalify(Gtk.Entry) { + static { GObject.registerClass({ GTypeName: "Entry" }, this) } + constructor(props?: EntryProps) { super(props as any) } +} + +// EventBox +export type EventBoxProps = ConstructProps +export class EventBox extends astalify(Astal.EventBox) { + static { GObject.registerClass({ GTypeName: "EventBox" }, this) } + constructor(props?: EventBoxProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// // TODO: Fixed +// // TODO: FlowBox +// +// Icon +export type IconProps = ConstructProps +export class Icon extends astalify(Astal.Icon) { + static { GObject.registerClass({ GTypeName: "Icon" }, this) } + constructor(props?: IconProps) { super(props as any) } +} + +// Label +export type LabelProps = ConstructProps +export class Label extends astalify(Astal.Label) { + static { GObject.registerClass({ GTypeName: "Label" }, this) } + constructor(props?: LabelProps) { super(props as any) } +} + +// LevelBar +export type LevelBarProps = ConstructProps +export class LevelBar extends astalify(Astal.LevelBar) { + static { GObject.registerClass({ GTypeName: "LevelBar" }, this) } + constructor(props?: LevelBarProps) { super(props as any) } +} + +// TODO: ListBox + +// Overlay +export type OverlayProps = ConstructProps +export class Overlay extends astalify(Astal.Overlay) { + static { GObject.registerClass({ GTypeName: "Overlay" }, this) } + constructor(props?: OverlayProps, ...children: Array) { super({ children, ...props } as any) } +} + +// Revealer +export type RevealerProps = ConstructProps +export class Revealer extends astalify(Gtk.Revealer) { + static { GObject.registerClass({ GTypeName: "Revealer" }, this) } + constructor(props?: RevealerProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// Scrollable +export type ScrollableProps = ConstructProps +export class Scrollable extends astalify(Astal.Scrollable) { + static { GObject.registerClass({ GTypeName: "Scrollable" }, this) } + constructor(props?: ScrollableProps, child?: BindableChild) { super({ child, ...props } as any) } +} + +// Slider +export type SliderProps = ConstructProps +export class Slider extends astalify(Astal.Slider) { + static { GObject.registerClass({ GTypeName: "Slider" }, this) } + constructor(props?: SliderProps) { super(props as any) } +} + +// Stack +export type StackProps = ConstructProps +export class Stack extends astalify(Astal.Stack) { + static { GObject.registerClass({ GTypeName: "Stack" }, this) } + constructor(props?: StackProps, ...children: Array) { super({ children, ...props } as any) } +} + +// Switch +export type SwitchProps = ConstructProps +export class Switch extends astalify(Gtk.Switch) { + static { GObject.registerClass({ GTypeName: "Switch" }, this) } + constructor(props?: SwitchProps) { super(props as any) } +} + +// Window +export type WindowProps = ConstructProps +export class Window extends astalify(Astal.Window) { + static { GObject.registerClass({ GTypeName: "Window" }, this) } + constructor(props?: WindowProps, child?: BindableChild) { super({ child, ...props } as any) } +} diff --git a/lang/gjs/src/gtk4/app.ts b/lang/gjs/src/gtk4/app.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/src/gtk4/app.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/src/gtk4/astalify.ts b/lang/gjs/src/gtk4/astalify.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/src/gtk4/astalify.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/src/gtk4/index.ts b/lang/gjs/src/gtk4/index.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/src/gtk4/index.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/src/gtk4/jsx-runtime.ts b/lang/gjs/src/gtk4/jsx-runtime.ts new file mode 100644 index 0000000..d931f73 --- /dev/null +++ b/lang/gjs/src/gtk4/jsx-runtime.ts @@ -0,0 +1 @@ +// TODO: gtk4 diff --git a/lang/gjs/src/index.ts b/lang/gjs/src/index.ts new file mode 100644 index 0000000..161c369 --- /dev/null +++ b/lang/gjs/src/index.ts @@ -0,0 +1,6 @@ +export * from "./process.js" +export * from "./time.js" +export * from "./file.js" +export * from "./gobject.js" +export { bind, default as Binding } from "./binding.js" +export { Variable } from "./variable.js" diff --git a/lang/gjs/src/process.ts b/lang/gjs/src/process.ts new file mode 100644 index 0000000..2f7816b --- /dev/null +++ b/lang/gjs/src/process.ts @@ -0,0 +1,68 @@ +import Astal from "gi://AstalIO" + +type Args = { + cmd: string | string[] + out?: (stdout: string) => void + err?: (stderr: string) => void +} + +export function subprocess(args: Args): Astal.Process + +export function subprocess( + cmd: string | string[], + onOut?: (stdout: string) => void, + onErr?: (stderr: string) => void, +): Astal.Process + +export function subprocess( + argsOrCmd: Args | string | string[], + onOut: (stdout: string) => void = print, + onErr: (stderr: string) => void = printerr, +) { + const args = Array.isArray(argsOrCmd) || typeof argsOrCmd === "string" + const { cmd, err, out } = { + cmd: args ? argsOrCmd : argsOrCmd.cmd, + err: args ? onErr : argsOrCmd.err || onErr, + out: args ? onOut : argsOrCmd.out || onOut, + } + + const proc = Array.isArray(cmd) + ? Astal.Process.subprocessv(cmd) + : Astal.Process.subprocess(cmd) + + proc.connect("stdout", (_, stdout: string) => out(stdout)) + proc.connect("stderr", (_, stderr: string) => err(stderr)) + return proc +} + +/** @throws {GLib.Error} Throws stderr */ +export function exec(cmd: string | string[]) { + return Array.isArray(cmd) + ? Astal.Process.execv(cmd) + : Astal.Process.exec(cmd) +} + +export function execAsync(cmd: string | string[]): Promise { + return new Promise((resolve, reject) => { + if (Array.isArray(cmd)) { + Astal.Process.exec_asyncv(cmd, (_, res) => { + try { + resolve(Astal.Process.exec_asyncv_finish(res)) + } + catch (error) { + reject(error) + } + }) + } + else { + Astal.Process.exec_async(cmd, (_, res) => { + try { + resolve(Astal.Process.exec_finish(res)) + } + catch (error) { + reject(error) + } + }) + } + }) +} diff --git a/lang/gjs/src/time.ts b/lang/gjs/src/time.ts new file mode 100644 index 0000000..a7e1e61 --- /dev/null +++ b/lang/gjs/src/time.ts @@ -0,0 +1,13 @@ +import Astal from "gi://AstalIO" + +export function interval(interval: number, callback?: () => void) { + return Astal.Time.interval(interval, () => void callback?.()) +} + +export function timeout(timeout: number, callback?: () => void) { + return Astal.Time.timeout(timeout, () => void callback?.()) +} + +export function idle(callback?: () => void) { + return Astal.Time.idle(() => void callback?.()) +} diff --git a/lang/gjs/src/variable.ts b/lang/gjs/src/variable.ts new file mode 100644 index 0000000..9b3d3d2 --- /dev/null +++ b/lang/gjs/src/variable.ts @@ -0,0 +1,230 @@ +import Astal from "gi://AstalIO" +import Binding, { type Connectable, type Subscribable } from "./binding.js" +import { interval } from "./time.js" +import { execAsync, subprocess } from "./process.js" + +class VariableWrapper extends Function { + private variable!: Astal.VariableBase + private errHandler? = console.error + + private _value: T + private _poll?: Astal.Time + private _watch?: Astal.Process + + private pollInterval = 1000 + private pollExec?: string[] | string + private pollTransform?: (stdout: string, prev: T) => T + private pollFn?: (prev: T) => T | Promise + + private watchTransform?: (stdout: string, prev: T) => T + private watchExec?: string[] | string + + constructor(init: T) { + super() + this._value = init + this.variable = new Astal.VariableBase() + this.variable.connect("dropped", () => { + this.stopWatch() + this.stopPoll() + }) + this.variable.connect("error", (_, err) => this.errHandler?.(err)) + return new Proxy(this, { + apply: (target, _, args) => target._call(args[0]), + }) + } + + private _call(transform?: (value: T) => R): Binding { + const b = Binding.bind(this) + return transform ? b.as(transform) : b as unknown as Binding + } + + toString() { + return String(`Variable<${this.get()}>`) + } + + get(): T { return this._value } + set(value: T) { + if (value !== this._value) { + this._value = value + this.variable.emit("changed") + } + } + + startPoll() { + if (this._poll) + return + + if (this.pollFn) { + this._poll = interval(this.pollInterval, () => { + const v = this.pollFn!(this.get()) + if (v instanceof Promise) { + v.then(v => this.set(v)) + .catch(err => this.variable.emit("error", err)) + } + else { + this.set(v) + } + }) + } + else if (this.pollExec) { + this._poll = interval(this.pollInterval, () => { + execAsync(this.pollExec!) + .then(v => this.set(this.pollTransform!(v, this.get()))) + .catch(err => this.variable.emit("error", err)) + }) + } + } + + startWatch() { + if (this._watch) + return + + this._watch = subprocess({ + cmd: this.watchExec!, + out: out => this.set(this.watchTransform!(out, this.get())), + err: err => this.variable.emit("error", err), + }) + } + + stopPoll() { + this._poll?.cancel() + delete this._poll + } + + stopWatch() { + this._watch?.kill() + delete this._watch + } + + isPolling() { return !!this._poll } + isWatching() { return !!this._watch } + + drop() { + this.variable.emit("dropped") + } + + onDropped(callback: () => void) { + this.variable.connect("dropped", callback) + return this as unknown as Variable + } + + onError(callback: (err: string) => void) { + delete this.errHandler + this.variable.connect("error", (_, err) => callback(err)) + return this as unknown as Variable + } + + subscribe(callback: (value: T) => void) { + const id = this.variable.connect("changed", () => { + callback(this.get()) + }) + return () => this.variable.disconnect(id) + } + + poll( + interval: number, + exec: string | string[], + transform?: (stdout: string, prev: T) => T + ): Variable + + poll( + interval: number, + callback: (prev: T) => T | Promise + ): Variable + + poll( + interval: number, + exec: string | string[] | ((prev: T) => T | Promise), + transform: (stdout: string, prev: T) => T = out => out as T, + ) { + this.stopPoll() + this.pollInterval = interval + this.pollTransform = transform + if (typeof exec === "function") { + this.pollFn = exec + delete this.pollExec + } + else { + this.pollExec = exec + delete this.pollFn + } + this.startPoll() + return this as unknown as Variable + } + + watch( + exec: string | string[], + transform: (stdout: string, prev: T) => T = out => out as T, + ) { + this.stopWatch() + this.watchExec = exec + this.watchTransform = transform + this.startWatch() + return this as unknown as Variable + } + + observe( + objs: Array<[obj: Connectable, signal: string]>, + callback: (...args: any[]) => T, + ): Variable + + observe( + obj: Connectable, + signal: string, + callback: (...args: any[]) => T, + ): Variable + + observe( + objs: Connectable | Array<[obj: Connectable, signal: string]>, + sigOrFn: string | ((obj: Connectable, ...args: any[]) => T), + callback?: (obj: Connectable, ...args: any[]) => T, + ) { + const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => this.get()) + const set = (obj: Connectable, ...args: any[]) => this.set(f(obj, ...args)) + + if (Array.isArray(objs)) { + for (const obj of objs) { + const [o, s] = obj + const id = o.connect(s, set) + this.onDropped(() => o.disconnect(id)) + } + } + else { + if (typeof sigOrFn === "string") { + const id = objs.connect(sigOrFn, set) + this.onDropped(() => objs.disconnect(id)) + } + } + + return this as unknown as Variable + } + + static derive< + const Deps extends Array>, + Args extends { + [K in keyof Deps]: Deps[K] extends Subscribable ? T : never + }, + V = Args, + >(deps: Deps, fn: (...args: Args) => V = (...args) => args as unknown as V) { + const update = () => fn(...deps.map(d => d.get()) as Args) + const derived = new Variable(update()) + const unsubs = deps.map(dep => dep.subscribe(() => derived.set(update()))) + derived.onDropped(() => unsubs.map(unsub => unsub())) + return derived + } +} + +export interface Variable extends Omit, "bind"> { + (transform: (value: T) => R): Binding + (): Binding +} + +export const Variable = new Proxy(VariableWrapper as any, { + apply: (_t, _a, args) => new VariableWrapper(args[0]), +}) as { + derive: typeof VariableWrapper["derive"] + (init: T): Variable + new(init: T): Variable +} + +export default Variable diff --git a/lang/gjs/tsconfig.json b/lang/gjs/tsconfig.json index 71fd218..171e75b 100644 --- a/lang/gjs/tsconfig.json +++ b/lang/gjs/tsconfig.json @@ -10,9 +10,7 @@ }, "include": [ "@girs", - "lib/*", - // "gtk3/*", - // "gtk4/*", + "src/*.ts", "index.ts", ] } diff --git a/lang/lua/astal-dev-1.rockspec b/lang/lua/astal-dev-1.rockspec index d392a79..3970672 100644 --- a/lang/lua/astal-dev-1.rockspec +++ b/lang/lua/astal-dev-1.rockspec @@ -19,13 +19,19 @@ dependencies = { build = { type = "builtin", modules = { - ["astal.application"] = "lib/application.lua", - ["astal.binding"] = "lib/binding.lua", - ["astal.init"] = "lib/init.lua", - ["astal.process"] = "lib/process.lua", - ["astal.time"] = "lib/time.lua", - ["astal.variable"] = "lib/variable.lua", - ["astal.widget"] = "lib/widget.lua", - ["astal.file"] = "lib/file.lua", + ["astal.binding"] = "astal/binding.lua", + ["astal.file"] = "astal/file.lua", + ["astal.init"] = "astal/init.lua", + ["astal.process"] = "astal/process.lua", + ["astal.time"] = "astal/time.lua", + ["astal.variable"] = "astal/variable.lua", + ["astal.gtk3.app"] = "astal/gtk3/app.lua", + ["astal.gtk3.init"] = "astal/gtk3/init.lua", + ["astal.gtk3.astalify"] = "astal/gtk3/astalify.lua", + ["astal.gtk3.widget"] = "astal/gtk3/widget.lua", + -- ["astal.gtk4.app"] = "astal/gtk4/app.lua", + -- ["astal.gtk4.init"] = "astal/gtk4/init.lua", + -- ["astal.gtk4.astalify"] = "astal/gtk4/astalify.lua", + -- ["astal.gtk4.widget"] = "astal/gtk4/widget.lua", }, } diff --git a/lang/lua/astal/binding.lua b/lang/lua/astal/binding.lua new file mode 100644 index 0000000..ba1e6e4 --- /dev/null +++ b/lang/lua/astal/binding.lua @@ -0,0 +1,71 @@ +local lgi = require("lgi") +local GObject = lgi.require("GObject", "2.0") + +---@class Binding +---@field emitter table|Variable +---@field property? string +---@field transformFn function +local Binding = {} + +---@param emitter table +---@param property? string +---@return Binding +function Binding.new(emitter, property) + return setmetatable({ + emitter = emitter, + property = property, + transformFn = function(v) + return v + end, + }, Binding) +end + +function Binding:__tostring() + local str = "Binding<" .. tostring(self.emitter) + if self.property ~= nil then + str = str .. ", " .. self.property + end + return str .. ">" +end + +function Binding:get() + if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then + return self.transformFn(self.emitter[self.property]) + end + if type(self.emitter.get) == "function" then + return self.transformFn(self.emitter:get()) + end + error("can not get: Not a GObject or a Variable " + self) +end + +---@param transform fun(value: any): any +---@return Binding +function Binding:as(transform) + local b = Binding.new(self.emitter, self.property) + b.transformFn = function(v) + return transform(self.transformFn(v)) + end + return b +end + +---@param callback fun(value: any) +---@return function +function Binding:subscribe(callback) + if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then + local id = self.emitter.on_notify:connect(function() + callback(self:get()) + end, self.property, false) + return function() + GObject.signal_handler_disconnect(self.emitter, id) + end + end + if type(self.emitter.subscribe) == "function" then + return self.emitter:subscribe(function() + callback(self:get()) + end) + end + error("can not subscribe: Not a GObject or a Variable " + self) +end + +Binding.__index = Binding +return Binding diff --git a/lang/lua/astal/file.lua b/lang/lua/astal/file.lua new file mode 100644 index 0000000..e3be783 --- /dev/null +++ b/lang/lua/astal/file.lua @@ -0,0 +1,45 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") +local GObject = lgi.require("GObject", "2.0") + +local M = {} + +---@param path string +---@return string +function M.read_file(path) + return Astal.read_file(path) +end + +---@param path string +---@param callback fun(content: string, err: string): nil +function M.read_file_async(path, callback) + Astal.read_file_async(path, function(_, res) + local content, err = Astal.read_file_finish(res) + callback(content, err) + end) +end + +---@param path string +---@param content string +function M.write_file(path, content) + Astal.write_file(path, content) +end + +---@param path string +---@param content string +---@param callback? fun(err: string): nil +function M.write_file_async(path, content, callback) + Astal.write_file_async(path, content, function(_, res) + if type(callback) == "function" then + callback(Astal.write_file_finish(res)) + end + end) +end + +---@param path string +---@param callback fun(file: string, event: integer): nil +function M.monitor_file(path, callback) + return Astal.monitor_file(path, GObject.Closure(callback)) +end + +return M diff --git a/lang/lua/astal/gtk3/app.lua b/lang/lua/astal/gtk3/app.lua new file mode 100644 index 0000000..7895f69 --- /dev/null +++ b/lang/lua/astal/gtk3/app.lua @@ -0,0 +1,96 @@ +local lgi = require("lgi") +local Astal = lgi.require("Astal", "3.0") +local AstalIO = lgi.require("AstalIO", "0.1") + +local AstalLua = Astal.Application:derive("AstalLua") +local request_handler + +function AstalLua:do_request(msg, conn) + if type(request_handler) == "function" then + request_handler(msg, function(response) + AstalIO.write_sock(conn, tostring(response), function(_, res) + AstalIO.write_sock_finish(res) + end) + end) + else + Astal.Application.do_request(self, msg, conn) + end +end + +function AstalLua:quit(code) + Astal.Application.quit(self) + os.exit(code) +end + +local app = AstalLua() + +---@class StartConfig +---@field icons? string +---@field instance_name? string +---@field gtk_theme? string +---@field icon_theme? string +---@field cursor_theme? string +---@field css? string +---@field hold? boolean +---@field request_handler? fun(msg: string, response: fun(res: any)) +---@field main? fun(...): unknown +---@field client? fun(message: fun(msg: string): string, ...): unknown + +---@param config StartConfig | nil +function Astal.Application:start(config) + if config == nil then + config = {} + end + + if config.client == nil then + config.client = function() + print('Astal instance "' .. app.instance_name .. '" is already running') + os.exit(1) + end + end + + if config.hold == nil then + config.hold = true + end + + request_handler = config.request_handler + + if config.css then + self:apply_css(config.css) + end + if config.icons then + self:add_icons(config.icons) + end + if config.instance_name then + self.instance_name = config.instance_name + end + if config.gtk_theme then + self.gtk_theme = config.gtk_theme + end + if config.icon_theme then + self.icon_theme = config.icon_theme + end + if config.cursor_theme then + self.cursor_theme = config.cursor_theme + end + + app.on_activate = function() + if type(config.main) == "function" then + config.main(table.unpack(arg)) + end + if config.hold then + self:hold() + end + end + + local _, err = app:acquire_socket() + if err ~= nil then + return config.client(function(msg) + return AstalIO.send_message(self.instance_name, msg) + end, table.unpack(arg)) + end + + self:run(nil) +end + +return app diff --git a/lang/lua/astal/gtk3/astalify.lua b/lang/lua/astal/gtk3/astalify.lua new file mode 100644 index 0000000..065de40 --- /dev/null +++ b/lang/lua/astal/gtk3/astalify.lua @@ -0,0 +1,236 @@ +local lgi = require("lgi") +local Astal = lgi.require("Astal", "3.0") +local Gtk = lgi.require("Gtk", "3.0") +local GObject = lgi.require("GObject", "2.0") +local Binding = require("astal.lib.binding") +local Variable = require("astal.lib.variable") +local exec_async = require("astal.lib.process").exec_async + +local function filter(tbl, fn) + local copy = {} + for key, value in pairs(tbl) do + if fn(value, key) then + if type(key) == "number" then + table.insert(copy, value) + else + copy[key] = value + end + end + end + return copy +end + +local function map(tbl, fn) + local copy = {} + for key, value in pairs(tbl) do + copy[key] = fn(value) + end + return copy +end + +local flatten +flatten = function(tbl) + local copy = {} + for _, value in pairs(tbl) do + if type(value) == "table" and getmetatable(value) == nil then + for _, inner in pairs(flatten(value)) do + table.insert(copy, inner) + end + else + table.insert(copy, value) + end + end + return copy +end + +local function includes(tbl, elem) + for _, value in pairs(tbl) do + if value == elem then + return true + end + end + return false +end + +local function set_children(parent, children) + children = map(flatten(children), function(item) + if Gtk.Widget:is_type_of(item) then + return item + end + return Gtk.Label({ + visible = true, + label = tostring(item), + }) + end) + + -- remove + if Gtk.Bin:is_type_of(parent) then + local ch = parent:get_child() + if ch ~= nil then + parent:remove(ch) + end + if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then + ch:destroy() + end + elseif Gtk.Container:is_type_of(parent) then + for _, ch in ipairs(parent:get_children()) do + parent:remove(ch) + if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then + ch:destroy() + end + end + end + + -- TODO: add more container types + if Astal.Box:is_type_of(parent) then + parent:set_children(children) + elseif Astal.Stack:is_type_of(parent) then + parent:set_children(children) + elseif Astal.CenterBox:is_type_of(parent) then + parent.start_widget = children[1] + parent.center_widget = children[2] + parent.end_widget = children[3] + elseif Astal.Overlay:is_type_of(parent) then + parent:set_child(children[1]) + children[1] = nil + parent:set_overlays(children) + elseif Gtk.Container:is_type_of(parent) then + for _, child in pairs(children) do + if Gtk.Widget:is_type_of(child) then + parent:add(child) + end + end + end +end + +local function merge_bindings(array) + local function get_values(...) + local args = { ... } + local i = 0 + return map(array, function(value) + if getmetatable(value) == Binding then + i = i + 1 + return args[i] + else + return value + end + end) + end + + local bindings = filter(array, function(v) + return getmetatable(v) == Binding + end) + + if #bindings == 0 then + return array + end + + if #bindings == 1 then + return bindings[1]:as(get_values) + end + + return Variable.derive(bindings, get_values)() +end + +return function(ctor) + function ctor:hook(object, signalOrCallback, callback) + if GObject.Object:is_type_of(object) and type(signalOrCallback) == "string" then + local id + if string.sub(signalOrCallback, 1, 8) == "notify::" then + local prop = string.gsub(signalOrCallback, "notify::", "") + id = object.on_notify:connect(function() + callback(self, object[prop]) + end, prop, false) + else + id = object["on_" .. signalOrCallback]:connect(function(_, ...) + callback(self, ...) + end) + end + self.on_destroy = function() + GObject.signal_handler_disconnect(object, id) + end + elseif type(object.subscribe) == "function" then + local unsub = object.subscribe(function(...) + signalOrCallback(self, ...) + end) + self.on_destroy = unsub + else + error("can not hook: not gobject+signal or subscribable") + end + end + + function ctor:toggle_class_name(name, on) + Astal.widget_toggle_class_name(self, name, on) + end + + return function(tbl) + if tbl == nil then + tbl = {} + end + + local bindings = {} + local setup = tbl.setup + + -- collect children + local children = merge_bindings(flatten(filter(tbl, function(_, key) + return type(key) == "number" + end))) + + -- default visible to true + if type(tbl.visible) ~= "boolean" then + tbl.visible = true + end + + -- collect props + local props = filter(tbl, function(_, key) + return type(key) == "string" and key ~= "setup" + end) + + -- collect signal handlers + for prop, value in pairs(props) do + if string.sub(prop, 0, 2) == "on" and type(value) ~= "function" then + props[prop] = function() + exec_async(value, print) + end + end + end + + -- collect bindings + for prop, value in pairs(props) do + if getmetatable(value) == Binding then + bindings[prop] = value + props[prop] = value:get() + end + end + + -- construct, attach bindings, add children + local widget = ctor() + + if getmetatable(children) == Binding then + set_children(widget, children:get()) + widget.on_destroy = children:subscribe(function(v) + set_children(widget, v) + end) + else + if #children > 0 then + set_children(widget, children) + end + end + + for prop, binding in pairs(bindings) do + widget.on_destroy = binding:subscribe(function(v) + widget[prop] = v + end) + end + + for prop, value in pairs(props) do + widget[prop] = value + end + + if type(setup) == "function" then + setup(widget) + end + + return widget + end +end diff --git a/lang/lua/astal/gtk3/init.lua b/lang/lua/astal/gtk3/init.lua new file mode 100644 index 0000000..6fb5455 --- /dev/null +++ b/lang/lua/astal/gtk3/init.lua @@ -0,0 +1,5 @@ +return { + App = require("astal.gtk3.app"), + astalify = require("astal.gtk3.astalify"), + Widget = require("astal.gtk3.widget"), +} diff --git a/lang/lua/astal/gtk3/widget.lua b/lang/lua/astal/gtk3/widget.lua new file mode 100644 index 0000000..beaad6c --- /dev/null +++ b/lang/lua/astal/gtk3/widget.lua @@ -0,0 +1,90 @@ +local lgi = require("lgi") +local Astal = lgi.require("Astal", "3.0") +local Gtk = lgi.require("Gtk", "3.0") +local astalify = require("astal.gtk3.astalify") + +local Widget = { + astalify = astalify, + Box = astalify(Astal.Box), + Button = astalify(Astal.Button), + CenterBox = astalify(Astal.CenterBox), + CircularProgress = astalify(Astal.CircularProgress), + DrawingArea = astalify(Gtk.DrawingArea), + Entry = astalify(Gtk.Entry), + EventBox = astalify(Astal.EventBox), + -- TODO: Fixed + -- TODO: FlowBox + Icon = astalify(Astal.Icon), + Label = astalify(Gtk.Label), + LevelBar = astalify(Astal.LevelBar), + -- TODO: ListBox + Overlay = astalify(Astal.Overlay), + Revealer = astalify(Gtk.Revealer), + Scrollable = astalify(Astal.Scrollable), + Slider = astalify(Astal.Slider), + Stack = astalify(Astal.Stack), + Switch = astalify(Gtk.Switch), + Window = astalify(Astal.Window), +} + +Gtk.Widget._attribute.css = { + get = Astal.widget_get_css, + set = Astal.widget_set_css, +} + +Gtk.Widget._attribute.class_name = { + get = function(self) + local result = "" + local strings = Astal.widget_get_class_names(self) + for i, str in ipairs(strings) do + result = result .. str + if i < #strings then + result = result .. " " + end + end + return result + end, + set = function(self, class_name) + local names = {} + for word in class_name:gmatch("%S+") do + table.insert(names, word) + end + Astal.widget_set_class_names(self, names) + end, +} + +Gtk.Widget._attribute.cursor = { + get = Astal.widget_get_cursor, + set = Astal.widget_set_cursor, +} + +Gtk.Widget._attribute.click_through = { + get = Astal.widget_get_click_through, + set = Astal.widget_set_click_through, +} + +local no_implicit_destroy = {} +Gtk.Widget._attribute.no_implicit_destroy = { + get = function(self) + return no_implicit_destroy[self] or false + end, + set = function(self, v) + if no_implicit_destroy[self] == nil then + self.on_destroy = function() + no_implicit_destroy[self] = nil + end + end + no_implicit_destroy[self] = v + end, +} + +Astal.Box._attribute.children = { + get = Astal.Box.get_children, + set = Astal.Box.set_children, +} + +return setmetatable(Widget, { + __call = function(_, ctor) + return astalify(ctor) + end, +}) diff --git a/lang/lua/astal/init.lua b/lang/lua/astal/init.lua new file mode 100644 index 0000000..f442db0 --- /dev/null +++ b/lang/lua/astal/init.lua @@ -0,0 +1,27 @@ +local lgi = require("lgi") +local Binding = require("astal.binding") +local File = require("astal.file") +local Process = require("astal.proc") +local Time = require("astal.time") +local Variable = require("astal.variable") + +return { + Variable = Variable, + bind = Binding.new, + + interval = Time.interval, + timeout = Time.timeout, + idle = Time.idle, + + subprocess = Process.subprocess, + exec = Process.exec, + exec_async = Process.exec_async, + + read_file = File.read_file, + read_file_async = File.read_file_async, + write_file = File.write_file, + write_file_async = File.write_file_async, + monitor_file = File.monitor_file, + + require = lgi.require, +} diff --git a/lang/lua/astal/process.lua b/lang/lua/astal/process.lua new file mode 100644 index 0000000..b8b7436 --- /dev/null +++ b/lang/lua/astal/process.lua @@ -0,0 +1,78 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") + +local M = {} + +---@param commandline string | string[] +---@param on_stdout? fun(out: string): nil +---@param on_stderr? fun(err: string): nil +---@return { kill: function } | nil proc +function M.subprocess(commandline, on_stdout, on_stderr) + if on_stdout == nil then + on_stdout = function(out) + io.stdout:write(tostring(out) .. "\n") + end + end + + if on_stderr == nil then + on_stderr = function(err) + io.stderr:write(tostring(err) .. "\n") + end + end + + local proc, err + if type(commandline) == "table" then + proc, err = Astal.Process.subprocessv(commandline) + else + proc, err = Astal.Process.subprocess(commandline) + end + if err ~= nil then + err(err) + return nil + end + proc.on_stdout = function(_, stdoud) + on_stdout(stdoud) + end + proc.on_stderr = function(_, stderr) + on_stderr(stderr) + end + return proc +end + +---@param commandline string | string[] +---@return string, string +function M.exec(commandline) + if type(commandline) == "table" then + return Astal.Process.execv(commandline) + else + return Astal.Process.exec(commandline) + end +end + +---@param commandline string | string[] +---@param callback? fun(out: string, err: string): nil +function M.exec_async(commandline, callback) + if callback == nil then + callback = function(out, err) + if err ~= nil then + io.stdout:write(tostring(out) .. "\n") + else + io.stderr:write(tostring(err) .. "\n") + end + end + end + + if type(commandline) == "table" then + Astal.Process.exec_asyncv(commandline, function(_, res) + local out, err = Astal.Process.exec_asyncv_finish(res) + callback(out, err) + end) + else + Astal.Process.exec_async(commandline, function(_, res) + local out, err = Astal.Process.exec_finish(res) + callback(out, err) + end) + end +end + +return M diff --git a/lang/lua/astal/time.lua b/lang/lua/astal/time.lua new file mode 100644 index 0000000..7719da9 --- /dev/null +++ b/lang/lua/astal/time.lua @@ -0,0 +1,27 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") +local GObject = lgi.require("GObject", "2.0") + +local M = {} + +---@param interval number +---@param fn function +---@return { cancel: function, on_now: function } +function M.interval(interval, fn) + return Astal.Time.interval(interval, GObject.Closure(fn)) +end + +---@param timeout number +---@param fn function +---@return { cancel: function, on_now: function } +function M.timeout(timeout, fn) + return Astal.Time.timeout(timeout, GObject.Closure(fn)) +end + +---@param fn function +---@return { cancel: function, on_now: function } +function M.idle(fn) + return Astal.Time.idle(GObject.Closure(fn)) +end + +return M diff --git a/lang/lua/astal/variable.lua b/lang/lua/astal/variable.lua new file mode 100644 index 0000000..5a5e169 --- /dev/null +++ b/lang/lua/astal/variable.lua @@ -0,0 +1,276 @@ +local lgi = require("lgi") +local Astal = lgi.require("AstalIO", "0.1") +local GObject = lgi.require("GObject", "2.0") +local Binding = require("astal.binding") +local Time = require("astal.time") +local Process = require("astal.process") + +---@class Variable +---@field private variable table +---@field private err_handler? function +---@field private _value any +---@field private _poll? table +---@field private _watch? table +---@field private poll_interval number +---@field private poll_exec? string[] | string +---@field private poll_transform? fun(next: any, prev: any): any +---@field private poll_fn? function +---@field private watch_transform? fun(next: any, prev: any): any +---@field private watch_exec? string[] | string +local Variable = {} +Variable.__index = Variable + +---@param value any +---@return Variable +function Variable.new(value) + local v = Astal.VariableBase() + local variable = setmetatable({ + variable = v, + _value = value, + }, Variable) + v.on_dropped = function() + variable:stop_watch() + variable:stop_watch() + end + v.on_error = function(_, err) + if variable.err_handler then + variable.err_handler(err) + end + end + return variable +end + +---@param transform function +---@return Binding +function Variable:__call(transform) + if transform == nil then + transform = function(v) + return v + end + return Binding.new(self) + end + return Binding.new(self):as(transform) +end + +function Variable:__tostring() + return "Variable<" .. tostring(self:get()) .. ">" +end + +function Variable:get() + return self._value or nil +end + +function Variable:set(value) + if value ~= self:get() then + self._value = value + self.variable:emit_changed() + end +end + +function Variable:start_poll() + if self._poll ~= nil then + return + end + + if self.poll_fn then + self._poll = Time.interval(self.poll_interval, function() + self:set(self.poll_fn(self:get())) + end) + elseif self.poll_exec then + self._poll = Time.interval(self.poll_interval, function() + Process.exec_async(self.poll_exec, function(out, err) + if err ~= nil then + return self.variable.emit_error(err) + end + self:set(self.poll_transform(out, self:get())) + end) + end) + end +end + +function Variable:start_watch() + if self._watch then + return + end + + self._watch = Process.subprocess(self.watch_exec, function(out) + self:set(self.watch_transform(out, self:get())) + end, function(err) + self.variable.emit_error(err) + end) +end + +function Variable:stop_poll() + if self._poll then + self._poll.cancel() + end + self._poll = nil +end + +function Variable:stop_watch() + if self._watch then + self._watch.kill() + end + self._watch = nil +end + +function Variable:is_polling() + return self._poll ~= nil +end + +function Variable:is_watching() + return self._watch ~= nil +end + +function Variable:drop() + self.variable.emit_dropped() +end + +---@param callback function +---@return Variable +function Variable:on_dropped(callback) + self.variable.on_dropped = callback + return self +end + +---@param callback function +---@return Variable +function Variable:on_error(callback) + self.err_handler = nil + self.variable.on_eror = function(_, err) + callback(err) + end + return self +end + +---@param callback fun(value: any) +---@return function +function Variable:subscribe(callback) + local id = self.variable.on_changed:connect(function() + callback(self:get()) + end) + return function() + GObject.signal_handler_disconnect(self.variable, id) + end +end + +---@param interval number +---@param exec string | string[] | function +---@param transform? fun(next: any, prev: any): any +function Variable:poll(interval, exec, transform) + if transform == nil then + transform = function(next) + return next + end + end + self:stop_poll() + self.poll_interval = interval + self.poll_transform = transform + + if type(exec) == "function" then + self.poll_fn = exec + self.poll_exec = nil + else + self.poll_exec = exec + self.poll_fn = nil + end + self:start_poll() + return self +end + +---@param exec string | string[] +---@param transform? fun(next: any, prev: any): any +function Variable:watch(exec, transform) + if transform == nil then + transform = function(next) + return next + end + end + self:stop_poll() + self.watch_exec = exec + self.watch_transform = transform + self:start_watch() + return self +end + +---@param object table | table[] +---@param sigOrFn string | fun(...): any +---@param callback fun(...): any +---@return Variable +function Variable:observe(object, sigOrFn, callback) + local f + if type(sigOrFn) == "function" then + f = sigOrFn + elseif type(callback) == "function" then + f = callback + else + f = function() + return self:get() + end + end + local set = function(...) + self:set(f(...)) + end + + if type(sigOrFn) == "string" then + object["on_" .. sigOrFn]:connect(set) + else + for _, obj in ipairs(object) do + obj[1]["on_" .. obj[2]]:connect(set) + end + end + return self +end + +---@param deps Variable | (Binding | Variable)[] +---@param transform? fun(...): any +---@return Variable +function Variable.derive(deps, transform) + if type(transform) == "nil" then + transform = function(...) + return { ... } + end + end + + if getmetatable(deps) == Variable then + local var = Variable.new(transform(deps:get())) + deps:subscribe(function(v) + var:set(transform(v)) + end) + return var + end + + for i, var in ipairs(deps) do + if getmetatable(var) == Variable then + deps[i] = Binding.new(var) + end + end + + local update = function() + local params = {} + for i, binding in ipairs(deps) do + params[i] = binding:get() + end + return transform(table.unpack(params), 1, #deps) + end + + local var = Variable.new(update()) + + local unsubs = {} + for i, b in ipairs(deps) do + unsubs[i] = b:subscribe(update) + end + + var.variable.on_dropped = function() + for _, unsub in ipairs(unsubs) do + unsub() + end + end + return var +end + +return setmetatable(Variable, { + __call = function(_, v) + return Variable.new(v) + end, +}) diff --git a/lang/lua/gtk3/app.lua b/lang/lua/gtk3/app.lua deleted file mode 100644 index 7895f69..0000000 --- a/lang/lua/gtk3/app.lua +++ /dev/null @@ -1,96 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "3.0") -local AstalIO = lgi.require("AstalIO", "0.1") - -local AstalLua = Astal.Application:derive("AstalLua") -local request_handler - -function AstalLua:do_request(msg, conn) - if type(request_handler) == "function" then - request_handler(msg, function(response) - AstalIO.write_sock(conn, tostring(response), function(_, res) - AstalIO.write_sock_finish(res) - end) - end) - else - Astal.Application.do_request(self, msg, conn) - end -end - -function AstalLua:quit(code) - Astal.Application.quit(self) - os.exit(code) -end - -local app = AstalLua() - ----@class StartConfig ----@field icons? string ----@field instance_name? string ----@field gtk_theme? string ----@field icon_theme? string ----@field cursor_theme? string ----@field css? string ----@field hold? boolean ----@field request_handler? fun(msg: string, response: fun(res: any)) ----@field main? fun(...): unknown ----@field client? fun(message: fun(msg: string): string, ...): unknown - ----@param config StartConfig | nil -function Astal.Application:start(config) - if config == nil then - config = {} - end - - if config.client == nil then - config.client = function() - print('Astal instance "' .. app.instance_name .. '" is already running') - os.exit(1) - end - end - - if config.hold == nil then - config.hold = true - end - - request_handler = config.request_handler - - if config.css then - self:apply_css(config.css) - end - if config.icons then - self:add_icons(config.icons) - end - if config.instance_name then - self.instance_name = config.instance_name - end - if config.gtk_theme then - self.gtk_theme = config.gtk_theme - end - if config.icon_theme then - self.icon_theme = config.icon_theme - end - if config.cursor_theme then - self.cursor_theme = config.cursor_theme - end - - app.on_activate = function() - if type(config.main) == "function" then - config.main(table.unpack(arg)) - end - if config.hold then - self:hold() - end - end - - local _, err = app:acquire_socket() - if err ~= nil then - return config.client(function(msg) - return AstalIO.send_message(self.instance_name, msg) - end, table.unpack(arg)) - end - - self:run(nil) -end - -return app diff --git a/lang/lua/gtk3/astalify.lua b/lang/lua/gtk3/astalify.lua deleted file mode 100644 index 065de40..0000000 --- a/lang/lua/gtk3/astalify.lua +++ /dev/null @@ -1,236 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "3.0") -local Gtk = lgi.require("Gtk", "3.0") -local GObject = lgi.require("GObject", "2.0") -local Binding = require("astal.lib.binding") -local Variable = require("astal.lib.variable") -local exec_async = require("astal.lib.process").exec_async - -local function filter(tbl, fn) - local copy = {} - for key, value in pairs(tbl) do - if fn(value, key) then - if type(key) == "number" then - table.insert(copy, value) - else - copy[key] = value - end - end - end - return copy -end - -local function map(tbl, fn) - local copy = {} - for key, value in pairs(tbl) do - copy[key] = fn(value) - end - return copy -end - -local flatten -flatten = function(tbl) - local copy = {} - for _, value in pairs(tbl) do - if type(value) == "table" and getmetatable(value) == nil then - for _, inner in pairs(flatten(value)) do - table.insert(copy, inner) - end - else - table.insert(copy, value) - end - end - return copy -end - -local function includes(tbl, elem) - for _, value in pairs(tbl) do - if value == elem then - return true - end - end - return false -end - -local function set_children(parent, children) - children = map(flatten(children), function(item) - if Gtk.Widget:is_type_of(item) then - return item - end - return Gtk.Label({ - visible = true, - label = tostring(item), - }) - end) - - -- remove - if Gtk.Bin:is_type_of(parent) then - local ch = parent:get_child() - if ch ~= nil then - parent:remove(ch) - end - if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then - ch:destroy() - end - elseif Gtk.Container:is_type_of(parent) then - for _, ch in ipairs(parent:get_children()) do - parent:remove(ch) - if ch ~= nil and not includes(children, ch) and not parent.no_implicit_destroy then - ch:destroy() - end - end - end - - -- TODO: add more container types - if Astal.Box:is_type_of(parent) then - parent:set_children(children) - elseif Astal.Stack:is_type_of(parent) then - parent:set_children(children) - elseif Astal.CenterBox:is_type_of(parent) then - parent.start_widget = children[1] - parent.center_widget = children[2] - parent.end_widget = children[3] - elseif Astal.Overlay:is_type_of(parent) then - parent:set_child(children[1]) - children[1] = nil - parent:set_overlays(children) - elseif Gtk.Container:is_type_of(parent) then - for _, child in pairs(children) do - if Gtk.Widget:is_type_of(child) then - parent:add(child) - end - end - end -end - -local function merge_bindings(array) - local function get_values(...) - local args = { ... } - local i = 0 - return map(array, function(value) - if getmetatable(value) == Binding then - i = i + 1 - return args[i] - else - return value - end - end) - end - - local bindings = filter(array, function(v) - return getmetatable(v) == Binding - end) - - if #bindings == 0 then - return array - end - - if #bindings == 1 then - return bindings[1]:as(get_values) - end - - return Variable.derive(bindings, get_values)() -end - -return function(ctor) - function ctor:hook(object, signalOrCallback, callback) - if GObject.Object:is_type_of(object) and type(signalOrCallback) == "string" then - local id - if string.sub(signalOrCallback, 1, 8) == "notify::" then - local prop = string.gsub(signalOrCallback, "notify::", "") - id = object.on_notify:connect(function() - callback(self, object[prop]) - end, prop, false) - else - id = object["on_" .. signalOrCallback]:connect(function(_, ...) - callback(self, ...) - end) - end - self.on_destroy = function() - GObject.signal_handler_disconnect(object, id) - end - elseif type(object.subscribe) == "function" then - local unsub = object.subscribe(function(...) - signalOrCallback(self, ...) - end) - self.on_destroy = unsub - else - error("can not hook: not gobject+signal or subscribable") - end - end - - function ctor:toggle_class_name(name, on) - Astal.widget_toggle_class_name(self, name, on) - end - - return function(tbl) - if tbl == nil then - tbl = {} - end - - local bindings = {} - local setup = tbl.setup - - -- collect children - local children = merge_bindings(flatten(filter(tbl, function(_, key) - return type(key) == "number" - end))) - - -- default visible to true - if type(tbl.visible) ~= "boolean" then - tbl.visible = true - end - - -- collect props - local props = filter(tbl, function(_, key) - return type(key) == "string" and key ~= "setup" - end) - - -- collect signal handlers - for prop, value in pairs(props) do - if string.sub(prop, 0, 2) == "on" and type(value) ~= "function" then - props[prop] = function() - exec_async(value, print) - end - end - end - - -- collect bindings - for prop, value in pairs(props) do - if getmetatable(value) == Binding then - bindings[prop] = value - props[prop] = value:get() - end - end - - -- construct, attach bindings, add children - local widget = ctor() - - if getmetatable(children) == Binding then - set_children(widget, children:get()) - widget.on_destroy = children:subscribe(function(v) - set_children(widget, v) - end) - else - if #children > 0 then - set_children(widget, children) - end - end - - for prop, binding in pairs(bindings) do - widget.on_destroy = binding:subscribe(function(v) - widget[prop] = v - end) - end - - for prop, value in pairs(props) do - widget[prop] = value - end - - if type(setup) == "function" then - setup(widget) - end - - return widget - end -end diff --git a/lang/lua/gtk3/widget.lua b/lang/lua/gtk3/widget.lua deleted file mode 100644 index beaad6c..0000000 --- a/lang/lua/gtk3/widget.lua +++ /dev/null @@ -1,90 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("Astal", "3.0") -local Gtk = lgi.require("Gtk", "3.0") -local astalify = require("astal.gtk3.astalify") - -local Widget = { - astalify = astalify, - Box = astalify(Astal.Box), - Button = astalify(Astal.Button), - CenterBox = astalify(Astal.CenterBox), - CircularProgress = astalify(Astal.CircularProgress), - DrawingArea = astalify(Gtk.DrawingArea), - Entry = astalify(Gtk.Entry), - EventBox = astalify(Astal.EventBox), - -- TODO: Fixed - -- TODO: FlowBox - Icon = astalify(Astal.Icon), - Label = astalify(Gtk.Label), - LevelBar = astalify(Astal.LevelBar), - -- TODO: ListBox - Overlay = astalify(Astal.Overlay), - Revealer = astalify(Gtk.Revealer), - Scrollable = astalify(Astal.Scrollable), - Slider = astalify(Astal.Slider), - Stack = astalify(Astal.Stack), - Switch = astalify(Gtk.Switch), - Window = astalify(Astal.Window), -} - -Gtk.Widget._attribute.css = { - get = Astal.widget_get_css, - set = Astal.widget_set_css, -} - -Gtk.Widget._attribute.class_name = { - get = function(self) - local result = "" - local strings = Astal.widget_get_class_names(self) - for i, str in ipairs(strings) do - result = result .. str - if i < #strings then - result = result .. " " - end - end - return result - end, - set = function(self, class_name) - local names = {} - for word in class_name:gmatch("%S+") do - table.insert(names, word) - end - Astal.widget_set_class_names(self, names) - end, -} - -Gtk.Widget._attribute.cursor = { - get = Astal.widget_get_cursor, - set = Astal.widget_set_cursor, -} - -Gtk.Widget._attribute.click_through = { - get = Astal.widget_get_click_through, - set = Astal.widget_set_click_through, -} - -local no_implicit_destroy = {} -Gtk.Widget._attribute.no_implicit_destroy = { - get = function(self) - return no_implicit_destroy[self] or false - end, - set = function(self, v) - if no_implicit_destroy[self] == nil then - self.on_destroy = function() - no_implicit_destroy[self] = nil - end - end - no_implicit_destroy[self] = v - end, -} - -Astal.Box._attribute.children = { - get = Astal.Box.get_children, - set = Astal.Box.set_children, -} - -return setmetatable(Widget, { - __call = function(_, ctor) - return astalify(ctor) - end, -}) diff --git a/lang/lua/init.lua b/lang/lua/init.lua deleted file mode 100644 index b6ab30c..0000000 --- a/lang/lua/init.lua +++ /dev/null @@ -1,27 +0,0 @@ -local lgi = require("lgi") -local Binding = require("astal.lib.binding") -local File = require("astal.lib.file") -local Process = require("astal.lib.process") -local Time = require("astal.lib.time") -local Variable = require("astal.lib.variable") - -return { - Variable = Variable, - bind = Binding.new, - - interval = Time.interval, - timeout = Time.timeout, - idle = Time.idle, - - subprocess = Process.subprocess, - exec = Process.exec, - exec_async = Process.exec_async, - - read_file = File.read_file, - read_file_async = File.read_file_async, - write_file = File.write_file, - write_file_async = File.write_file_async, - monitor_file = File.monitor_file, - - require = lgi.require, -} diff --git a/lang/lua/lib/binding.lua b/lang/lua/lib/binding.lua deleted file mode 100644 index ba1e6e4..0000000 --- a/lang/lua/lib/binding.lua +++ /dev/null @@ -1,71 +0,0 @@ -local lgi = require("lgi") -local GObject = lgi.require("GObject", "2.0") - ----@class Binding ----@field emitter table|Variable ----@field property? string ----@field transformFn function -local Binding = {} - ----@param emitter table ----@param property? string ----@return Binding -function Binding.new(emitter, property) - return setmetatable({ - emitter = emitter, - property = property, - transformFn = function(v) - return v - end, - }, Binding) -end - -function Binding:__tostring() - local str = "Binding<" .. tostring(self.emitter) - if self.property ~= nil then - str = str .. ", " .. self.property - end - return str .. ">" -end - -function Binding:get() - if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then - return self.transformFn(self.emitter[self.property]) - end - if type(self.emitter.get) == "function" then - return self.transformFn(self.emitter:get()) - end - error("can not get: Not a GObject or a Variable " + self) -end - ----@param transform fun(value: any): any ----@return Binding -function Binding:as(transform) - local b = Binding.new(self.emitter, self.property) - b.transformFn = function(v) - return transform(self.transformFn(v)) - end - return b -end - ----@param callback fun(value: any) ----@return function -function Binding:subscribe(callback) - if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then - local id = self.emitter.on_notify:connect(function() - callback(self:get()) - end, self.property, false) - return function() - GObject.signal_handler_disconnect(self.emitter, id) - end - end - if type(self.emitter.subscribe) == "function" then - return self.emitter:subscribe(function() - callback(self:get()) - end) - end - error("can not subscribe: Not a GObject or a Variable " + self) -end - -Binding.__index = Binding -return Binding diff --git a/lang/lua/lib/file.lua b/lang/lua/lib/file.lua deleted file mode 100644 index e3be783..0000000 --- a/lang/lua/lib/file.lua +++ /dev/null @@ -1,45 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("AstalIO", "0.1") -local GObject = lgi.require("GObject", "2.0") - -local M = {} - ----@param path string ----@return string -function M.read_file(path) - return Astal.read_file(path) -end - ----@param path string ----@param callback fun(content: string, err: string): nil -function M.read_file_async(path, callback) - Astal.read_file_async(path, function(_, res) - local content, err = Astal.read_file_finish(res) - callback(content, err) - end) -end - ----@param path string ----@param content string -function M.write_file(path, content) - Astal.write_file(path, content) -end - ----@param path string ----@param content string ----@param callback? fun(err: string): nil -function M.write_file_async(path, content, callback) - Astal.write_file_async(path, content, function(_, res) - if type(callback) == "function" then - callback(Astal.write_file_finish(res)) - end - end) -end - ----@param path string ----@param callback fun(file: string, event: integer): nil -function M.monitor_file(path, callback) - return Astal.monitor_file(path, GObject.Closure(callback)) -end - -return M diff --git a/lang/lua/lib/process.lua b/lang/lua/lib/process.lua deleted file mode 100644 index b8b7436..0000000 --- a/lang/lua/lib/process.lua +++ /dev/null @@ -1,78 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("AstalIO", "0.1") - -local M = {} - ----@param commandline string | string[] ----@param on_stdout? fun(out: string): nil ----@param on_stderr? fun(err: string): nil ----@return { kill: function } | nil proc -function M.subprocess(commandline, on_stdout, on_stderr) - if on_stdout == nil then - on_stdout = function(out) - io.stdout:write(tostring(out) .. "\n") - end - end - - if on_stderr == nil then - on_stderr = function(err) - io.stderr:write(tostring(err) .. "\n") - end - end - - local proc, err - if type(commandline) == "table" then - proc, err = Astal.Process.subprocessv(commandline) - else - proc, err = Astal.Process.subprocess(commandline) - end - if err ~= nil then - err(err) - return nil - end - proc.on_stdout = function(_, stdoud) - on_stdout(stdoud) - end - proc.on_stderr = function(_, stderr) - on_stderr(stderr) - end - return proc -end - ----@param commandline string | string[] ----@return string, string -function M.exec(commandline) - if type(commandline) == "table" then - return Astal.Process.execv(commandline) - else - return Astal.Process.exec(commandline) - end -end - ----@param commandline string | string[] ----@param callback? fun(out: string, err: string): nil -function M.exec_async(commandline, callback) - if callback == nil then - callback = function(out, err) - if err ~= nil then - io.stdout:write(tostring(out) .. "\n") - else - io.stderr:write(tostring(err) .. "\n") - end - end - end - - if type(commandline) == "table" then - Astal.Process.exec_asyncv(commandline, function(_, res) - local out, err = Astal.Process.exec_asyncv_finish(res) - callback(out, err) - end) - else - Astal.Process.exec_async(commandline, function(_, res) - local out, err = Astal.Process.exec_finish(res) - callback(out, err) - end) - end -end - -return M diff --git a/lang/lua/lib/time.lua b/lang/lua/lib/time.lua deleted file mode 100644 index 7719da9..0000000 --- a/lang/lua/lib/time.lua +++ /dev/null @@ -1,27 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("AstalIO", "0.1") -local GObject = lgi.require("GObject", "2.0") - -local M = {} - ----@param interval number ----@param fn function ----@return { cancel: function, on_now: function } -function M.interval(interval, fn) - return Astal.Time.interval(interval, GObject.Closure(fn)) -end - ----@param timeout number ----@param fn function ----@return { cancel: function, on_now: function } -function M.timeout(timeout, fn) - return Astal.Time.timeout(timeout, GObject.Closure(fn)) -end - ----@param fn function ----@return { cancel: function, on_now: function } -function M.idle(fn) - return Astal.Time.idle(GObject.Closure(fn)) -end - -return M diff --git a/lang/lua/lib/variable.lua b/lang/lua/lib/variable.lua deleted file mode 100644 index c93d04d..0000000 --- a/lang/lua/lib/variable.lua +++ /dev/null @@ -1,276 +0,0 @@ -local lgi = require("lgi") -local Astal = lgi.require("AstalIO", "0.1") -local GObject = lgi.require("GObject", "2.0") -local Binding = require("astal.lib.binding") -local Time = require("astal.lib.time") -local Process = require("astal.lib.process") - ----@class Variable ----@field private variable table ----@field private err_handler? function ----@field private _value any ----@field private _poll? table ----@field private _watch? table ----@field private poll_interval number ----@field private poll_exec? string[] | string ----@field private poll_transform? fun(next: any, prev: any): any ----@field private poll_fn? function ----@field private watch_transform? fun(next: any, prev: any): any ----@field private watch_exec? string[] | string -local Variable = {} -Variable.__index = Variable - ----@param value any ----@return Variable -function Variable.new(value) - local v = Astal.VariableBase() - local variable = setmetatable({ - variable = v, - _value = value, - }, Variable) - v.on_dropped = function() - variable:stop_watch() - variable:stop_watch() - end - v.on_error = function(_, err) - if variable.err_handler then - variable.err_handler(err) - end - end - return variable -end - ----@param transform function ----@return Binding -function Variable:__call(transform) - if transform == nil then - transform = function(v) - return v - end - return Binding.new(self) - end - return Binding.new(self):as(transform) -end - -function Variable:__tostring() - return "Variable<" .. tostring(self:get()) .. ">" -end - -function Variable:get() - return self._value or nil -end - -function Variable:set(value) - if value ~= self:get() then - self._value = value - self.variable:emit_changed() - end -end - -function Variable:start_poll() - if self._poll ~= nil then - return - end - - if self.poll_fn then - self._poll = Time.interval(self.poll_interval, function() - self:set(self.poll_fn(self:get())) - end) - elseif self.poll_exec then - self._poll = Time.interval(self.poll_interval, function() - Process.exec_async(self.poll_exec, function(out, err) - if err ~= nil then - return self.variable.emit_error(err) - end - self:set(self.poll_transform(out, self:get())) - end) - end) - end -end - -function Variable:start_watch() - if self._watch then - return - end - - self._watch = Process.subprocess(self.watch_exec, function(out) - self:set(self.watch_transform(out, self:get())) - end, function(err) - self.variable.emit_error(err) - end) -end - -function Variable:stop_poll() - if self._poll then - self._poll.cancel() - end - self._poll = nil -end - -function Variable:stop_watch() - if self._watch then - self._watch.kill() - end - self._watch = nil -end - -function Variable:is_polling() - return self._poll ~= nil -end - -function Variable:is_watching() - return self._watch ~= nil -end - -function Variable:drop() - self.variable.emit_dropped() -end - ----@param callback function ----@return Variable -function Variable:on_dropped(callback) - self.variable.on_dropped = callback - return self -end - ----@param callback function ----@return Variable -function Variable:on_error(callback) - self.err_handler = nil - self.variable.on_eror = function(_, err) - callback(err) - end - return self -end - ----@param callback fun(value: any) ----@return function -function Variable:subscribe(callback) - local id = self.variable.on_changed:connect(function() - callback(self:get()) - end) - return function() - GObject.signal_handler_disconnect(self.variable, id) - end -end - ----@param interval number ----@param exec string | string[] | function ----@param transform? fun(next: any, prev: any): any -function Variable:poll(interval, exec, transform) - if transform == nil then - transform = function(next) - return next - end - end - self:stop_poll() - self.poll_interval = interval - self.poll_transform = transform - - if type(exec) == "function" then - self.poll_fn = exec - self.poll_exec = nil - else - self.poll_exec = exec - self.poll_fn = nil - end - self:start_poll() - return self -end - ----@param exec string | string[] ----@param transform? fun(next: any, prev: any): any -function Variable:watch(exec, transform) - if transform == nil then - transform = function(next) - return next - end - end - self:stop_poll() - self.watch_exec = exec - self.watch_transform = transform - self:start_watch() - return self -end - ----@param object table | table[] ----@param sigOrFn string | fun(...): any ----@param callback fun(...): any ----@return Variable -function Variable:observe(object, sigOrFn, callback) - local f - if type(sigOrFn) == "function" then - f = sigOrFn - elseif type(callback) == "function" then - f = callback - else - f = function() - return self:get() - end - end - local set = function(...) - self:set(f(...)) - end - - if type(sigOrFn) == "string" then - object["on_" .. sigOrFn]:connect(set) - else - for _, obj in ipairs(object) do - obj[1]["on_" .. obj[2]]:connect(set) - end - end - return self -end - ----@param deps Variable | (Binding | Variable)[] ----@param transform? fun(...): any ----@return Variable -function Variable.derive(deps, transform) - if type(transform) == "nil" then - transform = function(...) - return { ... } - end - end - - if getmetatable(deps) == Variable then - local var = Variable.new(transform(deps:get())) - deps:subscribe(function(v) - var:set(transform(v)) - end) - return var - end - - for i, var in ipairs(deps) do - if getmetatable(var) == Variable then - deps[i] = Binding.new(var) - end - end - - local update = function() - local params = {} - for i, binding in ipairs(deps) do - params[i] = binding:get() - end - return transform(table.unpack(params), 1, #deps) - end - - local var = Variable.new(update()) - - local unsubs = {} - for i, b in ipairs(deps) do - unsubs[i] = b:subscribe(update) - end - - var.variable.on_dropped = function() - for _, unsub in ipairs(unsubs) do - unsub() - end - end - return var -end - -return setmetatable(Variable, { - __call = function(_, v) - return Variable.new(v) - end, -}) -- cgit v1.2.3