Skip to content

v1.2.1

Choose a tag to compare

@eugeniodepalo eugeniodepalo released this 16 Aug 13:00
· 69 commits to main since this release
0f0dbf6

Changelog

New Features

toVariant and fromVariant are public API on @gtkx/runtime

The GVariant codec behind useSetting was never exported, so calling a D-Bus method or building an a{sv} payload meant hand-assembling GLib.Variant.newArray, newDictEntry and newTuple and picking the right getter on the way back.

import { fromVariant, toVariant } from "@gtkx/runtime";

const args = toVariant("(a{sv})", [{ verbose: toVariant("b", true) }]);
const [names, count] = fromVariant("(asi)", reply);

A literal type string types both directions through VariantValue<S>: "as" an array of strings, "a{sv}" a record of GLib.Variant, "(si)" a [string, number], "x" and "t" a bigint; a string that is not a literal falls back to unknown. Unpacking is the inverse — a dictionary keyed by s, o or g comes back as a record and one keyed by anything else as a Map, a maybe as its value or null. A type string that is not exactly one complete GVariant type throws rather than half-reading it, and so does a value packed as "o" or "g" that is not a valid object path or signature.

Every deploy generates third-party notices

A package ships the Node.js runtime, the GTKX bundle and addon, and every npm package the bundle reaches, and nothing accounted for any of it: the deb's copyright file held the project's own terms alone, and no other target carried a notice file at all.

gtkx deploy now writes the deb's share/doc/<binaryName>/copyright in machine-readable copyright format 1.0, with a stanza per file the package carries, and gives every other target share/licenses/<binaryName>/THIRD-PARTY-NOTICES. Four sections: the bundled Node.js, whose LICENSE is the aggregate notice for V8, OpenSSL, ICU, libuv, zlib, brotli and llhttp; GTKX itself, with the MPL-2.0 notice, the MPL section 3.2(a) source pointer and the licenses of the Rust crates linked into gtkx.node; the bundled JavaScript dependencies, each license reproduced in full or its SPDX identifier listed; and the GTK, libadwaita, GtkSourceView and WebKitGTK the app declares, carrying what LGPL-2.1 section 6 asks of a work that uses a library.

The dependency list is what the build actually reached: gtkx build walks the module graph of dist/bundle.mjs and records every package into dist/gtkx-packages.json, which the deploy reads and keeps out of the staged tree. Flatpak source mode carries the notices inline to the same path. gtkx deploy --skip-build over a dist/ from an older gtkx build has no package list to read and fails asking for a build.

deploy.extraFiles takes a { source, mode } entry

An entry can now be { source: "tools/helper", mode: "755" } in place of a bare source path. mode is a three- or four-digit octal string, validated before anything is staged, and it is what nfpm records in the deb and the rpm, what the AppImage tree carries, and what the source-mode flatpak passes to install -D.

Breaking Changes

GLib criticals and addon failures raise an uncaught exception

A GLib CRITICAL, a GLib ERROR, a Rust panic caught at an FFI boundary and every internal failure the addon reports — a callback argument it could not decode, a boxed type it could not resolve, a timer it could not arm — produced one gtkx: line on stderr and nothing a JavaScript handler could see. Each now raises as a Node uncaughtException, marshalled to the install thread first when it came from another one. Records below CRITICAL are untouched, so GTK's theme-parser warnings stay warnings.

An app with no process.on("uncaughtException") handler now prints and exits non-zero at the first CRITICAL, so a g_return_if_fail violation that used to scroll past in a log ends the process. Install a handler and the app behaves as before: the handler runs, the call returns, the process stays up. gtkx dev already installs one. A Vitest run has no handler by default, so a suite that provoked criticals and passed anyway now fails — and the fix is the offending call, since GTK returned from it without doing what was asked.

deploy.flatpak.finishArgs and deploy.flatpak.cleanup merge with the defaults instead of replacing them

Setting either key threw the defaults away wholesale, so asking for one extra permission meant restating --share=ipc, --socket=wayland, --socket=fallback-x11 and --device=dri alongside it, and a config listing only --share=network produced an app that starts without a window. The defaults now come first, your entries follow, and duplicates collapse. To drop a default, ask for its negation — --nosocket=wayland, --unshare=ipc, --nodevice=dri — which is what flatpak build-finish takes. cleanup merges the same way but has no negation for a pattern; an empty array turns cleanup off entirely.

A project that set finishArgs to a list narrower than the defaults now builds a flatpak with wider permissions than before, and one that set cleanup now has /include, /share/pkgconfig, *.la and *.a stripped as well. Compare the finish-args and cleanup blocks of the generated manifest against the previous release's, and negate or empty what you meant to exclude.

emit only accepts a signal the class declares

v1.2.0 narrowed connect, on, off and friends to the class's own signal map but left every generated class a second emit(sigName: string, ...args: unknown[]): unknown overload, which any string satisfied: button.emit("clickd") type-checked and threw Unknown signal 'clickd' at runtime, and a correctly spelled name resolving through it lost its argument types and returned unknown. That overload now exists only on GObject.Object. Correct the name, or go through the root with (button as GObject.Object).emit(name).

Bugfixes

  • Fixed record fields with no generated accessor being declared in the .d.ts anyway, so GLib.Queue.head, GLib.List.next, Pango.LayoutLine.runs and GObject.SignalQuery.paramTypes typed as their GIR type and every one read undefined at runtime. Fields backed by a GList, GSList, GHashTable or a sibling-length array are gone from the class and the declarations alike, and the one shape that can be read — a null-terminated pointer array — gained a real accessor, so Gio.DBusNodeInfo.interfaces, Gio.DBusInterfaceInfo.methods, GdkPixbuf.PixbufFormat.mimeTypes and Gtk.RecentData.groups decode instead of answering undefined.
  • Fixed array-typed record fields being offered as writable when writing one always threw, since a field descriptor is transfer-none and the native writer refuses a transfer-none container outright. Gdk.TimeCoord.axes and Gsk.RoundedRect.corner had setters and appeared in ConstructorProps, so new Gdk.TimeCoord({ axes }) raised on every call. Array, list and hash-table fields are read-only now and out of ConstructorProps; inline struct arrays keep their element-wise setters.
  • Fixed a signal GIR marks detailed="1" accepting no ::detail suffix in its generated map, although the runtime has always stripped the suffix before looking the signal up. Only notify was special-cased, so settings.on("changed::theme", handler) and bar.on("offset-changed::low", handler) stopped type-checking once v1.2.0 removed the plain-string overloads. Each detailed signal now contributes a [detail: `changed::${string}`] index signature to both its maps, covering Gio.Settings, Gio.DBusProxy, Gio.ActionGroup, Gtk.LevelBar, Gtk.AppChooserButton, Adw.MessageDialog and WebKit.
  • Fixed gtkx codegen not counting @gtkx/runtime among the packages that must be able to import the generated @gtkx/gi, so a layout that installs it above the node_modules the store is written into produced a store the runtime could never resolve. Codegen now stops with the same error it gives for the other @gtkx packages.
  • Fixed a ColumnView over items with children drawing a Gtk.TreeExpander in every cell of a row instead of one per row, so a three-column tree gave each row three toggles, stamped expanderDescriptions on all three, and indented every column by the row's depth, leaving child cells further right than their parent's instead of lining up. Exactly one column carries the expander now — the first whose visible is not false — and the rest render renderCell directly and stay aligned at every depth. expandedIds and onExpandedChange are unaffected; a tree that wants the toggle elsewhere lists that column first.
  • Fixed useSignal running the handler captured on the first render for every emission inside a component wrapped in memo or forwardRef, so a memoized row that re-rendered with a new closure kept reading stale props and state for the life of the component. The hook built on React 19.2's useEffectEvent, which does not pick up the updated function through those wrappers; the handler is now held in a ref written from useInsertionEffect, so every emission runs the latest committed render's handler whether the component is wrapped or not, and a changing handler still never reconnects the signal.
  • Fixed deploy.extraFiles installing every file 644, or 755 when the destination name ended in .node or .so, so a helper script listed there arrived unrunnable however it had been staged. An entry without an explicit mode now takes 755 when the source file has any execute bit and 644 when it has none, and the extension decides nothing.
  • Fixed deploy.flatpak.mode: "source" emitting a manifest that installs no MIME package, no license file and none of the deploy.extraFiles entries, all three of which the prebuilt flatpak installs, so a Flathub submission registered none of the app's fileAssociations and shipped without its license. All three install now, and because a source build has only the git checkout to install from, a path that resolves outside the project root — or a symlink whose target does — fails the deploy naming the file rather than producing a manifest that cannot build.
  • Fixed the flatpak permission warnings running on deploys that never asked for a flatpak. Both now run only when flatpak is among the targets being built.
  • Fixed a panic inside the addon being reported with nothing that said where it happened, so every panic under a GObject callback read panic at callback entry: <payload> whatever line of Rust panicked. A panic hook now records the location and the report reads panic at <boundary> (<file>:<line>:<column>): <payload>.