@phreshos/react 0.1.8 → 0.1.10
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 +13 -12
- package/dist/live-snapshot.js +1 -1
- package/dist/main.d.ts +1 -1
- package/dist/main.js +1 -1
- package/dist/system-provider.d.ts +30 -0
- package/dist/{host-provider.js → system-provider.js} +28 -20
- package/package.json +5 -5
- package/dist/host-provider.d.ts +0 -28
package/README.md
CHANGED
|
@@ -19,8 +19,8 @@ It provides two kinds of adapter:
|
|
|
19
19
|
- `CurrentProvider` resolves exactly the current values named by its required
|
|
20
20
|
`provide` prop, then exposes them synchronously through `useProgram()`,
|
|
21
21
|
`useProcess()`, and `useParent()`.
|
|
22
|
-
- `
|
|
23
|
-
required `provide` prop. `
|
|
22
|
+
- `SystemProvider` subscribes before reading exactly the system values named by its
|
|
23
|
+
required `provide` prop. `useSystemAppearance()`, `useSystemTheme()`, `useDesktopSize()`, and
|
|
24
24
|
`usePointerPosition()` expose those values synchronously after resolution.
|
|
25
25
|
- `useSubscribe()` and `useObserve()` own persistent ordinary-event
|
|
26
26
|
registrations for one mounted React consumer.
|
|
@@ -77,35 +77,36 @@ The ordinary-event and traffic hooks retain the latest value returned by their p
|
|
|
77
77
|
`message => message`, so the hook retains the latest message unchanged.
|
|
78
78
|
|
|
79
79
|
```tsx
|
|
80
|
-
import {
|
|
80
|
+
import { system } from "@phreshos/client"
|
|
81
81
|
import { useSubscribe } from "@phreshos/react"
|
|
82
82
|
|
|
83
|
-
const desktop = useSubscribe(
|
|
83
|
+
const desktop = useSubscribe(system.desktop, "resize")
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
System reads are asynchronous while subscriptions are live-only. `SystemProvider`
|
|
87
87
|
subscribes before requesting each selected snapshot and prevents an older read
|
|
88
88
|
from overwriting a newer event:
|
|
89
89
|
|
|
90
90
|
```tsx
|
|
91
|
-
import {
|
|
91
|
+
import { SystemProvider, useDesktopSize, useSystemAppearance, useSystemTheme } from "@phreshos/react"
|
|
92
92
|
|
|
93
93
|
function Content() {
|
|
94
|
-
const theme =
|
|
94
|
+
const theme = useSystemTheme()
|
|
95
|
+
const appearance = useSystemAppearance()
|
|
95
96
|
const desktop = useDesktopSize()
|
|
96
97
|
|
|
97
|
-
return <p style={{ color:
|
|
98
|
+
return <p style={{ color: appearance.foreground[theme] }}>{desktop.width} × {desktop.height}</p>
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
function App() {
|
|
101
|
-
return <
|
|
102
|
+
return <SystemProvider provide={["appearance", "theme", "desktopSize"]} fallback={<p>Loading…</p>}>
|
|
102
103
|
<Content />
|
|
103
|
-
</
|
|
104
|
+
</SystemProvider>
|
|
104
105
|
}
|
|
105
106
|
```
|
|
106
107
|
|
|
107
108
|
The selection is required and non-empty. Nothing is read or subscribed merely
|
|
108
|
-
because either SDK was imported, and an unselected
|
|
109
|
+
because either SDK was imported, and an unselected system value never enters the
|
|
109
110
|
Client. `pointerPosition` is permission-guarded: selecting it does not request
|
|
110
111
|
permission, and resolution fails unless the Program already holds `pointer`.
|
|
111
112
|
The provider renders its fallback until every selected value resolves.
|
|
@@ -137,7 +138,7 @@ export default function App() {
|
|
|
137
138
|
}
|
|
138
139
|
```
|
|
139
140
|
|
|
140
|
-
Using a current or
|
|
141
|
+
Using a current or system hook outside its provider, or without selecting its
|
|
141
142
|
value, throws a configuration error. Neither provider supplies an implicit
|
|
142
143
|
"everything" selection, so adding a future capability cannot make it enter an
|
|
143
144
|
existing application.
|
package/dist/live-snapshot.js
CHANGED
|
@@ -14,7 +14,7 @@ export default class LiveSnapshot {
|
|
|
14
14
|
}
|
|
15
15
|
snapshot = () => {
|
|
16
16
|
if (this.value === unavailable)
|
|
17
|
-
throw new Error("The requested
|
|
17
|
+
throw new Error("The requested system value is not ready");
|
|
18
18
|
return this.value;
|
|
19
19
|
};
|
|
20
20
|
subscribe = (listener) => {
|
package/dist/main.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as CurrentProvider, useParent, useProcess, useProgram, type CurrentProvision, type CurrentProvisionName, type CurrentProviderProperties } from "./current-provider.js";
|
|
2
|
-
export { default as
|
|
2
|
+
export { default as SystemProvider, usePointerPosition, useDesktopSize, useSystemAppearance, useSystemTheme, type SystemProvision, type SystemProvisionName, type SystemProviderProperties } from "./system-provider.js";
|
|
3
3
|
export { default as useSubscribe } from "./use-subscribe.js";
|
|
4
4
|
export { default as useObserve } from "./use-observe.js";
|
|
5
5
|
export { default as useProgramState, type ProgramState } from "./use-program-state.js";
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as CurrentProvider, useParent, useProcess, useProgram } from "./current-provider.js";
|
|
2
|
-
export { default as
|
|
2
|
+
export { default as SystemProvider, usePointerPosition, useDesktopSize, useSystemAppearance, useSystemTheme } from "./system-provider.js";
|
|
3
3
|
export { default as useSubscribe } from "./use-subscribe.js";
|
|
4
4
|
export { default as useObserve } from "./use-observe.js";
|
|
5
5
|
export { default as useProgramState } from "./use-program-state.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type Appearance, type DesktopSize, type PointerPosition, type Theme } from "@phreshos/client";
|
|
3
|
+
declare const provisionNames: readonly ["appearance", "theme", "desktopSize", "pointerPosition"];
|
|
4
|
+
/** Requests and follows only the system values explicitly selected by the program. */
|
|
5
|
+
export default function SystemProvider(properties: SystemProviderProperties): import("react").FunctionComponentElement<SelectedSystemProviderProperties>;
|
|
6
|
+
/** Returns the complete unresolved Appearance and follows authoritative updates. */
|
|
7
|
+
export declare function useSystemAppearance(): Appearance;
|
|
8
|
+
/** Returns the effective local Desktop Theme and follows future changes. */
|
|
9
|
+
export declare function useSystemTheme(): Theme;
|
|
10
|
+
/** Returns the selected desktop size and follows future resize events. */
|
|
11
|
+
export declare function useDesktopSize(): DesktopSize;
|
|
12
|
+
/** Returns the selected Pointer position and follows future move events. */
|
|
13
|
+
export declare function usePointerPosition(): PointerPosition | null;
|
|
14
|
+
/** Values that SystemProvider can request and follow. */
|
|
15
|
+
export type SystemProvisionName = typeof provisionNames[number];
|
|
16
|
+
/** Required non-empty selection of system values. */
|
|
17
|
+
export type SystemProvision = readonly [SystemProvisionName, ...SystemProvisionName[]];
|
|
18
|
+
/** Properties accepted by SystemProvider. */
|
|
19
|
+
export interface SystemProviderProperties {
|
|
20
|
+
/** Content rendered after every selected system value has resolved. */
|
|
21
|
+
readonly children: ReactNode;
|
|
22
|
+
/** Content rendered while selected system values resolve. */
|
|
23
|
+
readonly fallback: ReactNode;
|
|
24
|
+
/** Exact system values requested and followed for mounted descendants. */
|
|
25
|
+
readonly provide: SystemProvision;
|
|
26
|
+
}
|
|
27
|
+
type SelectedSystemProviderProperties = SystemProviderProperties & Readonly<{
|
|
28
|
+
selection: readonly SystemProvisionName[];
|
|
29
|
+
}>;
|
|
30
|
+
export {};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { createContext, createElement, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { system } from "@phreshos/client";
|
|
3
3
|
import LiveSnapshot from "./live-snapshot.js";
|
|
4
|
-
const provisionNames = ["theme", "desktopSize", "pointerPosition"];
|
|
5
|
-
const
|
|
6
|
-
/** Requests and follows only the
|
|
7
|
-
export default function
|
|
4
|
+
const provisionNames = ["appearance", "theme", "desktopSize", "pointerPosition"];
|
|
5
|
+
const SystemContext = createContext(null);
|
|
6
|
+
/** Requests and follows only the system values explicitly selected by the program. */
|
|
7
|
+
export default function SystemProvider(properties) {
|
|
8
8
|
const normalized = normalizeProvision(properties.provide);
|
|
9
9
|
const selectionKey = normalized.join("\0");
|
|
10
10
|
const selection = useMemo(() => normalized, [selectionKey]);
|
|
11
|
-
return createElement(
|
|
11
|
+
return createElement(SelectedSystemProvider, { ...properties, key: selectionKey, selection });
|
|
12
12
|
}
|
|
13
|
-
function
|
|
13
|
+
function SelectedSystemProvider({ children, fallback, selection }) {
|
|
14
14
|
const stores = useMemo(() => createStores(selection), [selection]);
|
|
15
15
|
const [resolution, setResolution] = useState({ status: "pending" });
|
|
16
16
|
useEffect(() => {
|
|
@@ -35,10 +35,14 @@ function SelectedHostProvider({ children, fallback, selection }) {
|
|
|
35
35
|
return fallback;
|
|
36
36
|
if (resolution.status === "error")
|
|
37
37
|
throw resolution.error;
|
|
38
|
-
return createElement(
|
|
38
|
+
return createElement(SystemContext.Provider, { value: { provided: new Set(selection), stores } }, children);
|
|
39
39
|
}
|
|
40
|
-
/** Returns the
|
|
41
|
-
export function
|
|
40
|
+
/** Returns the complete unresolved Appearance and follows authoritative updates. */
|
|
41
|
+
export function useSystemAppearance() {
|
|
42
|
+
return useProvided("appearance");
|
|
43
|
+
}
|
|
44
|
+
/** Returns the effective local Desktop Theme and follows future changes. */
|
|
45
|
+
export function useSystemTheme() {
|
|
42
46
|
return useProvided("theme");
|
|
43
47
|
}
|
|
44
48
|
/** Returns the selected desktop size and follows future resize events. */
|
|
@@ -50,11 +54,11 @@ export function usePointerPosition() {
|
|
|
50
54
|
return useProvided("pointerPosition");
|
|
51
55
|
}
|
|
52
56
|
function useProvided(name) {
|
|
53
|
-
const context = useContext(
|
|
57
|
+
const context = useContext(SystemContext);
|
|
54
58
|
if (!context)
|
|
55
|
-
throw new Error(`${hookNames[name]} must be used inside
|
|
59
|
+
throw new Error(`${hookNames[name]} must be used inside SystemProvider`);
|
|
56
60
|
if (!context.provided.has(name))
|
|
57
|
-
throw new Error(`${hookNames[name]} requires "${name}" in
|
|
61
|
+
throw new Error(`${hookNames[name]} requires "${name}" in SystemProvider's provide prop`);
|
|
58
62
|
const store = context.stores[name];
|
|
59
63
|
return useSyncExternalStore(store.subscribe, store.snapshot, store.snapshot);
|
|
60
64
|
}
|
|
@@ -62,14 +66,17 @@ function createStores(selection) {
|
|
|
62
66
|
const stores = {};
|
|
63
67
|
for (const name of selection) {
|
|
64
68
|
switch (name) {
|
|
69
|
+
case "appearance":
|
|
70
|
+
stores.appearance = new LiveSnapshot(() => system.appearance.snapshot(), subscriber => system.appearance.subscribe("change", subscriber));
|
|
71
|
+
break;
|
|
65
72
|
case "theme":
|
|
66
|
-
stores.theme = new LiveSnapshot(() =>
|
|
73
|
+
stores.theme = new LiveSnapshot(() => system.theme.snapshot(), subscriber => system.theme.subscribe("change", subscriber));
|
|
67
74
|
break;
|
|
68
75
|
case "desktopSize":
|
|
69
|
-
stores.desktopSize = new LiveSnapshot(() =>
|
|
76
|
+
stores.desktopSize = new LiveSnapshot(() => system.desktop.size(), subscriber => system.desktop.subscribe("resize", subscriber));
|
|
70
77
|
break;
|
|
71
78
|
case "pointerPosition":
|
|
72
|
-
stores.pointerPosition = new LiveSnapshot(() =>
|
|
79
|
+
stores.pointerPosition = new LiveSnapshot(() => system.pointer.position(), subscriber => system.pointer.subscribe("move", subscriber));
|
|
73
80
|
break;
|
|
74
81
|
}
|
|
75
82
|
}
|
|
@@ -77,19 +84,20 @@ function createStores(selection) {
|
|
|
77
84
|
}
|
|
78
85
|
function normalizeProvision(provide) {
|
|
79
86
|
if (!Array.isArray(provide) || provide.length === 0)
|
|
80
|
-
throw new Error("
|
|
87
|
+
throw new Error("SystemProvider's provide prop must select at least one system value");
|
|
81
88
|
const selected = new Set();
|
|
82
89
|
for (const name of provide) {
|
|
83
90
|
if (!provisionNames.includes(name))
|
|
84
|
-
throw new Error(`
|
|
91
|
+
throw new Error(`SystemProvider cannot provide "${String(name)}"`);
|
|
85
92
|
if (selected.has(name))
|
|
86
|
-
throw new Error(`
|
|
93
|
+
throw new Error(`SystemProvider's provide prop selects "${name}" more than once`);
|
|
87
94
|
selected.add(name);
|
|
88
95
|
}
|
|
89
96
|
return [...selected];
|
|
90
97
|
}
|
|
91
98
|
const hookNames = {
|
|
92
|
-
|
|
99
|
+
appearance: "useSystemAppearance",
|
|
100
|
+
theme: "useSystemTheme",
|
|
93
101
|
desktopSize: "useDesktopSize",
|
|
94
102
|
pointerPosition: "usePointerPosition"
|
|
95
103
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "React adapters for the Client SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"prepack": "node --run test && node --run build"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@phreshos/client": "^0.1.
|
|
51
|
-
"@phreshos/core": "^0.1.
|
|
50
|
+
"@phreshos/client": "^0.1.22",
|
|
51
|
+
"@phreshos/core": "^0.1.19",
|
|
52
52
|
"react": "^19.2.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@phreshos/client": "^0.1.
|
|
56
|
-
"@phreshos/core": "^0.1.
|
|
55
|
+
"@phreshos/client": "^0.1.22",
|
|
56
|
+
"@phreshos/core": "^0.1.19",
|
|
57
57
|
"@testing-library/react": "^16.3.0",
|
|
58
58
|
"@types/react": "^19.2.18",
|
|
59
59
|
"@types/react-dom": "^19.2.4",
|
package/dist/host-provider.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from "react";
|
|
2
|
-
import { type DesktopSize, type PointerPosition, type ThemeProperties } from "@phreshos/client";
|
|
3
|
-
declare const provisionNames: readonly ["theme", "desktopSize", "pointerPosition"];
|
|
4
|
-
/** Requests and follows only the host values explicitly selected by the program. */
|
|
5
|
-
export default function HostProvider(properties: HostProviderProperties): import("react").FunctionComponentElement<SelectedHostProviderProperties>;
|
|
6
|
-
/** Returns the selected Theme snapshot and follows future change events. */
|
|
7
|
-
export declare function useHostTheme(): Readonly<ThemeProperties>;
|
|
8
|
-
/** Returns the selected desktop size and follows future resize events. */
|
|
9
|
-
export declare function useDesktopSize(): DesktopSize;
|
|
10
|
-
/** Returns the selected Pointer position and follows future move events. */
|
|
11
|
-
export declare function usePointerPosition(): PointerPosition | null;
|
|
12
|
-
/** Values that HostProvider can request and follow. */
|
|
13
|
-
export type HostProvisionName = typeof provisionNames[number];
|
|
14
|
-
/** Required non-empty selection of host values. */
|
|
15
|
-
export type HostProvision = readonly [HostProvisionName, ...HostProvisionName[]];
|
|
16
|
-
/** Properties accepted by HostProvider. */
|
|
17
|
-
export interface HostProviderProperties {
|
|
18
|
-
/** Content rendered after every selected host value has resolved. */
|
|
19
|
-
readonly children: ReactNode;
|
|
20
|
-
/** Content rendered while selected host values resolve. */
|
|
21
|
-
readonly fallback: ReactNode;
|
|
22
|
-
/** Exact host values requested and followed for mounted descendants. */
|
|
23
|
-
readonly provide: HostProvision;
|
|
24
|
-
}
|
|
25
|
-
type SelectedHostProviderProperties = HostProviderProperties & Readonly<{
|
|
26
|
-
selection: readonly HostProvisionName[];
|
|
27
|
-
}>;
|
|
28
|
-
export {};
|