Loading...
Loading...
Build native GNOME desktop applications using JavaScript (GJS) with GTK 4, Libadwaita, and the GNOME platform. Use when the user wants to create, modify, or debug a GNOME app written in JavaScript/GJS, including UI design with XML or Blueprint, GObject subclassing, Meson build setup, Flatpak packaging, or any task involving GJS bindings for GLib/GIO/GTK4/Adw libraries. Also use when working with `.ui` files, `meson.build`, GResource XML, GSettings schemas, `.desktop` files, or Flatpak manifests in a GJS project context.
npx skill4agent add padparadscho/skills js-gnome-apps| Component | Purpose |
|---|---|
| GJS | JavaScript runtime (SpiderMonkey) with GObject Introspection bindings |
| GTK 4 | UI toolkit |
| Libadwaita (Adw) 1.x | GNOME-specific widgets, adaptive layouts, styling |
| GLib / GIO | Core utilities, async I/O, settings, D-Bus, file operations |
| Meson | Build system |
| Flatpak | App packaging and distribution |
| Blueprint (optional) | Declarative UI markup that compiles to GTK XML |
// Built-in GJS modules
import Cairo from "cairo";
import Gettext from "gettext";
import System from "system";
// Platform libraries via gi:// URI
import GLib from "gi://GLib";
import GObject from "gi://GObject";
import Gio from "gi://Gio";
// Versioned imports (required when multiple API versions exist)
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";
// Relative user modules (include .js extension)
import * as Utils from "./lib/utils.js";gi://PascalCaseGObject.registerClass()constructor()_init()const MyWidget = GObject.registerClass(
{
GTypeName: "MyWidget",
Template: "resource:///com/example/MyApp/my-widget.ui",
InternalChildren: ["title_label", "action_button"],
Properties: {
"example-prop": GObject.ParamSpec.string(
"example-prop",
"",
"",
GObject.ParamFlags.READWRITE,
null,
),
},
Signals: {
"item-selected": {
param_types: [GObject.TYPE_STRING],
},
},
},
class MyWidget extends Gtk.Box {
constructor(params = {}) {
super(params);
// Template children: this._title_label, this._action_button
}
get example_prop() {
return this._example_prop ?? null;
}
set example_prop(value) {
if (this.example_prop === value) return;
this._example_prop = value;
this.notify("example-prop");
}
},
);kebab-casesnake_casethis.notify('prop-name')GTypeName<template class="...">InternalChildrenidthis._childIdChildrenthis.childIdimport GLib from "gi://GLib";
import GObject from "gi://GObject";
import Gio from "gi://Gio";
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw?version=1";
import { MyAppWindow } from "./window.js";
const MyApp = GObject.registerClass(
class MyApp extends Adw.Application {
constructor() {
super({
application_id: "com.example.MyApp",
flags: Gio.ApplicationFlags.DEFAULT_FLAGS,
});
const quitAction = new Gio.SimpleAction({ name: "quit" });
quitAction.connect("activate", () => this.quit());
this.add_action(quitAction);
this.set_accels_for_action("app.quit", ["<Primary>q"]);
}
vfunc_activate() {
let win = this.active_window;
if (!win) win = new MyAppWindow(this);
win.present();
}
},
);
const app = new MyApp();
app.run([System.programInvocationName, ...ARGV]);*_async/*_finishasync/awaitGio._promisify(
Gio.File.prototype,
"load_contents_async",
"load_contents_finish",
);
const file = Gio.File.new_for_path("/tmp/example.txt");
try {
const [contents] = await file.load_contents_async(null);
const text = new TextDecoder().decode(contents);
} catch (e) {
logError(e, "Failed to load file");
}constletvarlowerCamelCase.jslowercase.bind(this)exportvar