js-gnome-extensions

GNOME Shell Extensions

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "js-gnome-extensions" with this command: npx skills add padparadscho/skills/padparadscho-skills-js-gnome-extensions

GNOME Shell Extensions

Build extensions for GNOME Shell 45+ using GJS with ESModules.

Key Resources

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 — global object, 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, url

  • extension.js — Default export: subclass of Extension

Optional Files

  • prefs.js — Subclass of ExtensionPreferences (GTK4/Adwaita)

  • stylesheet.css — CSS for St widgets in gnome-shell (not prefs)

  • schemas/ — GSettings schema XML + compiled binary

  • locale/ — 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 call super(metadata)

  • Everything created in enable() MUST be cleaned up in disable()

  • disable() is called on lock screen (unless session-modes includes unlock-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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

rs-ratatui-crate

No summary provided by upstream source.

Repository SourceNeeds Review
General

js-gnome-apps

No summary provided by upstream source.

Repository SourceNeeds Review
General

rs-soroban-sdk

No summary provided by upstream source.

Repository SourceNeeds Review
General

js-stellar-sdk

No summary provided by upstream source.

Repository SourceNeeds Review