summaryrefslogtreecommitdiff
path: root/js/src
diff options
context:
space:
mode:
authorAylur <[email protected]>2024-05-25 14:44:50 +0200
committerAylur <[email protected]>2024-05-25 14:44:50 +0200
commit58fa1ab9be7ee8fd4a8e96865121a54d613978cc (patch)
tree56f01ba49fd2929690a16ac05a4af8f763e6b30b /js/src
parenta7e25a4a5fcf4de89fe5a149a9aaf50a92be7af1 (diff)
separate node and gjs into its own package
Diffstat (limited to 'js/src')
-rw-r--r--js/src/application.ts28
-rw-r--r--js/src/astalify.ts213
-rw-r--r--js/src/binding.ts78
-rw-r--r--js/src/process.ts83
-rw-r--r--js/src/time.ts27
-rw-r--r--js/src/variable.ts250
6 files changed, 0 insertions, 679 deletions
diff --git a/js/src/application.ts b/js/src/application.ts
deleted file mode 100644
index 10f840e..0000000
--- a/js/src/application.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-export type RequestHandler = {
- (request: string, res: (response: string) => void): void
-}
-
-export type Config = Partial<{
- instanceName: string
- gtkTheme: string
- iconTheme: string
- cursorTheme: string
- css: string
- requestHandler: RequestHandler
- hold: boolean
-}>
-
-export function runJS(body: string): Promise<any> {
- 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)
- }
- })
-}
diff --git a/js/src/astalify.ts b/js/src/astalify.ts
deleted file mode 100644
index bc24db6..0000000
--- a/js/src/astalify.ts
+++ /dev/null
@@ -1,213 +0,0 @@
-import Binding, { kebabify, type Connectable, type Subscribable } from "./binding.js"
-
-export type Widget<C extends { new(...args: any): any }> = InstanceType<C> & {
- className: string
- css: string
- cursor: Cursor
- hook(
- object: Connectable,
- signal: string,
- callback: (self: Widget<C>, ...args: any[]) => void,
- ): Widget<C>
- hook(
- object: Subscribable,
- callback: (self: Widget<C>, ...args: any[]) => void,
- ): Widget<C>
-}
-
-export default function <G extends { Bin: any, Container: any, Widget: any }>(
- Gtk: G,
- setter: (prop: string) => `set${string}`,
- Astal: {
- cssSetter: (w: any, css: string) => void,
- cssGetter: (w: any) => string | null,
- classSetter: (w: any, name: string[]) => void,
- classGetter: (w: any) => string[],
- cursorSetter: (w: any, cursor: string) => void,
- cursorGetter: (w: any) => string | null,
- },
-) {
- function hook(
- self: any,
- object: Connectable | Subscribable,
- signalOrCallback: string | ((self: any, ...args: any[]) => void),
- callback?: (self: any, ...args: any[]) => void,
- ) {
- if (typeof object.connect === "function" && callback) {
- const id = object.connect(signalOrCallback, (_: any, ...args: unknown[]) => {
- callback(self, ...args)
- })
- self.connect("destroy", () => {
- (object.disconnect as Connectable["disconnect"])(id)
- })
- }
-
- else if (typeof object.subscribe === "function" && typeof signalOrCallback === "function") {
- const unsub = object.subscribe((...args: unknown[]) => {
- signalOrCallback(self, ...args)
- })
- self.connect("destroy", unsub)
- }
-
- return self
- }
-
- function setChild(parent: any, child: any) {
- if (parent instanceof Gtk.Bin) {
- if (parent.get_child())
- parent.remove(parent.get_child()!)
- }
- if (parent instanceof Gtk.Container)
- parent.add(child)
- }
-
- function ctor(self: any, config: any, ...children: any[]) {
- const { setup, child, ...props } = config
- props.visible ??= true
-
- const bindings = Object.keys(props).reduce((acc: any, prop) => {
- if (props[prop] instanceof Binding) {
- const bind = [prop, props[prop]]
- prop === "child"
- ? setChild(self, props[prop].get())
- : self[setter(prop)](props[prop].get())
-
- delete props[prop]
- return [...acc, bind]
- }
- return acc
- }, [])
-
- const onHandlers = Object.keys(props).reduce((acc: any, key) => {
- if (key.startsWith("on")) {
- const sig = kebabify(key).split("-").slice(1).join("-")
- const handler = [sig, props[key]]
- delete props[key]
- return [...acc, handler]
- }
- return acc
- }, [])
-
- Object.assign(self, props)
- Object.assign(self, {
- hook(obj: any, sig: any, callback: any) {
- return hook(self, obj, sig, callback)
- },
- })
-
- if (child instanceof Binding) {
- setChild(self, child.get())
- self.connect("destroy", child.subscribe(v => {
- setChild(self, v)
- }))
- } else if (self instanceof Gtk.Container && child instanceof Gtk.Widget) {
- self.add(child)
- }
-
- for (const [signal, callback] of onHandlers)
- self.connect(signal, callback)
-
- if (self instanceof Gtk.Container && children) {
- for (const child of children)
- self.add(child)
- }
-
- for (const [prop, bind] of bindings) {
- self.connect("destroy", bind.subscribe((v: any) => {
- self[`${setter(prop)}`](v)
- }))
- }
-
- setup?.(self)
- return self
- }
-
- return function proxify<
- C extends { new(...args: any[]): any },
- >(klass: C) {
- Object.defineProperty(klass.prototype, "className", {
- get() { return Astal.classGetter(this).join(" ") },
- set(v) { Astal.classSetter(this, v.split(/\s+/)) },
- })
-
- Object.defineProperty(klass.prototype, "css", {
- get() { return Astal.cssGetter(this) },
- set(v) { Astal.cssSetter(this, v) },
- })
-
- Object.defineProperty(klass.prototype, "cursor", {
- get() { return Astal.cursorGetter(this) },
- set(v) { Astal.cursorSetter(this, v) },
- })
-
- const proxy = new Proxy(klass, {
- construct(_, [conf, ...children]) {
- const self = new klass
- return ctor(self, conf, ...children)
- },
- apply(_t, _a, [conf, ...children]) {
- const self = new klass
- return ctor(self, conf, ...children)
- },
- })
-
- return proxy
- }
-}
-
-
-type BindableProps<T> = {
- [K in keyof T]: Binding<NonNullable<T[K]>> | T[K];
-}
-
-export type ConstructProps<
- Self extends { new(...args: any[]): any },
- Props = unknown,
- Signals = unknown
-> = {
- [Key in `on${string}`]: (self: Widget<Self>) => unknown
-} & Partial<Signals> & BindableProps<Props & {
- className?: string
- css?: string
- cursor?: string
-}> & {
- onDestroy?: (self: Widget<Self>) => unknown
- onDraw?: (self: Widget<Self>) => unknown
- setup?: (self: Widget<Self>) => void
-}
-
-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/js/src/binding.ts b/js/src/binding.ts
deleted file mode 100644
index a8b6d55..0000000
--- a/js/src/binding.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-export const kebabify = (str: string) => str
- .replace(/([a-z])([A-Z])/g, "$1-$2")
- .replaceAll("_", "-")
- .toLowerCase()
-
-export interface Subscribable<T = unknown> {
- subscribe(callback: () => 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<Value> {
- private emitter: Subscribable<Value> | Connectable
- private prop?: string
- private transformFn = (v: any) => v
-
- static bind<
- T extends Connectable,
- P extends keyof T,
- >(object: T, property: P): Binding<T[P]>
-
- static bind<T>(object: Subscribable<T>): Binding<T>
-
- static bind(emitter: Connectable | Subscribable, prop?: string) {
- return new Binding(emitter, prop)
- }
-
- private constructor(emitter: Connectable | Subscribable<Value>, prop?: string) {
- this.emitter = emitter
- this.prop = prop && kebabify(prop)
- }
-
- toString() {
- return `Binding<${this.emitter}${this.prop ? `, "${this.prop}"` : ""}>`
- }
-
- as<T>(fn: (v: Value) => T): Binding<T> {
- const bind = new Binding(this.emitter, this.prop)
- bind.transformFn = (v: Value) => fn(this.transformFn(v))
- return bind as unknown as Binding<T>
- }
-
- get(): Value {
- if (typeof this.emitter.get === "function")
- return this.transformFn(this.emitter.get())
-
- if (typeof this.prop === "string")
- 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/js/src/process.ts b/js/src/process.ts
deleted file mode 100644
index 537aba8..0000000
--- a/js/src/process.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-type Proc = {
- connect(sig: "stdout" | "stderr", fn: (_: any, out: string) => void): number
-}
-
-type Config<P extends Proc> = {
- defaultOut(stdout: string): void
- defaultErr(stdout: string): void
- subprocess(cmd: string): P
- subprocessv(cmd: string[]): P
- exec(cmd: string): string | null
- execv(cmd: string[]): string | null
- execAsync(cmd: string): P
- execAsyncv(cmd: string[]): P
-}
-
-type Args<Out = void, Err = void> = {
- cmd: string | string[],
- out?: (stdout: string) => Out,
- err?: (stderr: string) => Err,
-}
-
-export default function <P extends Proc>(config: Config<P>) {
- function args<O, E>(argsOrCmd: Args | string | string[], onOut: O, onErr: E) {
- const params = Array.isArray(argsOrCmd) || typeof argsOrCmd === "string"
- return {
- cmd: params ? argsOrCmd : argsOrCmd.cmd,
- err: params ? onErr : argsOrCmd.err || onErr,
- out: params ? onOut : argsOrCmd.out || onOut,
- }
- }
-
- function subprocess(args: Args): P
- function subprocess(
- cmd: string | string[],
- onOut?: (stdout: string) => void,
- onErr?: (stderr: string) => void,
- ): P
- function subprocess(
- argsOrCmd: Args | string | string[],
- onOut: (stdout: string) => void = config.defaultOut,
- onErr: (stderr: string) => void = config.defaultErr,
- ) {
- const { cmd, err, out } = args(argsOrCmd, onOut, onErr)
- const proc = Array.isArray(cmd)
- ? config.subprocessv(cmd)
- : config.subprocess(cmd)
-
- proc.connect("stdout", (_, stdout: string) => out(stdout))
- proc.connect("stderr", (_, stderr: string) => err(stderr))
- return proc
- }
-
- function exec<Out = string, Err = string>(
- args: Args<Out, Err>
- ): Out | Err
- function exec<Out = string, Err = string>(
- cmd: string | string[],
- onOut?: (stdout: string) => Out,
- onErr?: (stderr: string) => Err,
- ): Out | Err
- function exec<Out = string, Err = string>(
- argsOrCmd: Args<Out, Err> | string | string[],
- onOut: (stdout: string) => Out = out => out as Out,
- onErr: (stderr: string) => Err = out => out as Err,
- ): Out | Err {
- const { cmd, err, out } = args(argsOrCmd, onOut, onErr)
- return Array.isArray(cmd)
- ? out(config.execv(cmd)!) as Out
- : err(config.exec(cmd)!) as Err
- }
-
- function execAsync(cmd: string | string[]): Promise<string> {
- const proc = Array.isArray(cmd)
- ? config.execAsyncv(cmd)
- : config.execAsync(cmd)
- return new Promise((resolve, reject) => {
- proc.connect("stdout", (_, out: string) => resolve(out))
- proc.connect("stderr", (_, err: string) => reject(err))
- })
- }
-
- return { subprocess, exec, execAsync }
-}
diff --git a/js/src/time.ts b/js/src/time.ts
deleted file mode 100644
index ce98c9a..0000000
--- a/js/src/time.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-interface Time {
- connect(sig: "now", fn: () => void): number
- cancel(): void
-}
-
-export default function Time<T extends Time>(Time: {
- interval(interval: number, closure: any): T
- timeout(timeout: number, closure: any): T
- idle(closure: any): T
-}) {
- function interval(interval: number, callback: () => void) {
- const t = Time.interval(interval, null)
- t.connect("now", callback)
- return t
- }
- function timeout(timeout: number, callback: () => void) {
- const t = Time.timeout(timeout, null)
- t.connect("now", callback)
- return t
- }
- function idle(callback: () => void) {
- const t = Time.idle(null)
- t.connect("now", callback)
- return t
- }
- return { interval, timeout, idle }
-}
diff --git a/js/src/variable.ts b/js/src/variable.ts
deleted file mode 100644
index 1d4e07f..0000000
--- a/js/src/variable.ts
+++ /dev/null
@@ -1,250 +0,0 @@
-import Binding, { type Connectable } from "./binding.js"
-
-type VariableBase = {
- emit(sig: "error" | "changed" | "dropped", ...args: any[]): void
- connect(sig: "error" | "changed" | "dropped", fn: (...args: any[]) => void): number
- disconnect(id: number): void
- runDispose?(): void // node, deno
- run_dispose?(): void // gjs
-}
-
-type VariableBaseCtor = {
- new(): VariableBase
-}
-
-type Time = any
-type Process = any
-
-type Config = {
- defaultErrHandler(err: any): void
- VariableBase: VariableBaseCtor
- interval(n: number, fn: () => void): Time
- execAsync(cmd: string | string[]): Promise<string>
- subprocess(args: {
- cmd: string | string[],
- out: (s: string) => void,
- err: (s: string) => void
- }): Process
-}
-
-// @ts-expect-error missing values
-export const config: Config = {}
-
-class VariableWrapper<T> extends Function {
- private variable!: VariableBase
- private errHandler? = config.defaultErrHandler
-
- private _value: T
- private _poll?: Time
- private _watch?: Process
-
- private pollInterval = 1000
- private pollExec?: string[] | string
- private pollTransform?: (stdout: string, prev: T) => T
- private pollFn?: (prev: T) => T | Promise<T>
-
- private watchTransform?: (stdout: string, prev: T) => T
- private watchExec?: string[] | string
-
- constructor(init: T) {
- super()
- this._value = init
- this.variable = new config.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<R = T>(transform?: (value: T) => R): Binding<R> {
- const b = Binding.bind(this)
- return transform ? b.as(transform) : b as unknown as Binding<R>
- }
-
- 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 = config.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 = config.interval(this.pollInterval, () => {
- config.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 = config.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")
- this.variable.runDispose?.()
- this.variable.run_dispose?.()
- }
-
- onDropped(callback: () => void) {
- this.variable.connect("dropped", callback)
- return this as unknown as Variable<T>
- }
-
- onError(callback: (err: string) => void) {
- delete this.errHandler
- this.variable.connect("error", (_, err) => callback(err))
- return this as unknown as Variable<T>
- }
-
- 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<T>
-
- poll(
- interval: number,
- callback: (prev: T) => T | Promise<T>
- ): Variable<T>
-
- poll(
- interval: number,
- exec: string | string[] | ((prev: T) => T | Promise<T>),
- 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<T>
- }
-
- 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<T>
- }
-
- observe(
- objs: Array<[obj: Connectable, signal: string]>,
- callback: (...args: any[]) => T): Variable<T>
-
- observe(
- obj: Connectable,
- signal: string,
- callback: (...args: any[]) => T): Variable<T>
-
- observe(
- objs: Connectable | Array<[obj: Connectable, signal: string]>,
- sigOrFn: string | ((...args: any[]) => T),
- callback?: (...args: any[]) => T,
- ) {
- const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => this.get())
- const set = (_: Connectable, ...args: any[]) => this.set(f(...args))
-
- if (Array.isArray(objs)) {
- for (const obj of objs) {
- const [o, s] = obj
- o.connect(s, set)
- }
- } else {
- if (typeof sigOrFn === "string")
- objs.connect(sigOrFn, set)
- }
-
- return this as unknown as Variable<T>
- }
-
- static derive<V,
- const Deps extends Array<Variable<any> | Binding<any>>,
- Args extends {
- [K in keyof Deps]: Deps[K] extends Variable<infer T>
- ? T : Deps[K] extends Binding<infer T> ? T : never
- },
- >(deps: Deps, fn: (...args: Args) => 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<T> extends Omit<VariableWrapper<T>, "bind"> {
- <R>(transform: (value: T) => R): Binding<R>
- (): Binding<T>
-}
-
-export const Variable = new Proxy(VariableWrapper as any, {
- apply: (_t, _a, args) => new VariableWrapper(args[0]),
-}) as {
- derive: typeof VariableWrapper["derive"]
- <T>(init: T): Variable<T>
- new <T>(init: T): Variable<T>
-}
-
-export default Variable