js-gnome-extensions
GNOME Shell Extensions
Build extensions for GNOME Shell 45+ using GJS with ESModules.
Key Resources
- Public APIs (GJS Docs): https://gjs-docs.gnome.org/
- GNOME Shell UI Source: https://gitlab.gnome.org/GNOME/gnome-shell/-/tree/main/js/ui
- Extensions Guide: https://gjs.guide/extensions/
- Review Guidelines: https://gjs.guide/extensions/review-guidelines/review-guidelines.html
Architecture Overview
Extensions run inside the gnome-shell process using Clutter/St toolkits (not GTK).
Preferences run in a separate GTK4/Adwaita process.
Library stack (bottom-up):
- Clutter — Actor-based toolkit, layout managers, animations
- St — Shell Toolkit: buttons, icons, labels, entries, scroll views (CSS-styleable)
- Meta (Mutter) — displays, workspaces, windows, keybindings
- Shell —
globalobject, app tracking, utilities - js/ui/ — GNOME Shell JS modules (Main, Panel, PopupMenu, QuickSettings, etc.)
Import conventions in extension.js:
import Clutter from "gi://Clutter";
import GObject from "gi://GObject";
import Gio from "gi://Gio";
import GLib from "gi://GLib";
import Meta from "gi://Meta";
import Shell from "gi://Shell";
import St from "gi://St";
import {
Extension,
gettext as _,
} from "resource:///org/gnome/shell/extensions/extension.js";
import * as Main from "resource:///org/gnome/shell/ui/main.js";
import * as PanelMenu from "resource:///org/gnome/shell/ui/panelMenu.js";
import * as PopupMenu from "resource:///org/gnome/shell/ui/popupMenu.js";
import * as QuickSettings from "resource:///org/gnome/shell/ui/quickSettings.js";
Import conventions in prefs.js:
import Gdk from "gi://Gdk?version=4.0";
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw";
import Gio from "gi://Gio";
import {
ExtensionPreferences,
gettext as _,
} from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
Critical rule: Never import GTK/Gdk/Adw in extension.js. Never import Clutter/Meta/St/Shell in prefs.js.
Extension Lifecycle
Required Files
metadata.json— UUID, name, description, shell-version, urlextension.js— Default export: subclass ofExtension
Optional Files
prefs.js— Subclass ofExtensionPreferences(GTK4/Adwaita)stylesheet.css— CSS for St widgets in gnome-shell (not prefs)schemas/— GSettings schema XML + compiled binarylocale/— Gettext translation .mo files
enable()/disable() Contract
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
export default class MyExtension extends Extension {
enable() {
// Create objects, connect signals, add UI
}
disable() {
// MUST undo everything done in enable():
// - Destroy all created widgets
// - Disconnect all signals
// - Remove all GLib.timeout/idle sources
// - Null out all references
}
}
Rules (enforced by extension review):
- Do NOT create GObject instances or connect signals in
constructor() constructor()may only set up static data (RegExp, Map, etc.) and callsuper(metadata)- Everything created in
enable()MUST be cleaned up indisable() disable()is called on lock screen (unlesssession-modesincludesunlock-dialog)
Reference Files
Detailed documentation is split into reference files. Read the appropriate file based on the task:
- references/review-guidelines.md — Complete review rules for extensions.gnome.org submission. Read before submitting or when reviewing extension code for compliance.
- references/ui-patterns.md — Panel indicators, popup menus, quick settings (toggles, sliders, menus), dialogs, notifications, and search providers with complete code examples. Read when building any UI component.
- references/preferences.md — GSettings schemas, prefs.js with GTK4/Adwaita, settings binding. Read when implementing extension preferences.
- references/development.md — Getting started, testing, debugging, translations, InjectionManager, and packaging. Read when setting up a new extension or debugging.
- references/porting-guide.md — Breaking changes for GNOME Shell 45–49. Read when porting an extension to a newer version.
Quick Start: Panel Indicator Extension
Minimal working extension with a panel icon:
metadata.json
{
"uuid": "my-extension@example.com",
"name": "My Extension",
"description": "Does something useful",
"shell-version": ["47", "48", "49"],
"url": "https://github.com/user/my-extension"
}
extension.js
import St from "gi://St";
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
import * as Main from "resource:///org/gnome/shell/ui/main.js";
import * as PanelMenu from "resource:///org/gnome/shell/ui/panelMenu.js";
export default class MyExtension extends Extension {
enable() {
this._indicator = new PanelMenu.Button(0.0, this.metadata.name, false);
const icon = new St.Icon({
icon_name: "face-laugh-symbolic",
style_class: "system-status-icon",
});
this._indicator.add_child(icon);
Main.panel.addToStatusArea(this.uuid, this._indicator);
}
disable() {
this._indicator?.destroy();
this._indicator = null;
}
}
Install & Test
# Create extension directory
mkdir -p ~/.local/share/gnome-shell/extensions/my-extension@example.com
# Copy files there, then:
# Wayland: run nested session
dbus-run-session gnome-shell --devkit --wayland # GNOME 49+
dbus-run-session gnome-shell --nested --wayland # GNOME 48 and earlier
# Enable extension in nested session
gnome-extensions enable my-extension@example.com
# Watch logs
journalctl -f -o cat /usr/bin/gnome-shell
Common Patterns Cheatsheet
Connect a signal (and clean up)
enable() {
this._handlerId = someObject.connect('some-signal', () => { /* ... */ });
}
disable() {
if (this._handlerId) {
someObject.disconnect(this._handlerId);
this._handlerId = null;
}
}
Add a timeout (and clean up)
enable() {
this._timeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
// do work
return GLib.SOURCE_CONTINUE; // or GLib.SOURCE_REMOVE
});
}
disable() {
if (this._timeoutId) {
GLib.Source.remove(this._timeoutId);
this._timeoutId = null;
}
}
Use GSettings
enable() {
this._settings = this.getSettings(); // uses metadata settings-schema
this._settings.bind('show-indicator', this._indicator, 'visible',
Gio.SettingsBindFlags.DEFAULT);
}
disable() {
this._settings = null;
}
Override a method (InjectionManager)
import {
Extension,
InjectionManager,
} from "resource:///org/gnome/shell/extensions/extension.js";
import { Panel } from "resource:///org/gnome/shell/ui/panel.js";
export default class MyExtension extends Extension {
enable() {
this._injectionManager = new InjectionManager();
this._injectionManager.overrideMethod(
Panel.prototype,
"toggleCalendar",
(originalMethod) => {
return function (...args) {
console.debug("Calendar toggled!");
originalMethod.call(this, ...args);
};
},
);
}
disable() {
this._injectionManager.clear();
this._injectionManager = null;
}
}
Packaging for Submission
cd ~/.local/share/gnome-shell/extensions/my-extension@example.com
gnome-extensions pack --podir=po --extra-source=utils.js .
# GNOME 49+: upload directly
gnome-extensions upload --accept-tos
More from padparadscho/skills
rs-ratatui-crate
Build terminal user interfaces (TUIs) in Rust with Ratatui (v0.30). Use this skill whenever working with the ratatui crate for creating interactive terminal applications, including: (1) Setting up a new Ratatui project, (2) Creating or modifying terminal UI layouts, (3) Implementing widgets (lists, tables, charts, text, gauges, etc.), (4) Handling keyboard/mouse input and events, (5) Structuring TUI application architecture (TEA, component-based, or monolithic patterns), (6) Writing custom widgets, (7) Managing application state in a TUI context, (8) Terminal setup/teardown and panic handling, (9) Testing TUI rendering with TestBackend. Also triggers for questions about crossterm event handling in a Ratatui context, tui-input, tui-textarea, or any ratatui-* ecosystem crate.
22js-gnome-apps
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.
6js-stellar-sdk
Guide for building applications with the Stellar JS SDK (@stellar/stellar-sdk). Use when working with the Stellar blockchain in JavaScript/TypeScript — including sending payments, creating accounts, issuing assets, managing trustlines, trading on the DEX, querying Horizon, interacting with Stellar RPC, streaming events, building and signing transactions, multisig, claimable balances, sponsored reserves, SEP-10 auth, federation, fee-bump transactions, and interacting with Soroban smart contracts via the JS SDK. Covers all @stellar/stellar-sdk usage patterns (Horizon module, rpc module, contract module, TransactionBuilder, Keypair, Operation, Asset, etc.).
5rs-soroban-sdk
Expert guidance for building smart contracts on Stellar using the Soroban Rust SDK. Use this skill when working with Soroban smart contracts for tasks including (1) creating new contracts with [contract] and [contractimpl] attributes, (2) implementing storage with Persistent, Temporary, or Instance storage types, (3) working with auth contexts and authorization, (4) handling tokens and Stellar Asset Contracts, (5) writing tests with testutils, (6) deploying contracts, (7) working with events and logging, (8) using crypto functions, (9) debugging contract errors, (10) security best practices and vulnerability prevention, (11) avoiding common security pitfalls like missing authorization, integer overflow, or reinitialization attacks.
5js-stronghold-sdk
Guide for integrating and building with the Stronghold Pay JS SDK and REST API for payment processing. Use when working with Stronghold Pay payment integration, accepting ACH/bank debit payments, linking bank accounts, creating charges/tips, generating PayLinks, or building checkout flows — in sandbox or live environments. Covers Stronghold.Pay.JS drop-in UI, REST API v2 endpoints, PayLink hosted payment pages, customer token management, and payment source handling.
3rs-yew-crate
Expert guidance for building Rust + WebAssembly frontend web applications using the Yew framework (v0.22). Use when creating, modifying, debugging, or architecting Yew applications — including function components, hooks, props, routing, contexts, events, server-side rendering, agents, and Suspense. Covers project setup with Trunk, the html! macro, state management, data fetching, and integration with the broader Yew/WASM ecosystem (yew-router, gloo, wasm-bindgen, web-sys, stylist, yewdux).
2