NexStage brings the macOS Stage Manager experience to your GNOME desktop. A sidebar of live window thumbnails sits at the screen edge, showing your open apps with real-time content previews. Click to focus, scroll to navigate, drag to reorder, right-click for options. Layouts: Arc carousel (signature curved look) or Vertical list. Position: left, right, or bottom. Modes: Raise-only (focus without minimizing) or Minimize-others (single-window stage, classic Stage Manager feel). Live thumbnails with real-time content. Active app highlighted, inactive cards lean for depth. Optional badges (window count per app), tooltips, close buttons. Multi-monitor: per-display or follow-focus. Reserve space (maximized windows avoid sidebar). Persistent or auto-hide. 4-tab Adw preferences window: Layout, Behavior, Animation, Shortcuts. Every detail configurable. Wayland & X11. Pure GJS/Clutter. No Electron, no npm. GPL-3.0+.
Note: Binary files aren't shown on the web site. To see all files, please download the extension zipfile.
EGO-C49-003 error
extensions targeting GNOME 49 must not call maximize or unmaximize with Meta.MaximizeFlags
This extension explicitly targets GNOME Shell 49 but still passes `Meta.MaximizeFlags` to `maximize()` or `unmaximize()`.
extension.js:551
win.unmaximize?.(Meta.MaximizeFlags.BOTH)
EGO-C49-004 error
extensions targeting GNOME 49 must not call Meta.Window.get_maximized
This extension explicitly targets GNOME Shell 49 but still calls removed `Meta.Window.get_maximized()`.
extension.js:522
win.get_maximized?.()
EGO-L-002 warning
objects created by extension should be destroyed in disable()
Objects assigned in `enable()` are missing matching `.destroy()` calls in `disable()` or its helper methods.
extension.js:1825
this._tooltip = tooltip
extension.js:1232
container
EGO-L-003 warning
signals connected by extension should be disconnected in disable()
Signals assigned in `enable()` are missing matching disconnect calls in `disable()` or its helper methods.
extension.js:2034
this._contextMenuClickId = clickId
extension.js:459
this._edge.connect('enter-event', () => { if (!this._isVisible) this._showPanel(); })
extension.js:460
this._panel.connect('enter-event', () => { if (!this._dragging) this._cancelHide(); })
extension.js:461
this._panel.connect('leave-event', () => { if (this._isVisible && !this._dragging) this._startHide(); })
extension.js:463
this._panel.connect('scroll-event', (_a, event) => {
if (this._dragging) return Clutter.EVENT_STOP;
if (this._layoutMode === 'vertical') {
const dir = event.get_scroll_direction();
if (dir === Clutter.ScrollDirection.DOWN) this._scrollV
EGO-L-004 warning
main loop sources should be removed in disable()
Main loop sources assigned in `enable()` are missing matching removals in `disable()` or its helper methods.
extension.js:105
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 130, () => {
this._refresh();
this._containers.forEach(c => {
c.opacity = 0;
c.ease({ opacity: 255, duration: 200, mode: Clutter.AnimationMode.EASE_OUT_QUAD
extension.js:1217
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:149
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 800, () => {
this._checkPersistence();
return GLib.SOURCE_REMOVE;
})
extension.js:1549
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:1717
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:1749
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:192
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 80, () => {
if (this._containers.length > prevCount && this._isVisible) {
const newC = this._containers[this._containers.length - 1];
if (newC) {
newC.set_scale(0.5, 0.
extension.js:1939
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 150, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:1948
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:1962
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 150, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:1966
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { this._refresh(); return GLib.SOURCE_REMOVE; })
extension.js:2065
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 150, () => {
const focused = global.display.get_focus_window();
if (focused) {
const app = tracker.get_window_app(focused);
if (app) {
const fid = app.get_id();
extension.js:2234
GLib.timeout_add(GLib.PRIORITY_DEFAULT, Math.round((40 + i * 50) * this._animSpeed), () => {
c.ease({
x: origX, y: origY,
opacity: 255,
scale_x: c._isActive ? 1.0 : this._inactiveScale,
scale_
extension.js:2263
GLib.timeout_add(GLib.PRIORITY_DEFAULT, (total - 1 - i) * 25, () => {
c.ease({
x: exitX, y: exitY,
opacity: 0,
scale_x: 0.8, scale_y: 0.8,
duration: 200,
mode: Clutter.Anim
extension.js:2276
GLib.timeout_add(GLib.PRIORITY_DEFAULT, Math.max(80, total * 25), () => {
this._panel.ease({ x: geo.hidX, y: geo.hidY, duration: 200, mode: Clutter.AnimationMode.EASE_IN_QUAD });
return GLib.SOURCE_REMOVE;
})
extension.js:235
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => {
this._refresh();
return GLib.SOURCE_REMOVE;
})
extension.js:486
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
this._adjustWindowForReserve(win);
return GLib.SOURCE_REMOVE;
})
extension.js:507
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => {
this._adjustWindowForReserve(win);
return GLib.SOURCE_REMOVE;
})
extension.js:556
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => {
win.move_resize_frame(false, x, y, w, h);
this._adjustingReserve = false;
return GLib.SOURCE_REMOVE;
})
extension.js:793
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => {
this._refresh();
return GLib.SOURCE_REMOVE;
})
extension.js:90
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => {
this._checkPersistence();
return GLib.SOURCE_REMOVE;
})
EGO-L-007 warning
main loop sources should be removed before being recreated
Main loop sources should be removed before creating a new source on the same field.
extension.js:839
this._physicsId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 16, () => {
this._offset += this._velocity;
this._velocity *= SCROLL_FRICTION;
const max = Math.max(0, this._groups.length - 1);
if (this._offset < 0) { this._offset = 0; this._vel
| Version | Status |
|---|---|
| 1.0.0 (1) | Unreviewed |