@domicile-desktop/sdk 0.0.0-alpha-e612f0697e41 → 0.0.0-alpha-ab2d2f0e2a24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,29 +16,34 @@ import type { Shell as ShellModule } from "@domicile-desktop/sdk/shell";
16
16
 
17
17
  export const Shell: ShellModule = (root, domicile) => {
18
18
  const show = () => {
19
- // Render `domicile.windows` as `<app app-id="…">` elements into `root`.
19
+ // Render `domicile.windows.list` as `<app app-id="…">` elements into
20
+ // `root`.
20
21
  };
21
22
  show();
22
- domicile.addEventListener("windowschanged", show);
23
+ domicile.windows.on("change", show);
23
24
  };
24
25
  ```
25
26
 
26
27
  - Domicile calls `Shell(root, domicile)` once. `domicile` is the only copy of
27
28
  the desktop; there is no global.
29
+ - `domicile` is a `Domicile`: one typed object with a namespace per subject
30
+ (`windows`, `displays`, `keyboard`, `session`, `portals`, `files`, …).
28
31
  - Nothing to await. The first call on `domicile` binds the channel.
29
- - State is readonly attributes (`windows`, `browserWindows`, `displays`,
30
- `theme`, …), each with a bare `<name>changed` event. Read, then listen.
31
- - Moments (`shortcut`, `focusrequested`, …) are events. The engine holds each
32
- type until its first listener exists.
33
- - `searchFiles` returns a promise. A newer call rejects the older with an
34
- `AbortError`.
32
+ - State is readonly attributes (`windows.list`, `displays.list`,
33
+ `theme.current`, …), `undefined` until the compositor says. Each
34
+ namespace's `on(type, listener)` says when they change and returns a
35
+ function that stops listening. Read, then listen.
36
+ - Moments (`keyboard.on("shortcut")`, `windows.on("focusrequest")`, …) are
37
+ events too. The engine holds each until its first listener exists.
38
+ - A call the compositor answers resolves a `Result`. A newer `files.search`
39
+ rejects the older with an `AbortError`.
35
40
  - Only Domicile calls a shell. Develop against the real desktop with
36
41
  `packages/dev-shell/bin/dev-shell.sh <shell>`.
37
42
  - Render `<app app-id="…">` and `<webview window="…">` as normal DOM. CSS
38
43
  (rounding, blur, transforms, z-index) applies to the live surface.
39
44
  - The engine sends the pointer, wheel and keys over an `<app>` to its client,
40
- through every CSS transform. `domicile.focusApp(appId)` and
41
- `domicile.focusChrome()` move the keyboard without a click.
45
+ through every CSS transform. `domicile.windows.focus(appId)` and
46
+ `domicile.windows.focusShell()` move the keyboard without a click.
42
47
  - Both tags are the engine's built-in elements. In React, bind their events on
43
48
  a ref; React does not bind `on…` props for unknown events.
44
49
  - Errors follow [ERRORS.md](/docs/guidelines/ERRORS.md): bugs throw, absence
@@ -51,22 +56,21 @@ See [docs/ELEMENTS.md](docs/ELEMENTS.md) for `<app>` input and focus, and the
51
56
 
52
57
  | Module | What it is |
53
58
  | --- | --- |
54
- | `./desktop/desktop` | `desktop(domicile)`: the desktop as one typed object, a namespace per subject (`windows`, `browser`, `keyboard`, `files`, `portals`, …). State is attributes with a `change` event through `on`; a call that can fail resolves a `Result`. Each namespace's types are in `./desktop/<namespace>`. |
55
- | `./desktop/app-desktop` | `appDesktop(host)`: an app module's `files`, `processes` and `dbus`. |
56
- | `./domicile-host` | `DomicileHost`: the type of the desktop `Shell` is handed. Generated from the engine's IDL; see [Generated types](#generated-types). |
59
+ | `./desktop/desktop` | `Domicile`, the type of the desktop `Shell` is handed: one typed object, a namespace per subject (`windows`, `browser`, `keyboard`, `files`, `portals`, …). State is attributes with events through `on`; a call that can fail resolves a `Result`. Each namespace's types are in `./desktop/<namespace>`. `desktop(host)` builds one over a host, as the engine does; see [The desktop layer](#the-desktop-layer). |
60
+ | `./desktop/app-desktop` | `AppDomicile`, the desktop an app module's `main` is handed: its `files`, `processes` and `dbus`. |
61
+ | `./domicile-host` | `DomicileHost`: the engine's host under the desktop. Generated from the engine's IDL; see [Generated types](#generated-types). |
57
62
  | `./config` | Types for the config file's sections, for a TypeScript config module. Generated; see [Generated types](#generated-types). `./config.schema.json` is the JSON Schema they come from. |
58
- | `./shell` | `Shell`: the export a shell module must provide. Domicile calls it once with the element to draw in. |
59
- | `./app` | `AppMain` and `DomicileAppHost`: an app module's `main` and the host it gets. Pass the host to `system()`. See [APP-MODULES.md](../../docs/architecture/APP-MODULES.md). |
63
+ | `./shell` | `Shell`: the export a shell module must provide. Domicile calls it once with the element to draw in and the `Domicile`. |
64
+ | `./app` | `AppMain`: an app module's `main`, which Domicile calls with the element to draw in and the `AppDomicile`. See [APP-MODULES.md](../../docs/architecture/APP-MODULES.md). |
60
65
  | `./app-element`, `./webview-element` | Types and event names for `<app>` and `<webview>`. |
61
66
  | `./bind-keys` | `bindKeys`: grab a shell's own chords by name and handle them by mode. `./key-action` and `./own-keybindings` are its parts. |
62
- | `./fake-host` | `FakeDomicileHost`: a desktop for a shell's tests. |
67
+ | `./fake-host` | `FakeDomicileHost`: a desktop for a shell's tests. Hand its `domicile` to the shell. |
63
68
  | `./file-preview` | `previewFile(domicile, path)`: a launcher's preview of a path, read through `domicile.files`: a directory's entries, a song's tags and cover, or the start of a text file. |
64
69
  | `./dbusmenu` | `watchMenu(domicile, { bus, path })`: a tray item's `com.canonical.dbusmenu` menu, read again on each change, with `click` and `aboutToShow`. Hidden entries are left out; labels lose their mnemonic underscore, and `mnemonic` says where it was. `icon-data` comes as a PNG `data:` URL. See [SYSTEM-TRAY.md](/docs/architecture/SYSTEM-TRAY.md). |
65
- | `./system` | `system(domicile)`: files, watches, processes and D-Bus. Calls resolve a `Result`; the compositor refuses most while the desktop is locked. See [SHELL-SYSTEM-ACCESS.md](/docs/SHELL-SYSTEM-ACCESS.md). |
66
- | `./portal` | `watchPortalRequests` and `answerPortalRequest`: application dialogs from `xdg-desktop-portal`, parsed by kind. `watchCapturing` and `stopCapturing`: running sessions that record the screen or control or capture input. `<PortalDialogs />` in component-library draws them. See [PORTALS.md](/docs/PORTALS.md). |
70
+ | `./system` | The types of the compositor's system calls: `SystemError`, `FileType`, `Listening`, … The calls are the desktop's `files`, `processes`, `dbus` and the rest; the compositor refuses most while the desktop is locked. See [SHELL-SYSTEM-ACCESS.md](/docs/SHELL-SYSTEM-ACCESS.md). |
71
+ | `./portal` | The types of application dialogs from `xdg-desktop-portal` (`PortalRequest`, `PortalAnswer`, …) and of running sessions that record the screen or control or capture input (`Capturing`). The desktop's `portals` lists and answers them; `<PortalDialogs />` in component-library draws them. See [PORTALS.md](/docs/PORTALS.md). |
67
72
  | `./global-shortcuts` | `fireGlobalShortcuts`: grab the chords applications hold through the GlobalShortcuts portal and report each press and release. `<PortalDialogs />` calls it. |
68
73
  | `./options` | `defineOptions` and `option`: the options a shell declares as its `options` export, which the Settings app edits. `watchShellOptions` reads the config's values of them, live. See [WRITING-A-SHELL.md](/docs/WRITING-A-SHELL.md#options). |
69
- | `./appearance` | `watchAppearance`: the config's accent color, contrast and reduced motion, which the Settings portal also serves applications. See [SHELL-CONFIG.md](/docs/SHELL-CONFIG.md#theme). |
70
74
  | `./icon-theme` | `readIconTheme(domicile)`: the config's `theme.icon_theme`, read from the compositor's Settings portal, or `None` for `hicolor` only. `watchIconTheme` reads it and follows `SettingChanged`. `@domicile-desktop/system-apps/app-icons` takes it. |
71
75
  | `./extension`, `./tray`, `./notification`, `./theme`, `./display-transform` | Data types and Zod schemas for what the desktop holds. |
72
76
 
@@ -78,19 +82,40 @@ speaks it.
78
82
 
79
83
  ## Design
80
84
 
81
- - **State is attributes with a bare `<name>changed` event**, over events that
82
- carry the value. The attribute is the one source of truth, and a late
83
- listener misses nothing (`navigator.onLine` with `online` works the same).
84
- - **The engine holds moments** (`focusrequested`, `shortcut`) until the first
85
+ - **One typed object, a namespace per subject.** If the desktop has it, it is
86
+ a member of `domicile`. SDK functions are for policy and composition
87
+ (`bindKeys`, `watchShellOptions`), not for reaching the desktop.
88
+ - **State is attributes with a bare change event**, over events that carry
89
+ the value. The attribute is the one source of truth, and a late listener
90
+ misses nothing (`navigator.onLine` with `online` works the same).
91
+ - **The engine holds moments** (`focusrequest`, `shortcut`) until the first
85
92
  listener of their type. Only the engine knows whether a listener exists.
86
- - **Asks return promises.** A newer `searchFiles` rejects the older.
93
+ - **Asks return promises.** A newer `files.search` rejects the older.
87
94
  - **The desktop is handed to `Shell`**, not found on a global. There is no
88
95
  `navigator.domicile` or `window.domicile`; the shell's copy is the only one.
89
96
  - **The engine reports the desktop's size and density**, and `<app>` routes
90
97
  its own input ([docs/ELEMENTS.md](docs/ELEMENTS.md)). Chords are resolved by
91
98
  the engine ([KEYBINDINGS.md](/docs/architecture/KEYBINDINGS.md)).
92
- - **The SDK is types and pure helpers.** The host types are generated from the
93
- IDL, below.
99
+ - **The SDK is the desktop's types, the layer that builds it, and pure
100
+ helpers.** The host types under it are generated from the IDL, below.
101
+
102
+ ## The desktop layer
103
+
104
+ The engine hands a shell the `Domicile` that `src/desktop/desktop.ts`'s
105
+ `desktop(host)` builds, and an app the `AppDomicile` of `appDesktop(host)`.
106
+ It does not build them itself: `codegen/generate-engine-layer.ts` bundles
107
+ `src/desktop/layer.ts` into the engine as `domicile_layer.js`, which the
108
+ engine evaluates on each shell and app page.
109
+
110
+ ```sh
111
+ bun run generate # after changing anything under src/desktop/
112
+ ```
113
+
114
+ - A change to the layer is an engine change: it needs an engine release
115
+ ([RELEASES.md](/packages/domicile-engine/docs/RELEASES.md)).
116
+ - `scripts/test-engine-layer-is-the-sdks.sh` fails when the file is stale.
117
+ - The engine's guards export `HostShell` instead of `Shell` to be handed the
118
+ host itself; see [GUARDS.md](/packages/domicile-engine/docs/GUARDS.md).
94
119
 
95
120
  ## Generated types
96
121
 
package/dist/app.d.ts CHANGED
@@ -1,11 +1,10 @@
1
- import type { DomicileAppHost } from "./domicile-host";
2
- export type { DomicileAppHost, DomicileAppHostEventMap } from "./domicile-host";
1
+ import type { AppDomicile } from "./desktop/app-desktop";
2
+ export type { AppDomicile } from "./desktop/app-desktop";
3
3
  /**
4
4
  * The function an app module exports as `main`.
5
5
  *
6
6
  * `domicile run-app <module>` opens an app window, and Domicile calls this
7
- * with the window's root element and the app's host. Pass the host to
8
- * `system()` from `./system`; the compositor answers files, processes and
9
- * D-Bus, and fails anything else with `permission_denied`.
7
+ * with the window's root element and the app's desktop: files, processes and
8
+ * D-Bus. The compositor fails anything else with `permission_denied`.
10
9
  */
11
- export type AppMain = (root: HTMLElement, host: DomicileAppHost) => void | Promise<void>;
10
+ export type AppMain = (root: HTMLElement, domicile: AppDomicile) => void | Promise<void>;
@@ -1,9 +1,9 @@
1
1
  // The desktop an app module is handed: the shell's system access without its
2
2
  // window manager. See docs/architecture/APP-MODULES.md.
3
- import { system } from "../system";
4
3
  import { dbus } from "./dbus";
5
4
  import { fileCalls } from "./files";
6
5
  import { processCalls } from "./processes";
6
+ import { system } from "./system-calls";
7
7
  /** The desktop an app's `host` reaches. Build one per host. */
8
8
  export const appDesktop = (host) => {
9
9
  const calls = system(host);
@@ -1,4 +1,4 @@
1
- import type { System } from "../system";
1
+ import type { System } from "./system-calls";
2
2
  export type Capture = {
3
3
  screenshot: System["screenshot"];
4
4
  /** A window as it last drew itself, as a `data:` URL of a PNG. */
@@ -1,4 +1,4 @@
1
- import type { System } from "../system";
1
+ import type { System } from "./system-calls";
2
2
  export type Dbus = {
3
3
  call: System["dbusCall"];
4
4
  /** Hear each signal that matches, until stopped. */
@@ -1,6 +1,5 @@
1
1
  // The desktop a shell is handed, typed and grouped by what it is about. See
2
2
  // docs/WRITING-A-SHELL.md.
3
- import { system } from "../system";
4
3
  import { appearance } from "./appearance";
5
4
  import { browser } from "./browser";
6
5
  import { capture } from "./capture";
@@ -18,6 +17,7 @@ import { processes } from "./processes";
18
17
  import { session } from "./session";
19
18
  import { shell } from "./shell";
20
19
  import { hostEvent, on } from "./subscribe";
20
+ import { system } from "./system-calls";
21
21
  import { theme } from "./theme";
22
22
  import { tray } from "./tray";
23
23
  import { windows } from "./windows";
@@ -1,7 +1,7 @@
1
1
  import type { DisplayTransform } from "../display-transform";
2
2
  import type { DomicileDisplay, DomicileHost } from "../domicile-host";
3
- import type { System } from "../system";
4
3
  import type { On } from "./subscribe";
4
+ import type { System } from "./system-calls";
5
5
  /** One screen. Sizes are whole CSS pixels; see `DomicileDisplay`. */
6
6
  export type Display = Omit<DomicileDisplay, "transform"> & {
7
7
  readonly transform: DisplayTransform;
@@ -1,5 +1,5 @@
1
1
  import type { DomicileFileSearch, DomicileHost } from "../domicile-host";
2
- import type { System } from "../system";
2
+ import type { System } from "./system-calls";
3
3
  export type FileSearch = DomicileFileSearch;
4
4
  /** A relative path starts at the home. Refused while the desktop is locked. */
5
5
  export type Files = {
@@ -1,6 +1,6 @@
1
1
  import type { DomicileHost } from "../domicile-host";
2
- import type { System } from "../system";
3
2
  import type { On } from "./subscribe";
3
+ import type { System } from "./system-calls";
4
4
  /** A grabbed chord, pressed or let go. `keycode` is an evdev code. */
5
5
  export type Shortcut = {
6
6
  chord: string;
@@ -0,0 +1,2 @@
1
+ export { appDesktop } from "./app-desktop";
2
+ export { desktop } from "./desktop";
@@ -0,0 +1,5 @@
1
+ // What the engine evaluates to build the desktop it hands a shell's `Shell` or
2
+ // an app's `main`. `codegen/generate-engine-layer.ts` bundles it into the
3
+ // engine; see "The desktop layer" in packages/chrome-sdk/README.md.
4
+ export { appDesktop } from "./app-desktop";
5
+ export { desktop } from "./desktop";
@@ -0,0 +1,34 @@
1
+ import type { DomicileHost } from "../domicile-host";
2
+ import type { BoundShortcut, PortalWallpaper } from "../portal";
3
+ import { Capturing, PortalAnswer, PortalRequest } from "../portal";
4
+ /** What the portal functions need of the desktop `Shell` is handed. */
5
+ export type PortalHost = Pick<DomicileHost, "answerPortalRequest"> & {
6
+ addEventListener: (type: "portalrequests", listener: (event: MessageEvent<string>) => void) => void;
7
+ removeEventListener: (type: "portalrequests", listener: (event: MessageEvent<string>) => void) => void;
8
+ };
9
+ /**
10
+ * Call `listener` with every unanswered request, oldest first, on each change.
11
+ * Returns a function that stops listening. Throws on a request of a known kind
12
+ * whose body does not parse: the compositor and this SDK disagree.
13
+ */
14
+ export declare const watchPortalRequests: (host: PortalHost, listener: (requests: readonly PortalRequest[]) => void) => (() => void);
15
+ /**
16
+ * Call `listener` with every chord applications hold through the
17
+ * GlobalShortcuts portal, on each push. Returns a function that stops
18
+ * listening.
19
+ */
20
+ export declare const watchBoundShortcuts: (host: PortalHost, listener: (shortcuts: readonly BoundShortcut[]) => void) => (() => void);
21
+ /**
22
+ * Call `listener` with every session that controls or captures input, on each
23
+ * change. Returns a function that stops listening.
24
+ */
25
+ export declare const watchCapturing: (host: PortalHost, listener: (sessions: readonly Capturing[]) => void) => (() => void);
26
+ /**
27
+ * Call `listener` with the pictures applications set through the Wallpaper
28
+ * portal, on each push. Returns a function that stops listening.
29
+ */
30
+ export declare const watchPortalWallpaper: (host: PortalHost, listener: (wallpaper: PortalWallpaper) => void) => (() => void);
31
+ /** Answer request `id`. The first answer to reach the compositor wins. */
32
+ export declare const answerPortalRequest: (host: PortalHost, id: number, answer: PortalAnswer) => void;
33
+ /** End capturing session `id`. */
34
+ export declare const stopCapturing: (host: PortalHost, id: number) => void;