skills/padparadscho/skills/js-gnome-apps

js-gnome-apps

SKILL.md

GNOME JavaScript (GJS) Application Development

Technology Stack

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

Imports (ES Modules — required for new code)

// 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";

Keep imports grouped: built-in → platform (gi://) → local, separated by blank lines. Use PascalCase for imported modules.

GObject Subclassing

Register every GObject subclass with GObject.registerClass(). Use constructor() (not _init(), which is legacy pre-GNOME 42).

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");
    }
  },
);

Key rules:

  • Property names: kebab-case in GObject declarations, snake_case in JS accessors
  • Always call this.notify('prop-name') in setters to emit change notifications
  • GTypeName is optional unless the type is referenced in UI XML <template class="...">
  • InternalChildren maps id attrs in UI XML → this._childId; Childrenthis.childId

Application Entry Point

import 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 with Gio._promisify

Wrap *_async/*_finish pairs once at module level for async/await usage:

Gio._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");
}

Code Style (GJS conventions)

  • 4-space indentation, single quotes, semicolons
  • const by default, let when mutation needed, never var
  • File names: lowerCamelCase.js; directories: lowercase
  • Arrow functions for inline callbacks; .bind(this) for method references
  • export for public API, never var

Reference Files

Consult these based on the task at hand:

  • Project setup & packaging: references/project-setup.md — Meson build, Flatpak manifests, GResource, application ID, desktop files, GSettings schemas, directory layout
  • UI design (GTK4 + Libadwaita): references/ui-patterns.md — Widget templates (UI XML), adaptive layouts (breakpoints, split views, view switcher), boxed lists, style classes, header bars, GNOME HIG patterns
  • GIO & platform patterns: references/gio-patterns.md — File I/O, GSettings, actions & menus, D-Bus, subprocesses, list models, GVariant
Weekly Installs
6
First Seen
Feb 7, 2026
Installed on
amp6
opencode6
kimi-cli6
codex6
github-copilot6
gemini-cli6