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>
Replace the workspace-warden.ts stub with a real implementation matching
design §5:
- On enable: snapshot org.gnome.mutter::dynamic-workspaces and
org.gnome.desktop.wm.preferences::num-workspaces, persist to
~/.local/state/dashward/workspace-warden.json, then disable dynamic
mode and append a new workspace as the dashboard slot. num-workspaces
is updated to match the new count so external observers stay in sync.
- Position invariant: connect to workspace-manager's workspaces-reordered,
workspace-added, and workspace-removed signals; whenever the dashboard
is no longer last, reorder it back. If something external removes the
dashboard, append a replacement.
- Defensive guards: external changes to num-workspaces or
dynamic-workspaces are clamped back; a `suppressGuard` flag avoids
feedback loops between our own writes and our own signal handlers.
- On disable: remove the dashboard workspace, restore both gsettings,
delete the state file.
Supporting infrastructure:
- util/logger.ts: console.log/warn/error wrappers with [Dashward] prefix.
- util/state-store.ts: load/save/clear JSON state under XDG_STATE_HOME.
- types/globals.d.ts: minimal Shell.Global declaration covering
workspace_manager / display / get_current_time().
Build chain fixes uncovered while wiring P1:
- Replace placeholder @girs/* versions in extension/ and container/
package.json with real published versions from npm (gnome-shell@50.0.0,
meta-16@16.0.0-4.0.0, etc.). Add the required @girs/gio/glib/gobject
packages so resolution actually succeeds.
- Set tsconfig `types` arrays to include each girs `/ambient` entry so
the `gi://*` and `resource:///*` module specifiers resolve.
- Add `override` modifiers on Extension.enable/disable (required under
the base tsconfig's noImplicitOverride).
- Fix workspace iteration to use get_workspace_by_index in a loop
instead of the non-existent get_workspaces() — Meta 16 doesn't expose
the bulk getter.
Verified: `pnpm -r build` and `pnpm -r exec tsc --noEmit` are both clean.
Functional verification against a real GNOME session is pending P2 — the
extension cannot be loaded yet because we haven't packaged it for the
test VM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bootstrap the Dashward repo per arch/UBUNTU-DASHBOARD-SPACE.md:
- pnpm-workspaces monorepo (sdk, extension, container, widgets-builtin/*)
- GNOME extension stub (metadata.json, src/*.ts placeholders for warden,
guard, supervisor, entry UX, DBus service)
- WebKit container stub (GJS main + page-side runtime + dashboard.html)
- TypeScript widget SDK (defineWidget + types)
- Builtin clock widget as the first SDK consumer example
- DBus interface XML (proto/shell.iface.xml) and shared types
- esbuild configs for extension and container; tsc for SDK
- Design doc copied in at repo root for discoverability
No functional logic yet -- all components are placeholders that compose
in extension.ts so the build chain can be exercised. P1 (workspace
warden) starts next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>