debdiff *.dsc | filterdiff -p1 -x'po/*.po'

diff -Nru gnome-shell-48.3/debian/changelog gnome-shell-48.4/debian/changelog
--- gnome-shell-48.3/debian/changelog	2025-07-13 12:24:58.000000000 +0100
+++ gnome-shell-48.4/debian/changelog	2025-08-18 15:35:38.000000000 +0100
@@ -1,3 +1,46 @@
+gnome-shell (48.4-1~deb13u1) trixie; urgency=medium
+
+  * Team upload
+  * d/control, d/gbp.conf: Set packaging branch for trixie stable updates
+
+ -- Simon McVittie <smcv@debian.org>  Mon, 18 Aug 2025 15:35:38 +0100
+
+gnome-shell (48.4-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream stable release
+    - network: If a network has no ID, don't treat it as available,
+      avoiding breaking the network menu
+      (gnome-shell!3785 upstream)
+    - Improve URL recognition heuristic for notifications so that non-URLs do
+      not become a link
+      (gnome-shell#8517 upstream)
+    - In gdm, improve efficiency of user list
+      (gnome-shell!3799 upstream)
+    - Fix signal order when taking a screenshot interactively is triggered
+      via D-Bus, for example from xdg-desktop-portal
+      (gnome-shell#8499 upstream)
+    - Improve cursor scaling on systems with different-DPI monitors when using
+      the Magnifier accessibility tool
+      (gnome-shell!475 upstream)
+    - In sliders like volume and brightness, avoid drawing part of the bar
+      over the handle in RTL locales
+      (gnome-shell!3817 upstream)
+    - Improve robustness of signal connections in the Thunderbolt and
+      smart-card code
+      (gnome-shell!3796 upstream)
+    - Code cleanups in extensions management service
+      (part of gnome-shell!3750 upstream)
+    - Translation updates
+  * d/control: Bump gjs version to 1.81.2 as per meson.build.
+    No practical effect, 1.82.x is already in trixie.
+  * d/gbp.conf: Use debian/forky branch for uploads targeting forky.
+    We'll stick to 48.x in testing/unstable for now, to get better testing
+    for future 48.x updates in trixie. Preliminary 49.x packaging for
+    experimental is already using the debian/latest branch.
+
+ -- Simon McVittie <smcv@debian.org>  Fri, 08 Aug 2025 09:19:26 +0100
+
 gnome-shell (48.3-1) unstable; urgency=medium
 
   * Team upload
diff -Nru gnome-shell-48.3/debian/control gnome-shell-48.4/debian/control
--- gnome-shell-48.3/debian/control	2025-07-13 12:24:58.000000000 +0100
+++ gnome-shell-48.4/debian/control	2025-08-18 15:35:38.000000000 +0100
@@ -9,7 +9,7 @@
                debhelper-compat (= 13),
                dh-sequence-gir,
                dh-sequence-gnome,
-               gjs (>= 1.73.1),
+               gjs (>= 1.81.2),
                gir1.2-accountsservice-1.0 <!nocheck>,
                gir1.2-gcr-4-dev,
                gir1.2-gdkpixbuf-2.0-dev,
@@ -32,7 +32,7 @@
                libecal2.0-dev (>= 3.45),
                libedataserver1.2-dev (>= 3.45),
                libgcr-4-dev (>= 3.90.0),
-               libgjs-dev (>= 1.73.1),
+               libgjs-dev (>= 1.81.2),
                libgl1-mesa-dri <!nocheck>,
                libglib2.0-dev (>= 2.80),
                libgnome-autoar-0-dev,
@@ -101,7 +101,7 @@
          gir1.2-rsvg-2.0,
          gir1.2-soup-3.0,
          gir1.2-upowerglib-1.0,
-         gjs (>= 1.73.1),
+         gjs (>= 1.81.2),
          gnome-control-center (>= 1:46),
          gnome-settings-daemon (>= 47~rc),
          gnome-shell-common (= ${source:Version}),
@@ -158,7 +158,7 @@
 Architecture: linux-any
 Depends: gir1.2-adw-1,
          gir1.2-gtk-4.0,
-         gjs (>= 1.73.1),
+         gjs (>= 1.81.2),
          gnome-shell (= ${binary:Version}),
          ${gir:Depends},
          ${misc:Depends},
diff -Nru gnome-shell-48.3/js/dbusServices/extensions/css/application.css gnome-shell-48.4/js/dbusServices/extensions/css/application.css
--- gnome-shell-48.3/js/dbusServices/extensions/css/application.css	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/dbusServices/extensions/css/application.css	2025-08-03 11:53:34.000000000 +0100
@@ -1,5 +1,3 @@
-.error-page preferencespage { margin: 30px; }
-
 .expander { padding: 12px; }
 .expander.expanded { border: 0 solid @borders; border-bottom-width: 1px; }
 .expander-toolbar {
diff -Nru gnome-shell-48.3/js/dbusServices/extensions/extensionPrefsDialog.js gnome-shell-48.4/js/dbusServices/extensions/extensionPrefsDialog.js
--- gnome-shell-48.3/js/dbusServices/extensions/extensionPrefsDialog.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/dbusServices/extensions/extensionPrefsDialog.js	2025-08-03 11:53:34.000000000 +0100
@@ -7,14 +7,18 @@
 
 import {formatError} from './misc/errorUtils.js';
 
-export const ExtensionPrefsDialog = GObject.registerClass({
-    GTypeName: 'ExtensionPrefsDialog',
-    Signals: {
+export class ExtensionPrefsDialog extends Adw.PreferencesWindow {
+    static [GObject.GTypeName] = 'ExtensionPrefsDialog';
+    static [GObject.signals] = {
         'loaded': {},
-    },
-}, class ExtensionPrefsDialog extends Adw.PreferencesWindow {
-    _init(extension) {
-        super._init({
+    };
+
+    static {
+        GObject.registerClass(this);
+    }
+
+    constructor(extension) {
+        super({
             title: extension.metadata.name,
             search_enabled: false,
         });
@@ -66,36 +70,36 @@
 
         this.add(new ExtensionPrefsErrorPage(this._extension, e));
     }
-});
+}
 
-const ExtensionPrefsErrorPage = GObject.registerClass({
-    GTypeName: 'ExtensionPrefsErrorPage',
-    Template: 'resource:///org/gnome/Shell/Extensions/ui/extension-error-page.ui',
-    InternalChildren: [
+class ExtensionPrefsErrorPage extends Adw.PreferencesPage {
+    static [GObject.GTypeName] = 'ExtensionPrefsErrorPage';
+    static [Gtk.template] =
+        'resource:///org/gnome/Shell/Extensions/ui/extension-error-page.ui';
+
+    static [Gtk.internalChildren] = [
         'expander',
         'expanderArrow',
         'revealer',
         'errorView',
-    ],
-}, class ExtensionPrefsErrorPage extends Adw.PreferencesPage {
-    static _classInit(klass) {
-        super._classInit(klass);
+    ];
+
+    static {
+        GObject.registerClass(this);
 
-        klass.install_action('page.copy-error',
+        this.install_action('page.copy-error',
             null,
             self => {
                 const clipboard = self.get_display().get_clipboard();
                 clipboard.set(self._errorMarkdown);
             });
-        klass.install_action('page.show-url',
+        this.install_action('page.show-url',
             null,
             self => Gtk.show_uri(self.get_root(), self._url, Gdk.CURRENT_TIME));
-
-        return klass;
     }
 
-    _init(extension, error) {
-        super._init();
+    constructor(extension, error) {
+        super();
 
         this._addCustomStylesheet();
 
@@ -157,4 +161,4 @@
             provider,
             Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
     }
-});
+}
diff -Nru gnome-shell-48.3/js/dbusServices/extensions/ui/extension-error-page.ui gnome-shell-48.4/js/dbusServices/extensions/ui/extension-error-page.ui
--- gnome-shell-48.3/js/dbusServices/extensions/ui/extension-error-page.ui	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/dbusServices/extensions/ui/extension-error-page.ui	2025-08-03 11:53:34.000000000 +0100
@@ -1,9 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <template class="ExtensionPrefsErrorPage" parent="AdwPreferencesPage">
-    <style>
-      <class name="error-page"/>
-    </style>
     <child>
       <object class="AdwPreferencesGroup">
         <child>
diff -Nru gnome-shell-48.3/js/gdm/loginDialog.js gnome-shell-48.4/js/gdm/loginDialog.js
--- gnome-shell-48.3/js/gdm/loginDialog.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/gdm/loginDialog.js	2025-08-03 11:53:34.000000000 +0100
@@ -182,7 +182,7 @@
         });
 
         this.child = this._box;
-        this._items = {};
+        this._items = new Map();
     }
 
     vfunc_key_focus_in() {
@@ -191,7 +191,7 @@
     }
 
     _moveFocusToItems() {
-        let hasItems = Object.keys(this._items).length > 0;
+        const hasItems = this._items.size > 0;
 
         if (!hasItems)
             return;
@@ -219,10 +219,8 @@
         else
             this._box.remove_style_pseudo_class('expanded');
 
-        for (let userName in this._items) {
-            let item = this._items[userName];
+        for (const item of this._items.values())
             item.sync_hover();
-        }
     }
 
     scrollToItem(item) {
@@ -248,7 +246,7 @@
     }
 
     getItemFromUserName(userName) {
-        let item = this._items[userName];
+        const item = this._items.get(userName);
 
         if (!item)
             return null;
@@ -257,7 +255,7 @@
     }
 
     containsUser(user) {
-        return this._items[user.get_user_name()] != null;
+        return this._items.has(user.get_user_name());
     }
 
     addUser(user) {
@@ -280,7 +278,7 @@
         let item = new UserListItem(user);
         this._box.add_child(item);
 
-        this._items[userName] = item;
+        this._items.set(userName, item);
 
         item.connect('activate', this._onItemActivated.bind(this));
 
@@ -301,17 +299,17 @@
         if (!userName)
             return;
 
-        let item = this._items[userName];
+        const item = this._items.get(userName);
 
         if (!item)
             return;
 
         item.destroy();
-        delete this._items[userName];
+        this._items.delete(userName);
     }
 
     numItems() {
-        return Object.keys(this._items).length;
+        return this._items.size;
     }
 });
 
@@ -351,7 +349,7 @@
 
         this._button.connect('clicked', () => this._menu.toggle());
 
-        this._items = {};
+        this._items = new Map();
         this._activeSessionId = null;
         this._populate();
     }
@@ -364,12 +362,11 @@
     }
 
     _updateOrnament() {
-        let itemIds = Object.keys(this._items);
-        for (let i = 0; i < itemIds.length; i++) {
-            if (itemIds[i] === this._activeSessionId)
-                this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.DOT);
+        for (const itemId of this._items.keys()) {
+            if (itemId === this._activeSessionId)
+                this._items.get(itemId).setOrnament(PopupMenu.Ornament.DOT);
             else
-                this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.NO_DOT);
+                this._items.get(itemId).setOrnament(PopupMenu.Ornament.NO_DOT);
         }
     }
 
@@ -400,7 +397,7 @@
             let id = ids[i];
             let item = new PopupMenu.PopupMenuItem(sessionName);
             this._menu.addMenuItem(item);
-            this._items[id] = item;
+            this._items.set(id, item);
 
             item.connect('activate', () => {
                 this.setActiveSession(id);
diff -Nru gnome-shell-48.3/js/misc/smartcardManager.js gnome-shell-48.4/js/misc/smartcardManager.js
--- gnome-shell-48.3/js/misc/smartcardManager.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/misc/smartcardManager.js	2025-08-03 11:53:34.000000000 +0100
@@ -72,7 +72,7 @@
     _addToken(token) {
         this._updateToken(token);
 
-        token.connect('g-properties-changed', (proxy, properties) => {
+        token.connectObject('g-properties-changed', (proxy, properties) => {
             const isInsertedChanged = !!properties.lookup_value('IsInserted', null);
             if (isInsertedChanged) {
                 this._updateToken(token);
@@ -82,7 +82,7 @@
                 else
                     this.emit('smartcard-removed', token);
             }
-        });
+        }, this);
 
         // Emit a smartcard-inserted at startup if it's already plugged in
         if (token.IsInserted)
@@ -100,7 +100,7 @@
         if (this._loginToken === token)
             this._loginToken = null;
 
-        token.disconnectAll();
+        token.disconnectObject(this);
     }
 
     hasInsertedTokens() {
diff -Nru gnome-shell-48.3/js/misc/util.js gnome-shell-48.4/js/misc/util.js
--- gnome-shell-48.3/js/misc/util.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/misc/util.js	2025-08-03 11:53:34.000000000 +0100
@@ -20,7 +20,7 @@
             '|' +
             'www\\d{0,3}[.]' +                    // www.
             '|' +
-            '[a-z0-9.\\-]+[.][a-z]{2,4}/' +       // foo.xx/
+            '([a-z0-9\\-]+[.])+[a-z]{2,4}/' +     // foo.xx/
         ')' +
         '(?:' +                                   // one or more:
             '[^\\s()<>]+' +                       // run of non-space non-()
diff -Nru gnome-shell-48.3/js/ui/barLevel.js gnome-shell-48.4/js/ui/barLevel.js
--- gnome-shell-48.3/js/ui/barLevel.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/ui/barLevel.js	2025-08-03 11:53:34.000000000 +0100
@@ -156,7 +156,6 @@
         cr.lineTo(endX, (height - this._barLevelHeight) / 2);
         cr.lineTo(xcArcEnd, (height - this._barLevelHeight) / 2);
         cr.setSourceColor(this._barLevelColor);
-        cr.fillPreserve();
         cr.fill();
 
         /* normal progress bar */
@@ -173,7 +172,6 @@
         cr.lineTo(xcArcStart, (height + this._barLevelHeight) / 2);
         if (this._value > 0)
             cr.setSourceColor(this._barLevelActiveColor);
-        cr.fillPreserve();
         cr.fill();
 
         /* overdrive progress barLevel */
@@ -188,7 +186,6 @@
             cr.lineTo(x, (height + this._barLevelHeight) / 2);
             cr.lineTo(x, (height - this._barLevelHeight) / 2);
             cr.setSourceColor(this._barLevelOverdriveColor);
-            cr.fillPreserve();
             cr.fill();
         }
 
@@ -208,7 +205,7 @@
                 cr.lineTo(Math.ceil(endX), (height - this._barLevelHeight) / 2);
             }
             cr.lineTo(endX, (height - this._barLevelHeight) / 2);
-            cr.fillPreserve();
+            cr.fill();
         }
 
         /* draw overdrive separator */
diff -Nru gnome-shell-48.3/js/ui/layout.js gnome-shell-48.4/js/ui/layout.js
--- gnome-shell-48.3/js/ui/layout.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/ui/layout.js	2025-08-03 11:53:34.000000000 +0100
@@ -992,22 +992,38 @@
         return ws.get_work_area_for_monitor(monitorIndex);
     }
 
+    _findIndexForRect(x, y, width, height) {
+        const rect = new Mtk.Rectangle({
+            x: Math.floor(x),
+            y: Math.floor(y),
+            width: Math.ceil(x + width) - Math.floor(x),
+            height: Math.ceil(y + height) - Math.floor(y),
+        });
+        return global.display.get_monitor_index_for_rect(rect);
+    }
+
     // This call guarantees that we return some monitor to simplify usage of it
     // In practice all tracked actors should be visible on some monitor anyway
     findIndexForActor(actor) {
         let [x, y] = actor.get_transformed_position();
         let [w, h] = actor.get_transformed_size();
-        const rect = new Mtk.Rectangle({x, y, width: w, height: h});
-        return global.display.get_monitor_index_for_rect(rect);
+        return this._findIndexForRect(x, y, w, h);
     }
 
-    findMonitorForActor(actor) {
-        let index = this.findIndexForActor(actor);
+    _findMonitorForIndex(index) {
         if (index >= 0 && index < this.monitors.length)
             return this.monitors[index];
         return null;
     }
 
+    findMonitorForActor(actor) {
+        return this._findMonitorForIndex(this.findIndexForActor(actor));
+    }
+
+    findMonitorForPoint(x, y) {
+        return this._findMonitorForIndex(this._findIndexForRect(x, y, 1, 1));
+    }
+
     _queueUpdateRegions() {
         if (!this._updateRegionIdle) {
             const laters = global.compositor.get_laters();
diff -Nru gnome-shell-48.3/js/ui/magnifier.js gnome-shell-48.4/js/ui/magnifier.js
--- gnome-shell-48.3/js/ui/magnifier.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/ui/magnifier.js	2025-08-03 11:53:34.000000000 +0100
@@ -5,6 +5,7 @@
 import Gio from 'gi://Gio';
 import GLib from 'gi://GLib';
 import GObject from 'gi://GObject';
+import Meta from 'gi://Meta';
 import Shell from 'gi://Shell';
 import St from 'gi://St';
 import * as Signals from '../misc/signals.js';
@@ -49,6 +50,8 @@
 }, class MouseSpriteContent extends GObject.Object {
     _init() {
         super._init();
+        this._scale = 1.0;
+        this._monitorScale = 1.0;
         this._texture = null;
     }
 
@@ -56,7 +59,10 @@
         if (!this._texture)
             return [false, 0, 0];
 
-        return [true, this._texture.get_width(), this._texture.get_height()];
+        let width = this._texture.get_width() / this._scale;
+        let height = this._texture.get_height() / this._scale;
+
+        return [true, width, height];
     }
 
     vfunc_paint_content(actor, node, _paintContext) {
@@ -72,6 +78,29 @@
         textureNode.add_rectangle(actor.get_content_box());
     }
 
+    _textureScale() {
+        if (!this._texture)
+            return 1;
+
+        /* This is a workaround to guess the sprite scale; while it works fine
+         * in normal scenarios, it's not guaranteed to work in all the cases,
+         * and so we should actually add an API to mutter that will allow us
+         * to know the real sprite texture scaling in order to adapt it to the
+         * wanted one. */
+        let avgSize = (this._texture.get_width() + this._texture.get_height()) / 2;
+        return Math.max(1, Math.floor(avgSize / Meta.prefs_get_cursor_size() + .1));
+    }
+
+    _recomputeScale() {
+        let scale = this._textureScale() / this._monitorScale;
+
+        if (this._scale !== scale) {
+            this._scale = scale;
+            return true;
+        }
+        return false;
+    }
+
     get texture() {
         return this._texture;
     }
@@ -86,7 +115,15 @@
 
         if (!oldTexture || !coglTexture ||
             oldTexture.get_width() !== coglTexture.get_width() ||
-            oldTexture.get_height() !== coglTexture.get_height())
+            oldTexture.get_height() !== coglTexture.get_height()) {
+            this._recomputeScale();
+            this.invalidate_size();
+        }
+    }
+
+    set monitorScale(monitorScale) {
+        this._monitorScale = monitorScale;
+        if (this._recomputeScale())
             this.invalidate_size();
     }
 });
@@ -119,6 +156,8 @@
         this._settingsInit(aZoomRegion);
         aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
 
+        this._updateContentScale();
+
         St.Settings.get().connect('notify::magnifier-active', () => {
             this.setActive(St.Settings.get().magnifier_active);
         });
@@ -127,6 +166,13 @@
         this._cursorUnfocusInhibited = false;
     }
 
+    _updateContentScale() {
+        let monitor = Main.layoutManager.findMonitorForPoint(this.xMouse,
+            this.yMouse);
+        this._mouseSprite.content.monitorScale = monitor
+            ? monitor.geometry_scale : 1;
+    }
+
     /**
      * showSystemCursor:
      * Show the system mouse pointer.
@@ -268,6 +314,8 @@
         this.xMouse = xMouse;
         this.yMouse = yMouse;
 
+        this._updateContentScale();
+
         let sysMouseOverAny = false;
         this._zoomRegions.forEach(zoomRegion => {
             if (zoomRegion.scrollToMousePos())
diff -Nru gnome-shell-48.3/js/ui/screenshot.js gnome-shell-48.4/js/ui/screenshot.js
--- gnome-shell-48.3/js/ui/screenshot.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/ui/screenshot.js	2025-08-03 11:53:34.000000000 +0100
@@ -1350,7 +1350,7 @@
             visible: false,
         }));
         this._captureButton.connect('clicked',
-            this._onCaptureButtonClicked.bind(this));
+            () => this._onCaptureButtonClicked().catch(logError));
         this._bottomRowContainer.add_child(this._captureButton);
 
         this._showPointerButtonContainer = new St.BoxLayout({
@@ -1872,9 +1872,13 @@
         return [x, y, w, h];
     }
 
-    _onCaptureButtonClicked() {
+    async _onCaptureButtonClicked() {
         if (this._shotButton.checked) {
-            this._saveScreenshot().catch(logError);
+            try {
+                await this._saveScreenshot();
+            } catch (e) {
+                logError(e);
+            }
             this.close();
         } else {
             // Screencast closes the UI on its own.
@@ -2142,7 +2146,7 @@
             symbol === Clutter.KEY_KP_Enter || symbol === Clutter.KEY_ISO_Enter ||
             ((event.get_state() & Clutter.ModifierType.CONTROL_MASK) &&
              (symbol === Clutter.KEY_c || symbol === Clutter.KEY_C))) {
-            this._onCaptureButtonClicked();
+            this._onCaptureButtonClicked().catch(logError);
             return Clutter.EVENT_STOP;
         }
 
diff -Nru gnome-shell-48.3/js/ui/slider.js gnome-shell-48.4/js/ui/slider.js
--- gnome-shell-48.3/js/ui/slider.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/ui/slider.js	2025-08-03 11:53:34.000000000 +0100
@@ -59,7 +59,7 @@
         let color = themeNode.get_foreground_color();
         cr.setSourceColor(color);
         cr.arc(handleX, handleY, this._handleRadius, 0, 2 * Math.PI);
-        cr.fillPreserve();
+        cr.fill();
         cr.$dispose();
     }
 
diff -Nru gnome-shell-48.3/js/ui/status/network.js gnome-shell-48.4/js/ui/status/network.js
--- gnome-shell-48.3/js/ui/status/network.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/ui/status/network.js	2025-08-03 11:53:34.000000000 +0100
@@ -116,7 +116,7 @@
     }
 
     _sortByName(one, two) {
-        return GLib.utf8_collate(one.name, two.name);
+        return GLib.utf8_collate(one.name ?? '', two.name ?? '');
     }
 
     _sortByMru(one, two) {
@@ -500,7 +500,8 @@
     }
 
     _syncConnections() {
-        const available = this._device.get_available_connections();
+        const available = this._device.get_available_connections().filter(
+            c => c.get_id() != null);
         const removed = [...this._connectionItems.keys()]
             .filter(conn => !available.includes(conn));
 
diff -Nru gnome-shell-48.3/js/ui/status/thunderbolt.js gnome-shell-48.4/js/ui/status/thunderbolt.js
--- gnome-shell-48.3/js/ui/status/thunderbolt.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/js/ui/status/thunderbolt.js	2025-08-03 11:53:34.000000000 +0100
@@ -139,7 +139,6 @@
     }
 
     close() {
-        this.disconnectAll();
         this._client = null;
     }
 
@@ -224,12 +223,16 @@
         this._indicator.icon_name = 'thunderbolt-symbolic';
 
         this._client = new Client();
-        this._client.connect('probing-changed', this._onProbing.bind(this));
+        this._client.connectObject(
+            'probing-changed', this._onProbing.bind(this), this);
 
         this._robot =  new AuthRobot(this._client);
+        this._robot.connectObject(
+            'enroll-device', this._onEnrollDevice.bind(this),
+            'enroll-failed', this._onEnrollFailed.bind(this),
+            this);
 
-        this._robot.connect('enroll-device', this._onEnrollDevice.bind(this));
-        this._robot.connect('enroll-failed', this._onEnrollFailed.bind(this));
+        this.connect('destroy', () => this._onDestroy());
 
         Main.sessionMode.connect('updated', this._sync.bind(this));
         this._sync();
diff -Nru gnome-shell-48.3/meson.build gnome-shell-48.4/meson.build
--- gnome-shell-48.3/meson.build	2025-08-19 23:30:39.000000000 +0100
+++ gnome-shell-48.4/meson.build	2025-08-19 23:30:39.000000000 +0100
@@ -1,5 +1,5 @@
 project('gnome-shell', 'c',
-  version: '48.3',
+  version: '48.4',
   meson_version: '>= 1.3.0',
   license: 'GPL-2.0-or-later',
 )
@@ -23,7 +23,7 @@
 gcr_req = '>= 3.90.0'
 gio_req = '>= 2.79.2'
 gi_req = '>= 1.49.1'
-gjs_req = '>= 1.73.1'
+gjs_req = '>= 1.81.2'
 gtk_req = '>= 4.0'
 mutter_req = '>= 48.0'
 polkit_req = '>= 0.100'
diff -Nru gnome-shell-48.3/NEWS gnome-shell-48.4/NEWS
--- gnome-shell-48.3/NEWS	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/NEWS	2025-08-03 11:53:34.000000000 +0100
@@ -1,3 +1,18 @@
+48.4
+====
+* Fix taking interactive screenshots via D-Bus [Florian; !3803]
+* Fix pointer scaling glitches in magnifier [Marco; !475]
+* Fix drawing glitch in sliders in RTL locales [Sebastian, Khalid; !3817]
+* Misc. bug fixes and cleanups [Marco, Sebastian, Florian, Mike, Jonas; !3785,
+  !3798, !3799, !3816, !3796, !3750]
+
+Contributors:
+  Khalid Abu Shawarib, Jonas Dreßler, Mike FABIAN, Sebastian Keller,
+  Florian Müllner, Marco Trevisan (Treviño)
+
+Translators:
+  Aefgh Threenine [th]
+
 48.3
 ====
 * Check all modifiers for modifier-scroll [Florian; !3725]
diff -Nru gnome-shell-48.3/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in gnome-shell-48.4/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in
--- gnome-shell-48.3/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/subprojects/extensions-app/data/metainfo/org.gnome.Extensions.metainfo.xml.in	2025-08-03 11:53:34.000000000 +0100
@@ -55,6 +55,7 @@
   </description>
 
   <releases>
+    <release version="48.4" date="2025-08-03"/>
     <release version="48.3" date="2025-06-29"/>
     <release version="48.2" date="2025-05-24"/>
     <release version="48.1" date="2025-04-12"/>
diff -Nru gnome-shell-48.3/subprojects/extensions-app/meson.build gnome-shell-48.4/subprojects/extensions-app/meson.build
--- gnome-shell-48.3/subprojects/extensions-app/meson.build	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/subprojects/extensions-app/meson.build	2025-08-03 11:53:34.000000000 +0100
@@ -1,5 +1,5 @@
 project('gnome-extensions-app',
-  version: '48.3',
+  version: '48.4',
   meson_version: '>= 0.58.0',
   license: 'GPL-2.0-or-later',
 )
diff -Nru gnome-shell-48.3/subprojects/extensions-app/subprojects/shew/meson.build gnome-shell-48.4/subprojects/extensions-app/subprojects/shew/meson.build
--- gnome-shell-48.3/subprojects/extensions-app/subprojects/shew/meson.build	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/subprojects/extensions-app/subprojects/shew/meson.build	2025-08-03 11:53:34.000000000 +0100
@@ -1,5 +1,5 @@
 project('shew', 'c',
-  version: '48.3',
+  version: '48.4',
   meson_version: '>= 0.58.0',
   license: 'LGPL-2.1-or-later',
 )
diff -Nru gnome-shell-48.3/subprojects/extensions-tool/meson.build gnome-shell-48.4/subprojects/extensions-tool/meson.build
--- gnome-shell-48.3/subprojects/extensions-tool/meson.build	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/subprojects/extensions-tool/meson.build	2025-08-03 11:53:34.000000000 +0100
@@ -1,5 +1,5 @@
 project('gnome-extensions-tool', 'c',
-  version: '48.3',
+  version: '48.4',
   meson_version: '>= 0.58.0',
   license: 'GPL-2.0-or-later',
 )
diff -Nru gnome-shell-48.3/subprojects/shew/meson.build gnome-shell-48.4/subprojects/shew/meson.build
--- gnome-shell-48.3/subprojects/shew/meson.build	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/subprojects/shew/meson.build	2025-08-03 11:53:34.000000000 +0100
@@ -1,5 +1,5 @@
 project('shew', 'c',
-  version: '48.3',
+  version: '48.4',
   meson_version: '>= 0.58.0',
   license: 'LGPL-2.1-or-later',
 )
diff -Nru gnome-shell-48.3/tests/unit/url.js gnome-shell-48.4/tests/unit/url.js
--- gnome-shell-48.3/tests/unit/url.js	2025-06-29 11:35:41.000000000 +0100
+++ gnome-shell-48.4/tests/unit/url.js	2025-08-03 11:53:34.000000000 +0100
@@ -54,6 +54,14 @@
             output: [{url: 'www.gnome.org/scheme-less', pos: 10}],
         },
         {
+            input: 'This is a gnome.org/scheme-less test without www.',
+            output: [{url: 'gnome.org/scheme-less', pos: 10}],
+        },
+        {
+            input: 'This is a status.gnome.org/scheme-less test.',
+            output: [{url: 'status.gnome.org/scheme-less', pos: 10}],
+        },
+        {
             input: 'This is a http://www.gnome.org:99/port test.',
             output: [{url: 'http://www.gnome.org:99/port', pos: 10}],
         },
@@ -104,6 +112,18 @@
             input: 'This is surely@not.a/url test.',
             output: [],
         },
+        {
+            input: 'This is not..aa/url test.',
+            output: [],
+        },
+        {
+            input: 'This is ..not/a-url test.',
+            output: [],
+        },
+        {
+            input: 'This is .absolutely.not/a-url test.',
+            output: [],
+        },
     ];
 
     for (const param of urlParameters) {
