@phreshos/client 0.1.5 → 0.1.6

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
@@ -18,7 +18,7 @@ a peer dependency. It does not redefine those objects, own server-side
18
18
  capabilities, or contain host and transport implementations.
19
19
 
20
20
  Its `Host` contract exposes only desktop-session capabilities: the public
21
- system Theme, surface and pointer state, publicly served values, and
21
+ system Theme, desktop and pointer state, publicly served values, and
22
22
  unrestricted server-side Fetch. `host.theme.snapshot()` explicitly and
23
23
  asynchronously reads the current snapshot retained by the desktop host.
24
24
  `host.theme.subscribe("change", listener)` is an ordinary live subscription:
@@ -26,22 +26,23 @@ it receives only complete replacements published after registration, with no
26
26
  initial value or replay. The Client cannot write the authoritative value. The
27
27
  Host does not expose system-wide Program or Process discovery.
28
28
 
29
- Surface and pointer access are independent objects rather than events merged
29
+ Desktop and pointer access are independent objects rather than events merged
30
30
  into Host:
31
31
 
32
32
  ```ts
33
- const surface = await host.surface.size()
34
- const stopSurface = host.surface.subscribe("resize", next => undefined)
33
+ const desktop = await host.desktop.size()
34
+ const stopDesktop = host.desktop.subscribe("resize", next => undefined)
35
35
 
36
36
  const position = await host.pointer.position()
37
37
  const stopPointer = host.pointer.subscribe("move", next => undefined)
38
38
  ```
39
39
 
40
- A Surface contains only the width and height of the current Client Window's
41
- own layer. Client code cannot select another layer, and the desktop's gutter
42
- never enters an endpoint. Pointer position and movement both require the
43
- `pointer` permission. Reads are asynchronous requests; subscriptions receive
44
- only future publications and never replay a retained value.
40
+ The desktop size is the complete desktop area containing this Client,
41
+ independent of the Client Window's layer. Client code cannot select a layer,
42
+ and the desktop's gutter never enters an endpoint. Pointer position and
43
+ movement both require the `pointer` permission. Reads are asynchronous
44
+ requests; subscriptions receive only future publications and never replay a
45
+ retained value.
45
46
 
46
47
  Permission decisions belong to the current Program, not to the desktop Host.
47
48
  They are available from a Program handle and flattened through `current` as the
@@ -127,36 +128,31 @@ The same rule applies to Client-visible traffic destinations.
127
128
 
128
129
  Client-visible Program handles can operate only within their own Program.
129
130
  They deliberately omit `install()` and `fork()`, and their storage areas never
130
- expose host filesystem paths. Client Window capabilities add awaitable
131
- `localMove()` and `localResize()` for representation-local gestures. Their
132
- Promises confirm that the current Client host accepted and applied the local
133
- draft; they do not enter server authority or emit Window events.
131
+ expose host filesystem paths. `current.window` is authoritative and
132
+ subscribable. `current.localWindow` is the physical representation belonging to
133
+ this iframe on this desktop. Its reads and updates are local, have no events,
134
+ and cannot target another Client handle.
134
135
  When both dimensions must change, `setGeometry({ position, size })` commits
135
136
  them through one authoritative request and produces one `geometry` event.
136
137
  Calling `move()` and `resize()` sequentially or through `Promise.all()` remains
137
138
  two independent operations and can expose an intermediate state remotely.
138
139
 
139
- An `under` or `over` Window representation may request one local host-rendered
140
- Surface. The capability follows the same desktop-local targeting model as
141
- `localMove()` and `localResize()`; it is command-only and has no server-owned
142
- state:
140
+ An `under` or `over` representation may request one local host-rendered Surface:
143
141
 
144
142
  ```ts
145
- await window.surface.set({
143
+ await current.localWindow.surface.set({
146
144
  opacity: 0.65,
147
- radius: "large",
148
- transaction: { duration: 240, easing: "ease-out" }
149
- })
145
+ radius: "large"
146
+ }, { duration: 240, easing: "ease-out", wait: true })
150
147
 
151
- await window.surface.remove()
148
+ await current.localWindow.surface.remove()
152
149
  ```
153
150
 
154
- The desktop holds the target only for the lifetime of the addressed iframe.
155
- Reloading or destroying that representation removes it; another accessible
156
- Window handle may tailor the same local representation, while other desktops
157
- remain unaffected. Program code may synchronize desired settings through its
158
- Server and explicitly apply them again. `set()` with no settings creates a
159
- sharp, fully opaque Surface.
151
+ The desktop holds local state only for the lifetime of that iframe
152
+ representation. Reloading or destroying it resets the representation from
153
+ authoritative truth, while other desktops remain unaffected. Program code may
154
+ synchronize desired settings through its Server and explicitly apply them
155
+ again. `set()` with no settings creates a sharp, fully opaque Surface.
160
156
  Opacity is a finite number from `0` through `1`; zero retains the Surface node.
161
157
  Radius accepts a nonnegative pixel number, a Theme-derived `ScaleLevel`, or
162
158
  `"full"`. Only `remove()` restores exact `null` and immediately removes the
package/dist/current.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { LocalWindow } from "@phreshos/core";
1
2
  import { type Channel } from "./channel.js";
2
3
  import { Client, Server, type Process, type Program, type Window } from "./domain.js";
3
4
  /** The current Process's canonical Server handle. */
@@ -8,6 +9,8 @@ export interface Current<Events extends object = {}> extends Channel<Events>, Pi
8
9
  readonly server: CurrentServer;
9
10
  /** Presentation capability of this current Client. */
10
11
  readonly window: Window;
12
+ /** Physical Window representation belonging only to this Client iframe. */
13
+ readonly localWindow: LocalWindow;
11
14
  /** The same permission capability exposed by the current Program. */
12
15
  readonly permissions: Program["permissions"];
13
16
  /** Returns the Process represented by this Client. */
package/dist/current.js CHANGED
@@ -84,6 +84,7 @@ currentClient = new CurrentClientHandle(owner);
84
84
  class ClientCurrent {
85
85
  server = currentServer;
86
86
  window = currentClient.window;
87
+ localWindow = new LocalWindowHandle();
87
88
  permissions = currentProgramPermissions;
88
89
  constructor() {
89
90
  bindChannel(this, channel);
@@ -104,6 +105,30 @@ class ClientCurrent {
104
105
  async stop() { await wire.request(["stop-current"]); }
105
106
  service() { return endpointService(null, "client"); }
106
107
  }
108
+ class LocalWindowHandle {
109
+ surface = new LocalWindowSurfaceHandle();
110
+ async state() {
111
+ const answer = await wire.request(["localWindow"]);
112
+ return answer[0];
113
+ }
114
+ async title() { return (await this.state()).title; }
115
+ async position() { return (await this.state()).position; }
116
+ async size() { return (await this.state()).size; }
117
+ async minimized() { return (await this.state()).minimized; }
118
+ async front() { return (await this.state()).front; }
119
+ async layer() { return (await this.state()).layer; }
120
+ async location() { return (await this.state()).location; }
121
+ async move(position, transaction) { await wire.request(["localWindowMove", position, transaction]); }
122
+ async resize(size, transaction) { await wire.request(["localWindowResize", size, transaction]); }
123
+ async setGeometry(geometry, transaction) { await wire.request(["localWindowGeometry", geometry, transaction]); }
124
+ async minimize(minimized = true) { await wire.request(["localWindowMinimize", minimized]); }
125
+ async changeTitle(title) { await wire.request(["localWindowTitle", title]); }
126
+ async raise() { await wire.request(["localWindowRaise"]); }
127
+ }
128
+ class LocalWindowSurfaceHandle {
129
+ async set(settings = {}, transaction) { await wire.request(["localWindowSurfaceSet", settings, transaction]); }
130
+ async remove() { await wire.request(["localWindowSurfaceRemove"]); }
131
+ }
107
132
  function bindChannel(target, source) {
108
133
  Object.assign(target, {
109
134
  publish: source.publish.bind(source),
@@ -0,0 +1,20 @@
1
+ import type { Size, Subscribable } from "@phreshos/core";
2
+ import Events from "./events.js";
3
+ /** Live changes to the desktop area containing this Client. */
4
+ export type DesktopEvents = {
5
+ /** The desktop area resized. */
6
+ resize: Size;
7
+ };
8
+ /** Explicit desktop size reads and future resizes. */
9
+ export interface HostDesktop extends Subscribable<DesktopEvents, never> {
10
+ /** Reads the complete current desktop area in CSS pixels. */
11
+ size(): Promise<Size>;
12
+ }
13
+ /** Desktop access bound to the current Client Process boundary. */
14
+ export default class ClientDesktop extends Events {
15
+ constructor();
16
+ size(): Promise<Readonly<{
17
+ width: import("@phreshos/core").Value;
18
+ height: import("@phreshos/core").Value;
19
+ }>>;
20
+ }
@@ -0,0 +1,34 @@
1
+ import Events from "./events.js";
2
+ import wire from "./wire.js";
3
+ /** Desktop access bound to the current Client Process boundary. */
4
+ export default class ClientDesktop extends Events {
5
+ constructor() {
6
+ super((event, listener, impossible) => wire.on("host-desktop", event, value => {
7
+ const size = createSize(value);
8
+ if (size)
9
+ listener(size);
10
+ }, null, impossible), observer => wire.onAll("host-desktop", (event, value) => {
11
+ const size = createSize(value);
12
+ if (typeof event === "string" && size)
13
+ observer(event, size);
14
+ }));
15
+ }
16
+ async size() {
17
+ const answer = await wire.request(["desktop"]);
18
+ const size = createSize(answer[0]);
19
+ if (!size)
20
+ throw new Error("The host returned an invalid desktop size");
21
+ return size;
22
+ }
23
+ }
24
+ function createSize(value) {
25
+ if (typeof value !== "object" || value === null || Array.isArray(value))
26
+ return null;
27
+ const size = value;
28
+ if (!finite(size.width) || !finite(size.height))
29
+ return null;
30
+ return Object.freeze({ width: size.width, height: size.height });
31
+ }
32
+ function finite(value) {
33
+ return typeof value === "number" && Number.isFinite(value);
34
+ }
package/dist/domain.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerObserver as CoreAnswerObserver, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskObserver as CoreAskObserver, type Cleanup, type ClientSurfaceSettings, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointTraffic as CoreEndpointTraffic, type EndpointDeclaration, type Exit, type Launch, type Permissions, type Position, type ServerTraffic as CoreServerTraffic, type Size, type TrafficMessage as CoreTrafficMessage, type TrafficCapture as CoreTrafficCapture, type TrafficEvents as CoreTrafficEvents, type Window as CoreWindow, type WindowState } from "@phreshos/core";
1
+ import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerObserver as CoreAnswerObserver, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskObserver as CoreAskObserver, type Cleanup, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointTraffic as CoreEndpointTraffic, type EndpointDeclaration, type Exit, type Launch, type Permissions, type ServerTraffic as CoreServerTraffic, type TrafficMessage as CoreTrafficMessage, type TrafficCapture as CoreTrafficCapture, type TrafficEvents as CoreTrafficEvents, type Window as CoreWindow, type WindowState } from "@phreshos/core";
2
2
  import Events from "./events.js";
3
3
  export interface HandleAddress {
4
4
  identity: string;
@@ -102,22 +102,8 @@ export type Client<Events extends object = {}> = Omit<CoreClient<Events>, "proce
102
102
  /** Returns the Process that owns this Client. */
103
103
  process(): Promise<Process>;
104
104
  };
105
- /** Client-owned Window capability visible inside a Client boundary. */
106
- export type Window = CoreWindow & {
107
- /** Host-rendered material belonging only to this desktop representation. */
108
- readonly surface: ClientWindowSurface;
109
- /** Moves this Window representation without changing authoritative state. */
110
- localMove(position: Position): Promise<void>;
111
- /** Resizes this Window representation without changing authoritative state. */
112
- localResize(size: Size): Promise<void>;
113
- };
114
- /** Command-only Surface of one Window representation on this desktop. */
115
- export interface ClientWindowSurface {
116
- /** Creates or replaces the representation-local Surface. */
117
- set(settings?: ClientSurfaceSettings): Promise<void>;
118
- /** Immediately removes the representation-local Surface. */
119
- remove(): Promise<void>;
120
- }
105
+ /** Authoritative Client-owned Window capability visible inside a Client boundary. */
106
+ export type Window = CoreWindow;
121
107
  export declare class TrafficHandle extends Events {
122
108
  protected readonly target: HandleAddress | null;
123
109
  protected readonly kind: "server" | "client";
package/dist/domain.js CHANGED
@@ -206,11 +206,9 @@ class ClientHandle extends ClientBase {
206
206
  }
207
207
  class WindowHandle extends Events {
208
208
  target;
209
- surface;
210
209
  constructor(target) {
211
210
  super(...deferredScoped("host-end", target, (_event, values) => values[0]));
212
211
  this.target = target;
213
- this.surface = new ClientWindowSurfaceHandle(target);
214
212
  }
215
213
  async state() {
216
214
  const answer = await wire.request(["window", await this.target()]);
@@ -224,22 +222,12 @@ class WindowHandle extends Events {
224
222
  async layer() { return (await this.state()).layer; }
225
223
  async location() { return (await this.state()).location; }
226
224
  async move(position) { await wire.request(["move", await this.target(), position]); }
227
- async localMove(position) { await wire.request(["localMove", await this.target(), position]); }
228
225
  async resize(size) { await wire.request(["resize", await this.target(), size]); }
229
226
  async setGeometry(geometry) { await wire.request(["setGeometry", await this.target(), geometry]); }
230
- async localResize(size) { await wire.request(["localResize", await this.target(), size]); }
231
227
  async minimize(minimized = true) { await wire.request(["minimize", await this.target(), minimized]); }
232
228
  async changeTitle(title) { await wire.request(["changeTitle", await this.target(), title]); }
233
229
  async raise() { await wire.request(["raise", await this.target()]); }
234
230
  }
235
- class ClientWindowSurfaceHandle {
236
- target;
237
- constructor(target) {
238
- this.target = target;
239
- }
240
- async set(settings = {}) { await wire.request(["surfaceSet", await this.target(), settings]); }
241
- async remove() { await wire.request(["surfaceRemove", await this.target()]); }
242
- }
243
231
  function deferredScoped(route, target, convert) {
244
232
  return [
245
233
  (event, listener, impossible) => {
package/dist/host.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import type { ClientServiceHandler, ServedFile, ServerServiceHandler, Theme, ThemeProperties } from "@phreshos/core";
2
2
  import { type HostPointer } from "./pointer.js";
3
- import { type HostSurface } from "./surface.js";
3
+ import { type HostDesktop } from "./desktop.js";
4
4
  /** Desktop capabilities structurally available to a Client endpoint. */
5
5
  export interface Host {
6
6
  /** Read-only system Theme explicitly read from and observed through the desktop host. */
7
7
  readonly theme: Theme<ThemeProperties>;
8
- /** This Client Window's asynchronous surface read and live updates. */
9
- readonly surface: HostSurface;
8
+ /** Layer-independent desktop size reads and live updates. */
9
+ readonly desktop: HostDesktop;
10
10
  /** Permission-guarded desktop pointer reads and live movement. */
11
11
  readonly pointer: HostPointer;
12
12
  /** Stores one value as a publicly reachable file. */
package/dist/host.js CHANGED
@@ -2,11 +2,11 @@ import { content } from "./content.js";
2
2
  import ClientTheme from "./theme.js";
3
3
  import wire from "./wire.js";
4
4
  import ClientPointer, {} from "./pointer.js";
5
- import ClientSurface, {} from "./surface.js";
5
+ import ClientDesktop, {} from "./desktop.js";
6
6
  import { service as serviceHandle } from "./service.js";
7
7
  class ClientHost {
8
8
  theme = new ClientTheme();
9
- surface = new ClientSurface();
9
+ desktop = new ClientDesktop();
10
10
  pointer = new ClientPointer();
11
11
  async serve(value) {
12
12
  const source = content(value);
package/dist/main.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { host, type Host } from "./host.js";
2
2
  export { type HostPointer, type PointerEvents, type PointerPosition } from "./pointer.js";
3
- export { type HostSurface, type Surface, type SurfaceEvents } from "./surface.js";
3
+ export { type DesktopEvents, type HostDesktop } from "./desktop.js";
4
4
  export { current, type Current, type CurrentServer } from "./current.js";
5
5
  export { type Channel, type ChannelCapture, type ChannelEvents, type ChannelMessage } from "./channel.js";
6
6
  export { ClientServiceHandler, ServerServiceHandler, ServiceHandler, type ClientServiceChannel, type ServerServiceChannel, type ServiceChannel, type ServiceKey, type ServiceLifecycleEvents } from "@phreshos/core";
7
- export { Client, Endpoint, Process, Program, Server, type ClientWindowSurface, type Window, type AnswerCapture, type AnswerMessage, type AnswerObserver, type AskCapture, type AskMessage, type AskObserver, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
8
- export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permissions, Position, ProgramArea, ProgramEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermissions, Timeoutable, Theme, ThemeEvents, ThemeProperties, Value, WritableTheme, WindowEvents, WindowGeometry, WindowLayer, WindowState, ClientSurfaceEasing, ClientSurfaceSettings, ClientSurfaceTransaction } from "@phreshos/core";
7
+ export { Client, Endpoint, Process, Program, Server, type Window, type AnswerCapture, type AnswerMessage, type AnswerObserver, type AskCapture, type AskMessage, type AskObserver, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
8
+ export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permissions, Position, ProgramArea, ProgramEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermissions, Timeoutable, Theme, ThemeEvents, ThemeProperties, Value, WritableTheme, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, SurfaceSettings, Transaction } from "@phreshos/core";
package/dist/main.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { host } from "./host.js";
2
2
  export {} from "./pointer.js";
3
- export {} from "./surface.js";
3
+ export {} from "./desktop.js";
4
4
  export { current } from "./current.js";
5
5
  export {} from "./channel.js";
6
6
  export { ClientServiceHandler, ServerServiceHandler, ServiceHandler } from "@phreshos/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/client",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "The SDK used by a Program's client endpoint.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -46,10 +46,10 @@
46
46
  "prepack": "node --run build"
47
47
  },
48
48
  "peerDependencies": {
49
- "@phreshos/core": "^0.1.5"
49
+ "@phreshos/core": "^0.1.6"
50
50
  },
51
51
  "devDependencies": {
52
- "@phreshos/core": "^0.1.5",
52
+ "@phreshos/core": "^0.1.6",
53
53
  "typescript": "^6.0.3"
54
54
  }
55
55
  }
package/dist/surface.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import type { Subscribable } from "@phreshos/core";
2
- import Events from "./events.js";
3
- /** The available workspace of this Client Window's layer. */
4
- export type Surface = Readonly<{
5
- /** Available width in CSS pixels. */
6
- width: number;
7
- /** Available height in CSS pixels. */
8
- height: number;
9
- }>;
10
- /** Live changes to this Client Window's surface. */
11
- export type SurfaceEvents = {
12
- /** The available workspace resized. */
13
- resize: Surface;
14
- };
15
- /** Explicit surface size reads and future resizes. */
16
- export interface HostSurface extends Subscribable<SurfaceEvents, never> {
17
- /** Reads this Client Window's current surface. */
18
- size(): Promise<Surface>;
19
- }
20
- /** Client surface access bound to the current Process boundary. */
21
- export default class ClientSurface extends Events {
22
- constructor();
23
- size(): Promise<Readonly<{
24
- /** Available width in CSS pixels. */
25
- width: number;
26
- /** Available height in CSS pixels. */
27
- height: number;
28
- }>>;
29
- }
package/dist/surface.js DELETED
@@ -1,34 +0,0 @@
1
- import Events from "./events.js";
2
- import wire from "./wire.js";
3
- /** Client surface access bound to the current Process boundary. */
4
- export default class ClientSurface extends Events {
5
- constructor() {
6
- super((event, listener, impossible) => wire.on("host-surface", event, value => {
7
- const surface = createSurface(value);
8
- if (surface)
9
- listener(surface);
10
- }, null, impossible), observer => wire.onAll("host-surface", (event, value) => {
11
- const surface = createSurface(value);
12
- if (typeof event === "string" && surface)
13
- observer(event, surface);
14
- }));
15
- }
16
- async size() {
17
- const answer = await wire.request(["surface"]);
18
- const surface = createSurface(answer[0]);
19
- if (!surface)
20
- throw new Error("The desktop returned an invalid Surface size");
21
- return surface;
22
- }
23
- }
24
- function createSurface(value) {
25
- if (typeof value !== "object" || value === null || Array.isArray(value))
26
- return null;
27
- const surface = value;
28
- if (!finite(surface.width) || !finite(surface.height))
29
- return null;
30
- return Object.freeze({ width: surface.width, height: surface.height });
31
- }
32
- function finite(value) {
33
- return typeof value === "number" && Number.isFinite(value);
34
- }