Replace the WindowGuard stub with the Layer 1 of design §6: any non- whitelisted window that joins the dashboard workspace is immediately moved back to the workspace it came from. This single hook catches all real paths into dashboard -- wmctrl/xdotool moves, super+shift+→ keybind, overview drag-drop, programmatic change_workspace, and initial window map with _NET_WM_DESKTOP pointing at dashboard. Implementation: - Per-window previousWs tracking via WeakMap<Meta.Window, number>, updated on each workspace-changed signal whenever the new workspace isn't the dashboard. At enable, walk all existing windows and pre-populate from their current workspace. - Bounce target resolution: prefer the recorded workspace; fall back to (dashboardIndex - 1), which always exists because WorkspaceWarden guarantees dashboard isn't workspace 0. - Whitelist Set<Meta.Window> with allow()/disallow() so P3's ContainerSupervisor can pin the WebKit container without it being immediately bounced. - All signal connections use connectObject/disconnectObject with `this` as the tracker, so dispose() unwinds everything in O(1) bookkeeping. - Override-redirect windows (menus, tooltips) are skipped. Layers 2 (overview drop refusal), 3 (hide dashboard from switcher strip), and 4 (Super+Page_Up/Down clamp) are documented in the file header and deferred to P8/P9 -- they're UX polish that prevents the *attempt*, while Layer 1 alone meets the P2 acceptance criteria of "every entry vector bounces". extension.ts wires WindowGuard up after WorkspaceWarden, in its own try/catch so a guard init failure leaves the warden disposable. Build-chain tweak: @girs/gnome-shell augments connectObject onto GObject.Object via dist/extensions/global.d.ts, but that file isn't pulled in by `@girs/gnome-shell/ambient`. Adding `@girs/gnome-shell/extensions/global` to tsconfig "types" loads the augmentation explicitly. Verified: `pnpm -r build` and `pnpm -r exec tsc --noEmit` are clean; extension bundle is 44.8 KB. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59 lines
1.8 KiB
TypeScript
59 lines
1.8 KiB
TypeScript
import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
|
|
|
|
import { WorkspaceWarden } from './workspace-warden.js';
|
|
import { WindowGuard } from './window-guard.js';
|
|
import { ContainerSupervisor } from './container-supervisor.js';
|
|
import { EntryUX } from './entry-ux.js';
|
|
import { DBusService } from './dbus-service.js';
|
|
import { log, error } from './util/logger.js';
|
|
|
|
export default class DashwardExtension extends Extension {
|
|
private warden?: WorkspaceWarden;
|
|
private guard?: WindowGuard;
|
|
private container?: ContainerSupervisor;
|
|
private entry?: EntryUX;
|
|
private dbus?: DBusService;
|
|
|
|
override enable(): void {
|
|
log(`enable: ${this.metadata.uuid} v${this.metadata.version}`);
|
|
|
|
try {
|
|
this.warden = new WorkspaceWarden();
|
|
} catch (e) {
|
|
error(`WorkspaceWarden init failed: ${String(e)}`);
|
|
// Don't proceed if the warden didn't come up — everything else
|
|
// depends on the dashboard workspace existing.
|
|
return;
|
|
}
|
|
|
|
try {
|
|
this.guard = new WindowGuard(this.warden);
|
|
} catch (e) {
|
|
error(`WindowGuard init failed: ${String(e)}`);
|
|
// Dashboard workspace exists but is undefended. Continue so disable()
|
|
// can still tear down the warden cleanly.
|
|
}
|
|
|
|
// P3+ components still placeholders, wired in their own phases.
|
|
// this.container = new ContainerSupervisor(this.warden, this.guard);
|
|
// this.entry = new EntryUX();
|
|
// this.dbus = new DBusService();
|
|
}
|
|
|
|
override disable(): void {
|
|
log('disable');
|
|
|
|
this.dbus?.dispose();
|
|
this.entry?.dispose();
|
|
this.container?.dispose();
|
|
this.guard?.dispose();
|
|
this.warden?.dispose();
|
|
|
|
this.dbus = undefined;
|
|
this.entry = undefined;
|
|
this.container = undefined;
|
|
this.guard = undefined;
|
|
this.warden = undefined;
|
|
}
|
|
}
|