Skip to main content

gnome-shell-extension-development

Provides practice for writing, checking and shipping a GNOME Shell extension in GJS: the layer split that keeps logic testable, the gates that catch what unit tests cannot import, verifying shell APIs against a live throwaway shell, the extension lifecycle (including the lock screen and suspend), notifications, settings and the preferences window. Use when building or reviewing a GNOME Shell extension, its preferences window or its notifications.

Informações da origem

Repositório
dandgabr/Coacus
Última atividade na origem
8 de outubro de 2026 às 00:55
Idioma detectado do SKILL.md
inglês
Estrelas
6
Forks
5

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
2 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
gnome-shell-extension-development
description
Provides practice for writing, checking and shipping a GNOME Shell extension in GJS: the layer split that keeps logic testable, the gates that catch what unit tests cannot import, verifying shell APIs against a live throwaway shell, the extension lifecycle (including the lock screen and suspend), notifications, settings and the preferences window. Use when building or reviewing a GNOME Shell extension, its preferences window or its notifications.
tags
["gnome-shell","gjs","extension","notifications","testing"]
# GNOME Shell extension development An extension runs inside the desktop's own process, so a mistake costs more than in an application: a module that fails to load disables the extension, a leaked object survives it, and the platform decides things (the lock screen, Do Not Disturb) that no setting of yours can override. This skill is the practice that kept one extension honest through notifications, a preferences window and a first-use flow. The facts it relies on were verified on a live shell; they are in [the verified notes](references/gnome-shell-50-notes.md), with the version and the date, and must be re-verified for another shell version. ## 1. Split by process and by layer - **A pure core** holds every rule and every sentence the user reads: no GObject import, so it runs under the plain interpreter and the unit tests cover it. - **Services** hold the I/O (timers, files, the keyring, the system bus). **The interface layer** only draws. **The preferences window is another process** (GTK and libadwaita); shell code never imports GTK, and the preferences never import the shell's toolkit. - Enforce the split with a structure test that reads the imports: the core imports nothing outside itself, the shell toolkit stays in the interface layer, the notification API stays there too, and the system bus stays in the services. - **Separate state from drawing.** A controller owns the state and the actions, and takes everything that touches the system as injected dependencies; each screen is a thin view over it. Two screens that sign in the same account then share one implementation, one sign-in at a time and one place to review, and the controller is testable with fakes: no keyring, no port, no browser. ## 2. Gates for what unit tests cannot import The interface modules need the shell, so no unit test loads them. A name declared twice, or not defined, is invisible until the extension is enabled, and then it simply fails to load. 1. Check the **syntax of every module**. 2. **Enable the extension in a throwaway headless shell** and read its state and its error: the state must be enabled and the error empty. This is the gate that catches an undefined name. 3. **Look at it**: drive the interface in a throwaway shell and capture the screen. A passing gate does not say the icon is the right size. A gate that has never failed is a hypothesis. Seed a defect (a name declared twice, a name that does not exist), watch the gate fail, then restore the file and confirm the diff is empty. Make the check script build its generated inputs first (the compiled settings schema, the translation catalogs): a test that reads a stale build fails for a setting it has never heard of. ## 3. Verify the shell API against a live shell The shell's own scripts are compiled into its binary and cannot be listed or read. An API recalled from an older version is a guess. In a throwaway shell with the evaluation channel on, enumerate the prototypes, the GObject properties and the enum values of what you are about to use, write against what you saw, and record the version and the date. Confirm at least: the constructors and properties of the notification classes, the urgency and privacy-scope values, how the session mode reports a locked screen and where the notification settings live. ## 4. Lifecycle, the lock screen and suspend - **Create after enabling, destroy when disabling**: sources, notifications, timers, signal handlers, bus subscriptions. A late event after disabling must create nothing, so the notifier keeps a destroyed flag. - **Without the `unlock-dialog` session mode the shell disables the extension while the screen is locked.** Nothing of yours can then be on a lock screen, no alert is raised while locked, and a notification that was not read is gone after unlocking. Do not add a setting for what the platform already decides: a switch for details on the lock screen did nothing and was removed. - The extension comes back on unlock, so what it needs to remember (what it already announced) lives on disk, and its memory must be longer than the longest lock: a quota that crossed its threshold during the lock is announced on the first look afterwards. - **After suspend every timer is late at once** (they use the monotonic clock) and the data looks old. Subscribe to the login manager's sleep signal, hold connection alerts for a grace window, then refresh with jitter. ## 5. Notifications - Use **a source of your own**; keep one notification per provider and kind and replace the old one rather than stacking. - Urgency is **normal or high, never critical**: only critical bypasses Do Not Disturb. With it on, the banner is held for normal and high alike and the notification waits in the list. Actions do nothing while the screen is locked. - **Text comes only from fixed, translated templates**, names from a registry and clamped numbers. Nothing a provider or a user typed reaches a notification or a tooltip: strip controls and bidirectional overrides, cap the length, turn markup off. - **Quiet by construction.** Announce a crossing once and re-arm only after the value falls by a margin or the window resets; never announce the first reading; cap the number an hour and fold the rest into one summary; one connection alert per outage. A connection alert measured in time needs a clock, because a rejected sign-in is polled no more. Announce nothing until the set of providers is known. - Offer a **test button** in the preferences that raises a counter the shell consumes. The assistant never sends a notification on its own. ## 6. Settings and the preferences window - **Classify every settings key** as restored or kept by Restore defaults, in one list, with a test that fails for an unclassified key: a new setting must be decided about when it is added. Never reset what belongs to the user's accounts. - **Reset in one batch** (delay then apply): the shell reacts to every key, and eighteen writes are eighteen rebuilds. - A combo row must **not write back a value it only read**, or following a reset gives the key a user value again and the shell reacts twice. - Read a setting once and refresh it on change, and **re-check it where it is used**: the user can edit it by hand. - Dim a control when its master switch is off instead of blocking it, so the value stays visible. Show the current value in the row, not only inside it. - A danger or terms confirmation has **one code path** used by every screen, with the safe response as the default. ## 7. Internationalization - **Follow the session language.** Never change the locale of the process: it would translate the shell itself. A language without a catalog shows the source text. - The extraction tool only sees **literal strings** inside the translation calls: a table of texts built outside them is invisible, so write the texts where they are translated. Keep the percent sign in the template (`%d%%`), because the language decides the spacing. - A plain interpreter has no `String.format`: use a small printf, so the same code runs under the tests. ## 8. Checking a change See [the verified notes](references/gnome-shell-50-notes.md) for the recipes: the evaluation scope lacks the toolkit, a fresh shell opens a welcome dialog, a virtual pointer that starts in the corner triggers the hot corner, an icon needs a size, and one preferences page can be shown alone with a small harness and an in-memory settings backend.
Ver no GitHub