2 Commits

Author SHA1 Message Date
e8670ac590 feat: P5 — first widget on screen (clock via SDK)
The dashboard finally has live content. `runtime.ts` now imports
`defineWidget` from `@dashward/widget-sdk` (added as a workspace
dependency of the container), defines a tabular-numerals clock widget
inline, and mounts it onto the 12-column grid via a generic
`mountWidget` + `createContext` pair that produces the SDK's
config/system/lifecycle/shell APIs.

container/runtime/runtime.ts
  - mountWidget(def, parent, instanceId, size): creates a grid card,
    applies grid-column/row span, builds the widget context, calls
    def.mount; wraps in try/catch so a mount failure visibly degrades
    the card (red border + error label) instead of breaking the page.
  - createContext: localStorage-backed config (persists per
    instanceId), theme reads dataset.theme and subscribes to the
    dashward:ThemeChanged CustomEvent (DBus signal forwarded from
    shell), shell.call delegates to window.__dashShell__. onMount
    fires via microtask; onUnmount handlers stored; visibility is a
    no-op until P7 (edit mode / workspace tracking).
  - clockDef: defineWidget<ClockConfig>; shadow DOM root, large
    tabular-numerals time string, re-renders on tick + config.onChange.

container/runtime/styles.css
  - Real card style: half-transparent backdrop-filter blur with a
    light/dark variant, rounded 18px corners, subtle border and shadow.
  - .widget-error fallback for failed mount.
  - grid-auto-rows: minmax(120px, auto) so widgets have a sensible
    minimum row height.

container/package.json
  - `@dashward/widget-sdk` is now a runtime dependency. esbuild
    bundles it into runtime.js.

Verified on ubuntu2504-test VM: clock card renders top-left at 4-wide
× 2-tall, ticks every second, follows system theme live via the P4
bridge (light → glass-white card, dark → glass-black card).

Layout persistence, edit mode, dynamic disk loading of widget bundles,
and iframe crash isolation per design §11 / §14 land in P6+.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 01:00:49 +01:00
7b5539cf75 feat(extension): P1 — WorkspaceWarden lifecycle + position invariant
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>
2026-05-22 23:05:24 +01:00