gnim 1.6.4 → 1.7.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.
Binary file
@@ -3,6 +3,9 @@ import Gio from "gi://Gio?version=2.0"
3
3
  import { configue } from "../jsx/env.js"
4
4
  import { getType, onCleanup, Accessor, Fragment } from "../index.js"
5
5
 
6
+ import type Adw from "gi://Adw"
7
+ const adw = await import("gi://Adw").then((m) => m.default).catch(() => null)
8
+
6
9
  const dummyBuilder = new Gtk.Builder()
7
10
 
8
11
  const { intrinsicElements } = configue({
@@ -14,6 +17,14 @@ const { intrinsicElements } = configue({
14
17
  ]
15
18
  return keys
16
19
  }
20
+ if (adw && ctor === adw.ToggleGroup) {
21
+ const keys: Array<Extract<keyof Adw.ToggleGroup, string>> = [
22
+ "active",
23
+ "activeName",
24
+ "active_name",
25
+ ]
26
+ return keys
27
+ }
17
28
  },
18
29
  setCss(object, css) {
19
30
  if (!(object instanceof Gtk.Widget)) {
package/dist/jsx/jsx.ts CHANGED
@@ -167,11 +167,6 @@ export function append(parent: GObject.Object, child: GObject.Object) {
167
167
  return
168
168
  }
169
169
 
170
- if (appendChild in parent && typeof parent[appendChild] === "function") {
171
- parent[appendChild](child, getType(child))
172
- return
173
- }
174
-
175
170
  if (child instanceof Fragment) {
176
171
  for (const ch of child) {
177
172
  append(parent, ch)
@@ -199,12 +194,12 @@ export function append(parent: GObject.Object, child: GObject.Object) {
199
194
  return
200
195
  }
201
196
 
202
- if (child) {
203
- if (!(child instanceof GObject.Object)) {
204
- child = env.textNode(child)
205
- }
206
- env.appendChild(parent, child)
197
+ if (appendChild in parent && typeof parent[appendChild] === "function") {
198
+ parent[appendChild](child, getType(child))
199
+ return
207
200
  }
201
+
202
+ env.appendChild(parent, child)
208
203
  }
209
204
 
210
205
  /** @internal */
package/dist/jsx/state.ts CHANGED
@@ -10,6 +10,8 @@ type SubscribeFunction = (callback: SubscribeCallback) => DisposeFunction
10
10
 
11
11
  export type Accessed<T> = T extends Accessor<infer V> ? V : never
12
12
 
13
+ const empty = Symbol("empty computed value")
14
+
13
15
  // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
14
16
  export class Accessor<T = unknown> extends Function {
15
17
  static $gtype = GObject.TYPE_JSOBJECT as unknown as GObject.GType<Accessor>
@@ -29,7 +31,6 @@ export class Accessor<T = unknown> extends Function {
29
31
  * @returns Unsubscribe function.
30
32
  */
31
33
  subscribe(callback: SubscribeCallback): DisposeFunction {
32
- // TODO: auto unsub when a scope is available?
33
34
  return this.#subscribe(callback)
34
35
  }
35
36
 
@@ -49,7 +50,38 @@ export class Accessor<T = unknown> extends Function {
49
50
  }
50
51
 
51
52
  protected _call<R = T>(transform: (value: T) => R): Accessor<R> {
52
- return this.as(transform)
53
+ let value: typeof empty | R = empty
54
+ let unsub: DisposeFunction
55
+
56
+ const subscribers = new Set<SubscribeCallback>()
57
+
58
+ const subscribe: SubscribeFunction = (callback) => {
59
+ if (subscribers.size === 0) {
60
+ unsub = this.subscribe(() => {
61
+ const newValue = transform(this.get())
62
+ if (value !== newValue) {
63
+ value = newValue
64
+ Array.from(subscribers).forEach((cb) => cb())
65
+ }
66
+ })
67
+ }
68
+
69
+ subscribers.add(callback)
70
+
71
+ return () => {
72
+ subscribers.delete(callback)
73
+ if (subscribers.size === 0) {
74
+ value = empty
75
+ unsub()
76
+ }
77
+ }
78
+ }
79
+
80
+ const get = (): R => {
81
+ return value !== empty ? value : transform(this.get())
82
+ }
83
+
84
+ return new Accessor(get, subscribe)
53
85
  }
54
86
 
55
87
  toString(): string {
@@ -64,8 +96,9 @@ export class Accessor<T = unknown> extends Function {
64
96
 
65
97
  export interface Accessor<T> {
66
98
  /**
67
- * Create a new `Accessor` that applies a transformation on its value.
99
+ * Create a computed `Accessor` that caches its transformed value.
68
100
  * @param transform The transformation to apply. Should be a pure function.
101
+ * see {@link createComputed} and {@link createComputedProducer}
69
102
  */
70
103
  <R = T>(transform: (value: T) => R): Accessor<R>
71
104
  }
@@ -104,16 +137,22 @@ export function createState<T>(init: T): State<T> {
104
137
  return [new Accessor(() => currentValue, subscribe), set as Setter<T>]
105
138
  }
106
139
 
107
- const empty = Symbol("empty computed value")
108
-
109
140
  function createComputedProducer<T>(fn: (track: <V>(signal: Accessor<V>) => V) => T): Accessor<T> {
110
- const subscribers = new Set<SubscribeCallback>()
111
141
  let value: typeof empty | T = empty
112
142
  let prevDeps = new Map<Accessor, DisposeFunction>()
113
143
 
144
+ const subscribers = new Set<SubscribeCallback>()
145
+ const cache = new Map<Accessor, unknown>()
146
+
114
147
  const effect = () => {
115
148
  const deps = new Set<Accessor>()
116
- value = fn((v) => (deps.add(v), v.get()))
149
+ const newValue = fn((v) => {
150
+ deps.add(v)
151
+ return (cache.get(v) as any) || v.get()
152
+ })
153
+
154
+ const didChange = value !== newValue
155
+ value = newValue
117
156
 
118
157
  const newDeps = new Map<Accessor, DisposeFunction>()
119
158
 
@@ -127,12 +166,21 @@ function createComputedProducer<T>(fn: (track: <V>(signal: Accessor<V>) => V) =>
127
166
 
128
167
  for (const dep of deps) {
129
168
  if (!newDeps.has(dep)) {
130
- newDeps.set(dep, dep.subscribe(effect))
169
+ const dispose = dep.subscribe(() => {
170
+ const value = dep.get()
171
+ if (cache.get(dep) !== value) {
172
+ cache.set(dep, value)
173
+ effect()
174
+ }
175
+ })
176
+ newDeps.set(dep, dispose)
131
177
  }
132
178
  }
133
179
 
134
180
  prevDeps = newDeps
135
- Array.from(subscribers).forEach((cb) => cb())
181
+ if (didChange) {
182
+ Array.from(subscribers).forEach((cb) => cb())
183
+ }
136
184
  }
137
185
 
138
186
  const subscribe: SubscribeFunction = (callback) => {
@@ -154,7 +202,7 @@ function createComputedProducer<T>(fn: (track: <V>(signal: Accessor<V>) => V) =>
154
202
  }
155
203
 
156
204
  const get = (): T => {
157
- return value === empty ? fn((v) => v.get()) : value
205
+ return value !== empty ? value : fn((v) => v.get())
158
206
  }
159
207
 
160
208
  return new Accessor(get, subscribe)
@@ -166,17 +214,36 @@ function createComputedArgs<
166
214
  V = Args,
167
215
  >(deps: Deps, transform?: (...args: Args) => V): Accessor<V> {
168
216
  let dispose: Array<DisposeFunction>
217
+ let value: typeof empty | V = empty
218
+
169
219
  const subscribers = new Set<SubscribeCallback>()
170
220
  const cache = new Array<unknown>(deps.length)
171
221
 
222
+ const compute = (): V => {
223
+ const args = deps.map((dep, i) => {
224
+ if (!cache[i]) {
225
+ cache[i] = dep.get()
226
+ }
227
+
228
+ return cache[i]
229
+ })
230
+
231
+ return transform ? transform(...(args as Args)) : (args as V)
232
+ }
233
+
172
234
  const subscribe: SubscribeFunction = (callback) => {
173
235
  if (subscribers.size === 0) {
174
236
  dispose = deps.map((dep, i) =>
175
237
  dep.subscribe(() => {
176
- const value = dep.get()
177
- if (cache[i] !== value) {
238
+ const newValue = dep.get()
239
+ if (cache[i] !== newValue) {
178
240
  cache[i] = dep.get()
179
- Array.from(subscribers).forEach((cb) => cb())
241
+
242
+ const newValue = compute()
243
+ if (value !== newValue) {
244
+ value = newValue
245
+ Array.from(subscribers).forEach((cb) => cb())
246
+ }
180
247
  }
181
248
  }),
182
249
  )
@@ -187,6 +254,7 @@ function createComputedArgs<
187
254
  return () => {
188
255
  subscribers.delete(callback)
189
256
  if (subscribers.size === 0) {
257
+ value = empty
190
258
  dispose.map((cb) => cb())
191
259
  dispose.length = 0
192
260
  cache.length = 0
@@ -195,15 +263,7 @@ function createComputedArgs<
195
263
  }
196
264
 
197
265
  const get = (): V => {
198
- const args = deps.map((dep, i) => {
199
- if (!cache[i]) {
200
- cache[i] = dep.get()
201
- }
202
-
203
- return cache[i]
204
- })
205
-
206
- return transform ? transform(...(args as Args)) : (args as V)
266
+ return value !== empty ? value : compute()
207
267
  }
208
268
 
209
269
  return new Accessor(get, subscribe)
@@ -303,7 +363,9 @@ export function createBinding<T>(object: GObject.Object | Gio.Settings, key: str
303
363
  const get = (): T => {
304
364
  if (object instanceof Gio.Settings) {
305
365
  return object.get_value(key).recursiveUnpack() as T
306
- } else {
366
+ }
367
+
368
+ if (object instanceof GObject.Object) {
307
369
  const getter = `get_${prop.replaceAll("-", "_")}` as keyof typeof object
308
370
 
309
371
  if (getter in object && typeof object[getter] === "function") {
@@ -312,9 +374,9 @@ export function createBinding<T>(object: GObject.Object | Gio.Settings, key: str
312
374
 
313
375
  if (prop in object) return object[prop] as T
314
376
  if (key in object) return object[key as keyof typeof object] as T
315
-
316
- throw Error(`cannot get property ${key}`)
317
377
  }
378
+
379
+ throw Error(`cannot get property "${key}" on "${object}"`)
318
380
  }
319
381
 
320
382
  return new Accessor(get, subscribe)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnim",
3
- "version": "1.6.4",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "author": "Aylur",
6
6
  "license": "MIT",
@@ -20,12 +20,16 @@
20
20
  "docs:preview": "vitepress preview docs"
21
21
  },
22
22
  "devDependencies": {
23
+ "@eslint/js": "latest",
23
24
  "@girs/adw-1": "latest",
24
25
  "@girs/clutter-16": "latest",
25
26
  "@girs/gtk-3.0": "latest",
27
+ "@girs/gtk-4.0": "latest",
26
28
  "@girs/soup-3.0": "latest",
29
+ "@girs/shell-16": "latest",
27
30
  "@girs/st-16": "latest",
28
31
  "@girs/gnome-shell": "latest",
32
+ "@girs/gjs": "latest",
29
33
  "esbuild": "latest",
30
34
  "eslint": "latest",
31
35
  "typescript": "latest",