chore: P0 skeleton
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>
This commit is contained in:
28
extension/esbuild.config.js
Normal file
28
extension/esbuild.config.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import esbuild from 'esbuild';
|
||||
|
||||
const watch = process.argv.includes('--watch');
|
||||
|
||||
const opts = {
|
||||
entryPoints: ['src/extension.ts'],
|
||||
bundle: true,
|
||||
outfile: 'dist/extension.js',
|
||||
format: 'esm',
|
||||
target: 'firefox128',
|
||||
platform: 'neutral',
|
||||
external: [
|
||||
'gi://*',
|
||||
'resource:///*',
|
||||
'system',
|
||||
'cairo',
|
||||
'gettext',
|
||||
],
|
||||
sourcemap: 'inline',
|
||||
logLevel: 'info',
|
||||
};
|
||||
|
||||
if (watch) {
|
||||
const ctx = await esbuild.context(opts);
|
||||
await ctx.watch();
|
||||
} else {
|
||||
await esbuild.build(opts);
|
||||
}
|
||||
9
extension/metadata.json
Normal file
9
extension/metadata.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"uuid": "dashward@hangman-lab.top",
|
||||
"name": "Dashward",
|
||||
"description": "A dedicated rightmost workspace for custom widgets, in the spirit of macOS Dashboard.",
|
||||
"shell-version": ["48"],
|
||||
"url": "https://git.hangman-lab.top/hzhang/Dashward",
|
||||
"version": 1,
|
||||
"settings-schema": "org.gnome.shell.extensions.dashward"
|
||||
}
|
||||
16
extension/package.json
Normal file
16
extension/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@dashward/extension",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "GNOME Shell extension that creates and guards the Dashward workspace.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node esbuild.config.js",
|
||||
"dev": "node esbuild.config.js --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@girs/gnome-shell": "48.0.0-next.1",
|
||||
"@girs/meta-16": "16.0.0-next.1",
|
||||
"@girs/gtk-3.0": "3.24.0-next.1"
|
||||
}
|
||||
}
|
||||
9
extension/src/container-supervisor.ts
Normal file
9
extension/src/container-supervisor.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// Stub: see design §7 — web container process.
|
||||
// Spawn container via Gio.Subprocess; match its window by Wayland app_id;
|
||||
// pin to dashboard workspace; restart with exponential backoff on crash.
|
||||
|
||||
export class ContainerSupervisor {
|
||||
dispose(): void {
|
||||
// graceful shutdown via DBus, then SIGTERM after 2s
|
||||
}
|
||||
}
|
||||
8
extension/src/dbus-service.ts
Normal file
8
extension/src/dbus-service.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Stub: see design §8.1 — Shell ↔ Container DBus.
|
||||
// Bus: top.hangmanlab.Dashward.Shell, path /top/hangmanlab/Dashward.
|
||||
|
||||
export class DBusService {
|
||||
dispose(): void {
|
||||
// unown bus name, disconnect handlers
|
||||
}
|
||||
}
|
||||
9
extension/src/entry-ux.ts
Normal file
9
extension/src/entry-ux.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// Stub: see design §13 — entry/exit UX.
|
||||
// Three paths in: 4-finger swipe right past last workspace, Super+Grave,
|
||||
// panel button. All converge on openDashboard().
|
||||
|
||||
export class EntryUX {
|
||||
dispose(): void {
|
||||
// remove gesture hook, keybinding, panel indicator
|
||||
}
|
||||
}
|
||||
40
extension/src/extension.ts
Normal file
40
extension/src/extension.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
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';
|
||||
|
||||
export default class DashwardExtension extends Extension {
|
||||
private warden?: WorkspaceWarden;
|
||||
private guard?: WindowGuard;
|
||||
private container?: ContainerSupervisor;
|
||||
private entry?: EntryUX;
|
||||
private dbus?: DBusService;
|
||||
|
||||
enable(): void {
|
||||
log('[Dashward] enable: P0 skeleton — components not wired yet');
|
||||
this.warden = new WorkspaceWarden();
|
||||
this.guard = new WindowGuard();
|
||||
this.container = new ContainerSupervisor();
|
||||
this.entry = new EntryUX();
|
||||
this.dbus = new DBusService();
|
||||
}
|
||||
|
||||
disable(): void {
|
||||
log('[Dashward] 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;
|
||||
}
|
||||
}
|
||||
|
||||
declare function log(msg: string): void;
|
||||
9
extension/src/window-guard.ts
Normal file
9
extension/src/window-guard.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// Stub: see design §6 — window-defense rules.
|
||||
// Layer 1: window-added bounce-back. Layer 2: overview drop refusal.
|
||||
// Layer 3: hide from switcher strip. Layer 4: workspace switcher clamp.
|
||||
|
||||
export class WindowGuard {
|
||||
dispose(): void {
|
||||
// disconnect all signals and restore patched prototypes
|
||||
}
|
||||
}
|
||||
9
extension/src/workspace-warden.ts
Normal file
9
extension/src/workspace-warden.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// Stub: see design §5 — workspace lifecycle.
|
||||
// Responsibilities: snapshot gsettings, append the dashboard workspace, hold
|
||||
// position invariant on every workspace mutation, restore on disable.
|
||||
|
||||
export class WorkspaceWarden {
|
||||
dispose(): void {
|
||||
// restore snapshotted settings, remove dashboard workspace
|
||||
}
|
||||
}
|
||||
10
extension/tsconfig.json
Normal file
10
extension/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"noEmit": true,
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
13
extension/widgets-builtin/clock/package.json
Normal file
13
extension/widgets-builtin/clock/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@dashward/widget-clock",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "esbuild src/index.ts --bundle --format=esm --outfile=dist/index.js --external:@dashward/widget-sdk",
|
||||
"dev": "esbuild src/index.ts --bundle --format=esm --outfile=dist/index.js --external:@dashward/widget-sdk --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dashward/widget-sdk": "workspace:*"
|
||||
}
|
||||
}
|
||||
41
extension/widgets-builtin/clock/src/index.ts
Normal file
41
extension/widgets-builtin/clock/src/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { defineWidget } from '@dashward/widget-sdk';
|
||||
|
||||
interface ClockConfig {
|
||||
hour12: boolean;
|
||||
showSeconds: boolean;
|
||||
}
|
||||
|
||||
export default defineWidget<ClockConfig>({
|
||||
id: 'clock',
|
||||
defaultConfig: { hour12: false, showSeconds: true },
|
||||
|
||||
mount(host, { config, lifecycle, system }) {
|
||||
const root = host.element.attachShadow({ mode: 'open' });
|
||||
root.innerHTML = `
|
||||
<style>
|
||||
:host { display: grid; place-items: center; }
|
||||
.time { font: 600 clamp(2rem, 8vw, 5rem) ui-sans-serif, system-ui; }
|
||||
:host([data-theme="dark"]) .time { color: #f0f0f0; }
|
||||
</style>
|
||||
<div class="time"></div>
|
||||
`;
|
||||
const el = root.querySelector('.time') as HTMLDivElement;
|
||||
|
||||
const render = () => {
|
||||
const cfg = config.get();
|
||||
el.textContent = new Date().toLocaleTimeString(undefined, {
|
||||
hour12: cfg.hour12,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: cfg.showSeconds ? '2-digit' : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
const timer = setInterval(render, 1000);
|
||||
render();
|
||||
lifecycle.onUnmount(() => clearInterval(timer));
|
||||
|
||||
config.onChange(render);
|
||||
system.onThemeChange(t => (host.element.dataset.theme = t));
|
||||
},
|
||||
});
|
||||
10
extension/widgets-builtin/clock/widget.json
Normal file
10
extension/widgets-builtin/clock/widget.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "clock",
|
||||
"name": "Clock",
|
||||
"version": "0.0.0",
|
||||
"entry": "dist/index.js",
|
||||
"icon": "icon.svg",
|
||||
"size": { "w": 2, "h": 2, "minW": 1, "minH": 1, "maxW": 4, "maxH": 4 },
|
||||
"permissions": ["timer"],
|
||||
"configSchema": { "hour12": "boolean", "showSeconds": "boolean" }
|
||||
}
|
||||
Reference in New Issue
Block a user