gnim 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/dbus.d.ts +141 -0
- package/dist/dbus.js +609 -0
- package/dist/fetch.d.ts +87 -0
- package/dist/fetch.js +303 -0
- package/dist/gnome/jsx-runtime.d.ts +5 -0
- package/dist/gnome/jsx-runtime.js +98 -0
- package/dist/gnome/signalTracker.d.ts +6 -0
- package/dist/gnome/signalTracker.js +5 -0
- package/dist/gobject.d.ts +161 -0
- package/dist/gobject.js +252 -0
- package/dist/gtk3/jsx-runtime.d.ts +5 -0
- package/dist/gtk3/jsx-runtime.js +143 -0
- package/dist/gtk3/style.d.ts +4 -0
- package/dist/gtk3/style.js +25 -0
- package/dist/gtk4/jsx-runtime.d.ts +5 -0
- package/dist/gtk4/jsx-runtime.js +143 -0
- package/dist/gtk4/style.d.ts +4 -0
- package/dist/gtk4/style.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +74 -0
- package/dist/jsx/For.d.ts +25 -0
- package/dist/jsx/For.js +66 -0
- package/dist/jsx/Fragment.d.ts +28 -0
- package/dist/jsx/Fragment.js +76 -0
- package/dist/jsx/This.d.ts +28 -0
- package/dist/jsx/This.js +64 -0
- package/dist/jsx/With.d.ts +17 -0
- package/dist/jsx/With.js +34 -0
- package/dist/jsx/env.d.ts +20 -0
- package/dist/jsx/env.js +14 -0
- package/dist/jsx/index.d.ts +7 -0
- package/dist/jsx/index.js +7 -0
- package/dist/jsx/jsx.d.ts +83 -0
- package/dist/jsx/jsx.js +121 -0
- package/dist/jsx/scope.d.ts +99 -0
- package/dist/jsx/scope.js +159 -0
- package/dist/jsx/state.d.ts +117 -0
- package/dist/jsx/state.js +238 -0
- package/dist/util.d.ts +12 -0
- package/dist/util.js +65 -0
- package/package.json +103 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export declare class Scope {
|
|
2
|
+
static current: Scope | null;
|
|
3
|
+
parent?: Scope | null;
|
|
4
|
+
context?: any;
|
|
5
|
+
private cleanups;
|
|
6
|
+
private mounts;
|
|
7
|
+
private mounted;
|
|
8
|
+
constructor(parent: Scope | null);
|
|
9
|
+
onCleanup(callback: () => void): void;
|
|
10
|
+
onMount(callback: () => void): void;
|
|
11
|
+
run<T>(fn: () => T): T;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
}
|
|
14
|
+
export type Context<T = any> = {
|
|
15
|
+
use(): T;
|
|
16
|
+
provide<R>(value: T, fn: () => R): R;
|
|
17
|
+
(props: {
|
|
18
|
+
value: T;
|
|
19
|
+
children: () => JSX.Element;
|
|
20
|
+
}): JSX.Element;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Example Usage:
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const MyContext = createContext("fallback-value")
|
|
26
|
+
*
|
|
27
|
+
* function ConsumerComponent() {
|
|
28
|
+
* const value = MyContext.use()
|
|
29
|
+
*
|
|
30
|
+
* return <Gtk.Label label={value} />
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* function ProviderComponent() {
|
|
34
|
+
* return (
|
|
35
|
+
* <Gtk.Box>
|
|
36
|
+
* <MyContext value="my-value">
|
|
37
|
+
* {() => <ConsumerComponent />}
|
|
38
|
+
* </MyContext>
|
|
39
|
+
* </Gtk.Box>
|
|
40
|
+
* )
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function createContext<T>(defaultValue: T): Context<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Gets the scope that owns the currently running code.
|
|
47
|
+
*
|
|
48
|
+
* Example:
|
|
49
|
+
* ```ts
|
|
50
|
+
* const scope = getScope()
|
|
51
|
+
* setTimeout(() => {
|
|
52
|
+
* // This callback gets run without an owner scope.
|
|
53
|
+
* // Restore owner via scope.run:
|
|
54
|
+
* scope.run(() => {
|
|
55
|
+
* const foo = FooContext.use()
|
|
56
|
+
* onCleanup(() => {
|
|
57
|
+
* print("some cleanup")
|
|
58
|
+
* })
|
|
59
|
+
* })
|
|
60
|
+
* }, 1000)
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function getScope(): Scope;
|
|
64
|
+
/**
|
|
65
|
+
* Attach a cleanup callback to the current {@link Scope}.
|
|
66
|
+
*/
|
|
67
|
+
export declare function onCleanup(cleanup: () => void): void;
|
|
68
|
+
/**
|
|
69
|
+
* Attach a callback to run when the currently running {@link Scope} returns.
|
|
70
|
+
*/
|
|
71
|
+
export declare function onMount(cleanup: () => void): void;
|
|
72
|
+
/**
|
|
73
|
+
* Creates a root {@link Scope} that when disposed will remove
|
|
74
|
+
* any child signal handler or state subscriber.
|
|
75
|
+
*
|
|
76
|
+
* Example:
|
|
77
|
+
* ```tsx
|
|
78
|
+
* createRoot((dispose) => {
|
|
79
|
+
* let root: Gtk.Window
|
|
80
|
+
*
|
|
81
|
+
* const [state] = createState("value")
|
|
82
|
+
*
|
|
83
|
+
* const remove = () => {
|
|
84
|
+
* root.destroy()
|
|
85
|
+
* dispose()
|
|
86
|
+
* }
|
|
87
|
+
*
|
|
88
|
+
* return (
|
|
89
|
+
* <Gtk.Window $={(self) => (root = self)}>
|
|
90
|
+
* <Gtk.Box>
|
|
91
|
+
* <Gtk.Label label={state} />
|
|
92
|
+
* <Gtk.Button $clicked={remove} />
|
|
93
|
+
* </Gtk.Box>
|
|
94
|
+
* </Gtk.Window>
|
|
95
|
+
* )
|
|
96
|
+
* })
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare function createRoot<T>(fn: (dispose: () => void) => T): T;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
export class Scope {
|
|
2
|
+
constructor(parent) {
|
|
3
|
+
this.cleanups = new Set();
|
|
4
|
+
this.mounts = new Set();
|
|
5
|
+
this.mounted = false;
|
|
6
|
+
this.parent = parent;
|
|
7
|
+
}
|
|
8
|
+
onCleanup(callback) {
|
|
9
|
+
this.cleanups?.add(callback);
|
|
10
|
+
}
|
|
11
|
+
onMount(callback) {
|
|
12
|
+
if (this.parent && !this.parent.mounted) {
|
|
13
|
+
this.parent.onMount(callback);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
this.mounts.add(callback);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
run(fn) {
|
|
20
|
+
const prev = Scope.current;
|
|
21
|
+
Scope.current = this;
|
|
22
|
+
try {
|
|
23
|
+
return fn();
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
this.mounts.forEach((cb) => cb());
|
|
27
|
+
this.mounts.clear();
|
|
28
|
+
this.mounted = true;
|
|
29
|
+
Scope.current = prev;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
dispose() {
|
|
33
|
+
this.cleanups.forEach((cb) => cb());
|
|
34
|
+
this.cleanups.clear();
|
|
35
|
+
delete this.parent;
|
|
36
|
+
delete this.context;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Example Usage:
|
|
41
|
+
* ```tsx
|
|
42
|
+
* const MyContext = createContext("fallback-value")
|
|
43
|
+
*
|
|
44
|
+
* function ConsumerComponent() {
|
|
45
|
+
* const value = MyContext.use()
|
|
46
|
+
*
|
|
47
|
+
* return <Gtk.Label label={value} />
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* function ProviderComponent() {
|
|
51
|
+
* return (
|
|
52
|
+
* <Gtk.Box>
|
|
53
|
+
* <MyContext value="my-value">
|
|
54
|
+
* {() => <ConsumerComponent />}
|
|
55
|
+
* </MyContext>
|
|
56
|
+
* </Gtk.Box>
|
|
57
|
+
* )
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function createContext(defaultValue) {
|
|
62
|
+
function provide(value, fn) {
|
|
63
|
+
const scope = new Scope(Scope.current);
|
|
64
|
+
onCleanup(() => scope.dispose());
|
|
65
|
+
scope.context = value;
|
|
66
|
+
return scope.run(fn);
|
|
67
|
+
}
|
|
68
|
+
function use() {
|
|
69
|
+
let scope = Scope.current;
|
|
70
|
+
while (scope) {
|
|
71
|
+
const value = scope.context;
|
|
72
|
+
if (value)
|
|
73
|
+
return value;
|
|
74
|
+
scope = scope.parent ?? null;
|
|
75
|
+
}
|
|
76
|
+
return defaultValue;
|
|
77
|
+
}
|
|
78
|
+
function context({ value, children }) {
|
|
79
|
+
return provide(value, children);
|
|
80
|
+
}
|
|
81
|
+
return Object.assign(context, {
|
|
82
|
+
provide,
|
|
83
|
+
use,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Gets the scope that owns the currently running code.
|
|
88
|
+
*
|
|
89
|
+
* Example:
|
|
90
|
+
* ```ts
|
|
91
|
+
* const scope = getScope()
|
|
92
|
+
* setTimeout(() => {
|
|
93
|
+
* // This callback gets run without an owner scope.
|
|
94
|
+
* // Restore owner via scope.run:
|
|
95
|
+
* scope.run(() => {
|
|
96
|
+
* const foo = FooContext.use()
|
|
97
|
+
* onCleanup(() => {
|
|
98
|
+
* print("some cleanup")
|
|
99
|
+
* })
|
|
100
|
+
* })
|
|
101
|
+
* }, 1000)
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function getScope() {
|
|
105
|
+
const scope = Scope.current;
|
|
106
|
+
if (!scope) {
|
|
107
|
+
throw Error("cannot get scope: out of tracking context");
|
|
108
|
+
}
|
|
109
|
+
return scope;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Attach a cleanup callback to the current {@link Scope}.
|
|
113
|
+
*/
|
|
114
|
+
export function onCleanup(cleanup) {
|
|
115
|
+
if (!Scope.current) {
|
|
116
|
+
console.error(Error("out of tracking context: will not be able to cleanup"));
|
|
117
|
+
}
|
|
118
|
+
Scope.current?.onCleanup(cleanup);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Attach a callback to run when the currently running {@link Scope} returns.
|
|
122
|
+
*/
|
|
123
|
+
export function onMount(cleanup) {
|
|
124
|
+
if (!Scope.current) {
|
|
125
|
+
console.error(Error("cannot attach onMount: out of tracking context"));
|
|
126
|
+
}
|
|
127
|
+
Scope.current?.onMount(cleanup);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Creates a root {@link Scope} that when disposed will remove
|
|
131
|
+
* any child signal handler or state subscriber.
|
|
132
|
+
*
|
|
133
|
+
* Example:
|
|
134
|
+
* ```tsx
|
|
135
|
+
* createRoot((dispose) => {
|
|
136
|
+
* let root: Gtk.Window
|
|
137
|
+
*
|
|
138
|
+
* const [state] = createState("value")
|
|
139
|
+
*
|
|
140
|
+
* const remove = () => {
|
|
141
|
+
* root.destroy()
|
|
142
|
+
* dispose()
|
|
143
|
+
* }
|
|
144
|
+
*
|
|
145
|
+
* return (
|
|
146
|
+
* <Gtk.Window $={(self) => (root = self)}>
|
|
147
|
+
* <Gtk.Box>
|
|
148
|
+
* <Gtk.Label label={state} />
|
|
149
|
+
* <Gtk.Button $clicked={remove} />
|
|
150
|
+
* </Gtk.Box>
|
|
151
|
+
* </Gtk.Window>
|
|
152
|
+
* )
|
|
153
|
+
* })
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export function createRoot(fn) {
|
|
157
|
+
const scope = new Scope(null);
|
|
158
|
+
return scope.run(() => fn(() => scope.dispose()));
|
|
159
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import GObject from "gi://GObject";
|
|
2
|
+
import Gio from "gi://Gio";
|
|
3
|
+
type SubscribeCallback = () => void;
|
|
4
|
+
type DisposeFunction = () => void;
|
|
5
|
+
type SubscrubeFunction = (callback: SubscribeCallback) => DisposeFunction;
|
|
6
|
+
export type Accessed<T> = T extends Accessor<infer V> ? V : never;
|
|
7
|
+
export declare class Accessor<T = unknown> extends Function {
|
|
8
|
+
#private;
|
|
9
|
+
static $gtype: GObject.GType<Accessor>;
|
|
10
|
+
/** @experimental */
|
|
11
|
+
static evaluating?: Set<Accessor<unknown>>;
|
|
12
|
+
constructor(get: () => T, subscribe?: SubscrubeFunction);
|
|
13
|
+
/**
|
|
14
|
+
* Subscribe for value changes.
|
|
15
|
+
* @param callback The function to run when the current value changes.
|
|
16
|
+
* @returns Unsubscribe function.
|
|
17
|
+
*/
|
|
18
|
+
subscribe(callback: SubscribeCallback): DisposeFunction;
|
|
19
|
+
/**
|
|
20
|
+
* @returns The current value.
|
|
21
|
+
*/
|
|
22
|
+
get(): T;
|
|
23
|
+
/**
|
|
24
|
+
* Create a new `Accessor` that applies a transformation on its value.
|
|
25
|
+
* @param transform The transformation to apply. Should be a pure function.
|
|
26
|
+
*/
|
|
27
|
+
as<R = T>(transform: (value: T) => R): Accessor<R>;
|
|
28
|
+
protected _call<R = T>(transform: (value: T) => R): Accessor<R>;
|
|
29
|
+
toString(): string;
|
|
30
|
+
[Symbol.toPrimitive](): string;
|
|
31
|
+
}
|
|
32
|
+
export interface Accessor<T> {
|
|
33
|
+
/**
|
|
34
|
+
* Create a new `Accessor` that applies a transformation on its value.
|
|
35
|
+
* @param transform The transformation to apply. Should be a pure function.
|
|
36
|
+
*/
|
|
37
|
+
<R = T>(transform: (value: T) => R): Accessor<R>;
|
|
38
|
+
}
|
|
39
|
+
export type Setter<T> = {
|
|
40
|
+
(value: T): void;
|
|
41
|
+
(value: (prev: T) => T): void;
|
|
42
|
+
};
|
|
43
|
+
export type State<T> = [Accessor<T>, Setter<T>];
|
|
44
|
+
/**
|
|
45
|
+
* Create a writable signal.
|
|
46
|
+
*
|
|
47
|
+
* @param init The intial value of the signal
|
|
48
|
+
* @returns An `Accessor` and a setter function
|
|
49
|
+
*/
|
|
50
|
+
export declare function createState<T>(init: T): State<T>;
|
|
51
|
+
/**
|
|
52
|
+
* ```ts Example
|
|
53
|
+
* let a: Accessor<number>
|
|
54
|
+
* let b: Accessor<string>
|
|
55
|
+
*
|
|
56
|
+
* const c: Accessor<[number, string]> = createComputed([a, b])
|
|
57
|
+
*
|
|
58
|
+
* const d: Accessor<string> = createComputed([a, b], (a: number, b: string) => `${a} ${b}`)
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* Create an `Accessor` which is computed from a list of `Accessor`s.
|
|
62
|
+
* @param deps List of `Accessors`.
|
|
63
|
+
* @param transform An optional transform function.
|
|
64
|
+
* @returns The computed `Accessor`.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createComputed<const Deps extends Array<Accessor<any>>, Args extends {
|
|
67
|
+
[K in keyof Deps]: Accessed<Deps[K]>;
|
|
68
|
+
}, V = Args>(deps: Deps, transform?: (...args: Args) => V): Accessor<V>;
|
|
69
|
+
/**
|
|
70
|
+
* Create an `Accessor` on a `GObject.Object`'s `property`.
|
|
71
|
+
*
|
|
72
|
+
* @param object The `GObject.Object` to create the `Accessor` on.
|
|
73
|
+
* @param property One of its registered properties.
|
|
74
|
+
*/
|
|
75
|
+
export declare function createBinding<T extends GObject.Object, P extends keyof T>(object: T, property: Extract<P, string>): Accessor<T[P]>;
|
|
76
|
+
/**
|
|
77
|
+
* Create an `Accessor` on a `Gio.Settings`'s `key`.
|
|
78
|
+
* Values are recursively unpacked.
|
|
79
|
+
*
|
|
80
|
+
* @param object The `Gio.Settings` to create the `Accessor` on.
|
|
81
|
+
* @param key The settings key
|
|
82
|
+
*/
|
|
83
|
+
export declare function createBinding<T>(settings: Gio.Settings, key: string): Accessor<T>;
|
|
84
|
+
type ConnectionHandler<O extends GObject.Object, S extends keyof O["$signals"], Return> = O["$signals"][S] extends (...args: any[]) => infer R ? void extends R ? (...args: Parameters<O["$signals"][S]>) => Return : never : never;
|
|
85
|
+
/**
|
|
86
|
+
* ```ts Example
|
|
87
|
+
* const value: Accessor<string> = createConnection(
|
|
88
|
+
* "initial value",
|
|
89
|
+
* [obj1, "sig-name", (...args) => "str"],
|
|
90
|
+
* [obj2, "sig-name", (...args) => "str"]
|
|
91
|
+
* )
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* Create an `Accessor` which sets up a list of `GObject.Object` signal connections.
|
|
95
|
+
* @param init The initial value
|
|
96
|
+
* @param signals A list of `GObject.Object`, signal name and callback pairs to connect.
|
|
97
|
+
*/
|
|
98
|
+
export declare function createConnection<T, O1 extends GObject.Object, S1 extends keyof O1["$signals"], O2 extends GObject.Object, S2 extends keyof O2["$signals"], O3 extends GObject.Object, S3 extends keyof O3["$signals"], O4 extends GObject.Object, S4 extends keyof O4["$signals"], O5 extends GObject.Object, S5 extends keyof O5["$signals"], O6 extends GObject.Object, S6 extends keyof O6["$signals"], O7 extends GObject.Object, S7 extends keyof O7["$signals"], O8 extends GObject.Object, S8 extends keyof O8["$signals"], O9 extends GObject.Object, S9 extends keyof O9["$signals"]>(init: T, h1: [O1, S1, ConnectionHandler<O1, S1, T>], h2?: [O2, S2, ConnectionHandler<O2, S2, T>], h3?: [O3, S3, ConnectionHandler<O3, S3, T>], h4?: [O4, S4, ConnectionHandler<O4, S4, T>], h5?: [O5, S5, ConnectionHandler<O5, S5, T>], h6?: [O6, S6, ConnectionHandler<O6, S6, T>], h7?: [O7, S7, ConnectionHandler<O7, S7, T>], h8?: [O8, S8, ConnectionHandler<O8, S8, T>], h9?: [O9, S9, ConnectionHandler<O9, S9, T>]): Accessor<T>;
|
|
99
|
+
/**
|
|
100
|
+
* Create a signal from a provier function.
|
|
101
|
+
* The provider is called when the first subscriber appears and the returned dispose
|
|
102
|
+
* function from the provider will be called when the number of subscribers drop to zero.
|
|
103
|
+
*
|
|
104
|
+
* Example:
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* const value = createExternal(0, (set) => {
|
|
108
|
+
* const interval = setInterval(() => set((v) => v + 1))
|
|
109
|
+
* return () => clearInterval(interval)
|
|
110
|
+
* })
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @param init The initial value
|
|
114
|
+
* @param producer The producer function which should return a cleanup function
|
|
115
|
+
*/
|
|
116
|
+
export declare function createExternal<T>(init: T, producer: (set: Setter<T>) => DisposeFunction): Accessor<T>;
|
|
117
|
+
export {};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _Accessor_get, _Accessor_subscribe;
|
|
13
|
+
import GObject from "gi://GObject";
|
|
14
|
+
import Gio from "gi://Gio";
|
|
15
|
+
import { kebabify } from "../util";
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
17
|
+
export class Accessor extends Function {
|
|
18
|
+
constructor(get, subscribe) {
|
|
19
|
+
super("return arguments.callee._call.apply(arguments.callee, arguments)");
|
|
20
|
+
_Accessor_get.set(this, void 0);
|
|
21
|
+
_Accessor_subscribe.set(this, void 0);
|
|
22
|
+
__classPrivateFieldSet(this, _Accessor_subscribe, subscribe ?? (() => () => void 0), "f");
|
|
23
|
+
__classPrivateFieldSet(this, _Accessor_get, get, "f");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Subscribe for value changes.
|
|
27
|
+
* @param callback The function to run when the current value changes.
|
|
28
|
+
* @returns Unsubscribe function.
|
|
29
|
+
*/
|
|
30
|
+
subscribe(callback) {
|
|
31
|
+
return __classPrivateFieldGet(this, _Accessor_subscribe, "f").call(this, callback);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @returns The current value.
|
|
35
|
+
*/
|
|
36
|
+
get() {
|
|
37
|
+
Accessor.evaluating?.add(this);
|
|
38
|
+
return __classPrivateFieldGet(this, _Accessor_get, "f").call(this);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a new `Accessor` that applies a transformation on its value.
|
|
42
|
+
* @param transform The transformation to apply. Should be a pure function.
|
|
43
|
+
*/
|
|
44
|
+
as(transform) {
|
|
45
|
+
return new Accessor(() => transform(__classPrivateFieldGet(this, _Accessor_get, "f").call(this)), __classPrivateFieldGet(this, _Accessor_subscribe, "f"));
|
|
46
|
+
}
|
|
47
|
+
_call(transform) {
|
|
48
|
+
return this.as(transform);
|
|
49
|
+
}
|
|
50
|
+
toString() {
|
|
51
|
+
return `Accessor<${this.get()}>`;
|
|
52
|
+
}
|
|
53
|
+
[(_Accessor_get = new WeakMap(), _Accessor_subscribe = new WeakMap(), Symbol.toPrimitive)]() {
|
|
54
|
+
console.warn("Accessor implicitly converted to a primitive value.");
|
|
55
|
+
return this.toString();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
Accessor.$gtype = GObject.TYPE_JSOBJECT;
|
|
59
|
+
/**
|
|
60
|
+
* Create a writable signal.
|
|
61
|
+
*
|
|
62
|
+
* @param init The intial value of the signal
|
|
63
|
+
* @returns An `Accessor` and a setter function
|
|
64
|
+
*/
|
|
65
|
+
export function createState(init) {
|
|
66
|
+
let currentValue = init;
|
|
67
|
+
const subscribers = new Set();
|
|
68
|
+
const subscribe = (callback) => {
|
|
69
|
+
subscribers.add(callback);
|
|
70
|
+
return () => subscribers.delete(callback);
|
|
71
|
+
};
|
|
72
|
+
const set = (newValue) => {
|
|
73
|
+
const value = typeof newValue === "function" ? newValue(currentValue) : newValue;
|
|
74
|
+
if (currentValue !== value) {
|
|
75
|
+
currentValue = value;
|
|
76
|
+
subscribers.forEach((cb) => cb());
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
return [new Accessor(() => currentValue, subscribe), set];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* ```ts Example
|
|
83
|
+
* let a: Accessor<number>
|
|
84
|
+
* let b: Accessor<string>
|
|
85
|
+
*
|
|
86
|
+
* const c: Accessor<[number, string]> = createComputed([a, b])
|
|
87
|
+
*
|
|
88
|
+
* const d: Accessor<string> = createComputed([a, b], (a: number, b: string) => `${a} ${b}`)
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* Create an `Accessor` which is computed from a list of `Accessor`s.
|
|
92
|
+
* @param deps List of `Accessors`.
|
|
93
|
+
* @param transform An optional transform function.
|
|
94
|
+
* @returns The computed `Accessor`.
|
|
95
|
+
*/
|
|
96
|
+
export function createComputed(deps, transform) {
|
|
97
|
+
let dispose;
|
|
98
|
+
const subscribers = new Set();
|
|
99
|
+
const cache = new Array(deps.length);
|
|
100
|
+
const subscribe = (callback) => {
|
|
101
|
+
if (subscribers.size === 0) {
|
|
102
|
+
dispose = deps.map((dep, i) => dep.subscribe(() => {
|
|
103
|
+
const value = dep.get();
|
|
104
|
+
if (cache[i] !== value) {
|
|
105
|
+
cache[i] = dep.get();
|
|
106
|
+
subscribers.forEach((cb) => cb());
|
|
107
|
+
}
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
subscribers.add(callback);
|
|
111
|
+
return () => {
|
|
112
|
+
subscribers.delete(callback);
|
|
113
|
+
if (subscribers.size === 0) {
|
|
114
|
+
dispose.map((cb) => cb());
|
|
115
|
+
dispose.length = 0;
|
|
116
|
+
cache.length = 0;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
const get = () => {
|
|
121
|
+
const args = deps.map((dep, i) => {
|
|
122
|
+
if (!cache[i]) {
|
|
123
|
+
cache[i] = dep.get();
|
|
124
|
+
}
|
|
125
|
+
return cache[i];
|
|
126
|
+
});
|
|
127
|
+
return transform ? transform(...args) : args;
|
|
128
|
+
};
|
|
129
|
+
return new Accessor(get, subscribe);
|
|
130
|
+
}
|
|
131
|
+
export function createBinding(object, key) {
|
|
132
|
+
const prop = kebabify(key);
|
|
133
|
+
const subscribe = (callback) => {
|
|
134
|
+
const sig = object instanceof Gio.Settings ? "changed" : "notify";
|
|
135
|
+
const id = object.connect(`${sig}::${prop}`, () => callback());
|
|
136
|
+
return () => object.disconnect(id);
|
|
137
|
+
};
|
|
138
|
+
const get = () => {
|
|
139
|
+
if (object instanceof Gio.Settings) {
|
|
140
|
+
return object.get_value(key).recursiveUnpack();
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const getter = `get_${prop.replaceAll("-", "_")}`;
|
|
144
|
+
if (getter in object && typeof object[getter] === "function") {
|
|
145
|
+
return object[getter]();
|
|
146
|
+
}
|
|
147
|
+
if (prop in object)
|
|
148
|
+
return object[prop];
|
|
149
|
+
if (key in object)
|
|
150
|
+
return object[key];
|
|
151
|
+
throw Error(`cannot get property ${key}`);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
return new Accessor(get, subscribe);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* ```ts Example
|
|
158
|
+
* const value: Accessor<string> = createConnection(
|
|
159
|
+
* "initial value",
|
|
160
|
+
* [obj1, "sig-name", (...args) => "str"],
|
|
161
|
+
* [obj2, "sig-name", (...args) => "str"]
|
|
162
|
+
* )
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* Create an `Accessor` which sets up a list of `GObject.Object` signal connections.
|
|
166
|
+
* @param init The initial value
|
|
167
|
+
* @param signals A list of `GObject.Object`, signal name and callback pairs to connect.
|
|
168
|
+
*/
|
|
169
|
+
export function createConnection(init, h1, h2, h3, h4, h5, h6, h7, h8, h9) {
|
|
170
|
+
let value = init;
|
|
171
|
+
let dispose;
|
|
172
|
+
const subscribers = new Set();
|
|
173
|
+
const signals = [h1, h2, h3, h4, h5, h6, h7, h8, h9].filter((h) => h !== undefined);
|
|
174
|
+
const subscribe = (callback) => {
|
|
175
|
+
if (subscribers.size === 0) {
|
|
176
|
+
dispose = signals.map(([object, signal, callback]) => {
|
|
177
|
+
const id = object.connect(signal, (_, ...args) => {
|
|
178
|
+
const newValue = callback(...args);
|
|
179
|
+
if (value !== newValue) {
|
|
180
|
+
value = newValue;
|
|
181
|
+
subscribers.forEach((cb) => cb());
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
return () => object.disconnect(id);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
subscribers.add(callback);
|
|
188
|
+
return () => {
|
|
189
|
+
subscribers.delete(callback);
|
|
190
|
+
if (subscribers.size === 0) {
|
|
191
|
+
dispose.map((cb) => cb());
|
|
192
|
+
dispose.length = 0;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
return new Accessor(() => value, subscribe);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Create a signal from a provier function.
|
|
200
|
+
* The provider is called when the first subscriber appears and the returned dispose
|
|
201
|
+
* function from the provider will be called when the number of subscribers drop to zero.
|
|
202
|
+
*
|
|
203
|
+
* Example:
|
|
204
|
+
*
|
|
205
|
+
* ```ts
|
|
206
|
+
* const value = createExternal(0, (set) => {
|
|
207
|
+
* const interval = setInterval(() => set((v) => v + 1))
|
|
208
|
+
* return () => clearInterval(interval)
|
|
209
|
+
* })
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* @param init The initial value
|
|
213
|
+
* @param producer The producer function which should return a cleanup function
|
|
214
|
+
*/
|
|
215
|
+
export function createExternal(init, producer) {
|
|
216
|
+
let currentValue = init;
|
|
217
|
+
let dispose;
|
|
218
|
+
const subscribers = new Set();
|
|
219
|
+
const subscribe = (callback) => {
|
|
220
|
+
if (subscribers.size === 0) {
|
|
221
|
+
dispose = producer((v) => {
|
|
222
|
+
const newValue = typeof v === "function" ? v(currentValue) : v;
|
|
223
|
+
if (newValue !== currentValue) {
|
|
224
|
+
currentValue = newValue;
|
|
225
|
+
subscribers.forEach((cb) => cb());
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
subscribers.add(callback);
|
|
230
|
+
return () => {
|
|
231
|
+
subscribers.delete(callback);
|
|
232
|
+
if (subscribers.size === 0) {
|
|
233
|
+
dispose();
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
return new Accessor(() => currentValue, subscribe);
|
|
238
|
+
}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type GObject from "gi://GObject";
|
|
2
|
+
export declare function kebabify(str: string): string;
|
|
3
|
+
export type Pascalify<S extends string> = S extends `${infer Head}-${infer Tail}` ? `${Capitalize<Head>}${Pascalify<Tail>}` : Capitalize<S>;
|
|
4
|
+
export declare function snakeify(str: string): string;
|
|
5
|
+
export type XmlNode = {
|
|
6
|
+
name: string;
|
|
7
|
+
attributes?: Record<string, string>;
|
|
8
|
+
children?: Array<XmlNode>;
|
|
9
|
+
};
|
|
10
|
+
export declare function xml({ name, attributes, children }: XmlNode): string;
|
|
11
|
+
export declare function getterWorkaround<T extends object>(object: T, prop: Extract<keyof T, string>): void;
|
|
12
|
+
export declare function set(obj: GObject.Object, prop: string, value: any): void;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export function kebabify(str) {
|
|
2
|
+
return str
|
|
3
|
+
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
4
|
+
.replaceAll("_", "-")
|
|
5
|
+
.toLowerCase();
|
|
6
|
+
}
|
|
7
|
+
export function snakeify(str) {
|
|
8
|
+
return str
|
|
9
|
+
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
10
|
+
.replaceAll("-", "_")
|
|
11
|
+
.toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
export function xml({ name, attributes, children }) {
|
|
14
|
+
let builder = `<${name}`;
|
|
15
|
+
const attrs = Object.entries(attributes ?? []);
|
|
16
|
+
if (attrs.length > 0) {
|
|
17
|
+
for (const [key, value] of attrs) {
|
|
18
|
+
builder += ` ${key}="${value}"`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (children && children.length > 0) {
|
|
22
|
+
builder += ">";
|
|
23
|
+
for (const node of children) {
|
|
24
|
+
builder += xml(node);
|
|
25
|
+
}
|
|
26
|
+
builder += `</${name}>`;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
builder += " />";
|
|
30
|
+
}
|
|
31
|
+
return builder;
|
|
32
|
+
}
|
|
33
|
+
// Bindings work over properties in kebab-case because thats the convention of gobject
|
|
34
|
+
// however in js its either snake_case or camelCase
|
|
35
|
+
// also on DBus interfaces its PascalCase by convention
|
|
36
|
+
// so as a workaround we use get_property_name as a workaround
|
|
37
|
+
export function getterWorkaround(object, prop) {
|
|
38
|
+
Object.defineProperty(object, `get_${kebabify(prop).replaceAll("-", "_")}`, {
|
|
39
|
+
configurable: false,
|
|
40
|
+
enumerable: true,
|
|
41
|
+
value: () => object[prop],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// attempt setting a property of GObject.Object
|
|
45
|
+
export function set(obj, prop, value) {
|
|
46
|
+
const key = snakeify(prop);
|
|
47
|
+
const getter = `get_${key}`;
|
|
48
|
+
const setter = `set_${key}`;
|
|
49
|
+
let current;
|
|
50
|
+
if (getter in obj && typeof obj[getter] === "function") {
|
|
51
|
+
current = obj[getter]();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
current = obj[prop];
|
|
55
|
+
}
|
|
56
|
+
if (current !== value) {
|
|
57
|
+
if (setter in obj && typeof obj[setter] === "function") {
|
|
58
|
+
;
|
|
59
|
+
obj[setter](value);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
Object.assign(obj, { [prop]: value });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|