foldkit-primitives 0.8.0 → 0.10.0
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 +32 -2
- package/dist/device/index.d.mts +66 -33
- package/dist/device/index.d.mts.map +1 -1
- package/dist/device/index.mjs +34 -34
- package/dist/device/index.mjs.map +1 -1
- package/dist/dom/index.d.mts +78 -18
- package/dist/dom/index.d.mts.map +1 -1
- package/dist/dom/index.mjs +56 -22
- package/dist/dom/index.mjs.map +1 -1
- package/dist/{editable-text-BbsPOATY.d.mts → editable-text-Dvm-Fuqs.d.mts} +65 -3
- package/dist/editable-text-Dvm-Fuqs.d.mts.map +1 -0
- package/dist/events/index.d.mts +52 -31
- package/dist/events/index.d.mts.map +1 -1
- package/dist/events/index.mjs +40 -28
- package/dist/events/index.mjs.map +1 -1
- package/dist/{follow-tab-stop-BDxyoUPQ.mjs → follow-tab-stop-Ci1tqAIW.mjs} +63 -61
- package/dist/follow-tab-stop-Ci1tqAIW.mjs.map +1 -0
- package/dist/{index-Cppf-lBx.d.mts → index-BC8kcar9.d.mts} +35 -16
- package/dist/index-BC8kcar9.d.mts.map +1 -0
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +5 -5
- package/dist/{input-modality-m6v7__Xs.mjs → input-modality-BXD383jg.mjs} +9 -9
- package/dist/input-modality-BXD383jg.mjs.map +1 -0
- package/dist/{input-modality-DGOhAknO.d.mts → input-modality-BY-bSrEc.d.mts} +9 -2
- package/dist/input-modality-BY-bSrEc.d.mts.map +1 -0
- package/dist/interaction/index.d.mts +118 -118
- package/dist/interaction/index.d.mts.map +1 -1
- package/dist/interaction/index.mjs +3 -3
- package/dist/interaction/index.mjs.map +1 -1
- package/dist/media/index.d.mts +24 -3
- package/dist/media/index.d.mts.map +1 -1
- package/dist/media/index.mjs +23 -13
- package/dist/media/index.mjs.map +1 -1
- package/dist/motion/index.d.mts +1 -1
- package/dist/motion/index.mjs +1 -2
- package/dist/{motion-DGGBZ0wt.mjs → motion-DmFDppRn.mjs} +20 -20
- package/dist/{motion-DGGBZ0wt.mjs.map → motion-DmFDppRn.mjs.map} +1 -1
- package/dist/net/index.d.mts +57 -19
- package/dist/net/index.d.mts.map +1 -1
- package/dist/net/index.mjs +31 -31
- package/dist/net/index.mjs.map +1 -1
- package/dist/observers/index.d.mts +25 -0
- package/dist/observers/index.d.mts.map +1 -1
- package/dist/observers/index.mjs +22 -22
- package/dist/observers/index.mjs.map +1 -1
- package/dist/state/index.d.mts +98 -44
- package/dist/state/index.d.mts.map +1 -1
- package/dist/state/index.mjs +39 -39
- package/dist/state/index.mjs.map +1 -1
- package/dist/time/index.d.mts +35 -5
- package/dist/time/index.d.mts.map +1 -1
- package/dist/time/index.mjs +20 -20
- package/dist/time/index.mjs.map +1 -1
- package/package.json +14 -14
- package/dist/editable-text-BbsPOATY.d.mts.map +0 -1
- package/dist/follow-tab-stop-BDxyoUPQ.mjs.map +0 -1
- package/dist/index-Cppf-lBx.d.mts.map +0 -1
- package/dist/input-modality-DGOhAknO.d.mts.map +0 -1
- package/dist/input-modality-m6v7__Xs.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ import * as Subscription from 'foldkit/subscription'
|
|
|
106
106
|
import { keyboardEvents } from 'foldkit-primitives/events'
|
|
107
107
|
|
|
108
108
|
const subscriptions = Subscription.make<GameModel, GameMessage>()(() => ({
|
|
109
|
-
keys: Subscription.
|
|
109
|
+
keys: Subscription.persistentEntry(
|
|
110
110
|
keyboardEvents({ preventDefault: press => press.key.startsWith('Arrow') }).pipe(
|
|
111
111
|
Stream.filter(event => event._tag === 'Pressed'),
|
|
112
112
|
Stream.map(({ key }) => GameMessage.PressedKey({ key })),
|
|
@@ -138,6 +138,36 @@ const copy = (model: NoteModel): Update.Return<NoteModel, NoteMessage> => ({
|
|
|
138
138
|
})
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
## Find one by the job
|
|
142
|
+
|
|
143
|
+
| You want to | Reach for | In |
|
|
144
|
+
| --- | --- | --- |
|
|
145
|
+
| Follow the color scheme, reduced motion or any media query | `MediaQuery`, `PrefersDark`, `PrefersReducedMotion` | [media](./media/README.md) |
|
|
146
|
+
| Change the layout at named widths | `Breakpoints` | [media](./media/README.md) |
|
|
147
|
+
| Know whether the tab is visible, the window's size, or the user idle | `Visibility`, `WindowSize`, `Idle` | [events](./events/README.md) |
|
|
148
|
+
| Answer keyboard shortcuts | `keyboardEvents` with `matchHotkey` | [events](./events/README.md) |
|
|
149
|
+
| Count ticks, or run something on an interval | `Timer`, `Interval`, `ticks` | [time](./time/README.md) |
|
|
150
|
+
| Wait until typing stops, or limit how often something runs | `debounce`, `Throttle` | [time](./time/README.md) |
|
|
151
|
+
| Say "3 minutes ago" | `formatRelativeTime` | [time](./time/README.md) |
|
|
152
|
+
| Page through a list | `Pagination`, `range` | [state](./state/README.md) |
|
|
153
|
+
| Undo and redo | `history` | [state](./state/README.md) |
|
|
154
|
+
| Draw only the visible rows of a long list | `Virtual` | [state](./state/README.md) |
|
|
155
|
+
| Animate a value, or let an element leave before it is removed | `Tween`, `Spring`, `Presence` | [motion](./motion/README.md) |
|
|
156
|
+
| Move with arrow keys through a toolbar, list, grid or tree | `RovingTabindex`, `ListNavigation`, `GridNavigation`, `TreeNavigation` | [interaction](./interaction/README.md) |
|
|
157
|
+
| Close a popup on Escape or an outside press | `DismissLayer`, `Overlay` | [interaction](./interaction/README.md) |
|
|
158
|
+
| Press, long-press or drag | `Press`, `LongPress`, `Move`, `PointerDrag` | [interaction](./interaction/README.md) |
|
|
159
|
+
| Edit text in place | `EditableText` | [interaction](./interaction/README.md) |
|
|
160
|
+
| Tell a screen reader what changed | `LiveAnnounce` | [interaction](./interaction/README.md) |
|
|
161
|
+
| Know whether the browser is online, or talk over a socket | `Online`, `websocket`, `sse` | [net](./net/README.md) |
|
|
162
|
+
| Talk between tabs | `broadcastMessages`, `SharedHost` | [net](./net/README.md) |
|
|
163
|
+
| Use the camera, the location or a permission | `mediaStream`, `Geolocation`, `permissions` | [device](./device/README.md) |
|
|
164
|
+
| Track one element's size or visibility | `Resize`, `Intersection`, `Bounds` | [observers](./observers/README.md) |
|
|
165
|
+
| Focus, mask or keep an element in view | `Autofocus`, `InputMask`, `KeepInView` | [dom](./dom/README.md) |
|
|
166
|
+
| Copy, share, load a script or restore scroll | `copyText`, `share`, `loadScript`, `keepScroll` | [dom](./dom/README.md) |
|
|
167
|
+
|
|
168
|
+
Working widgets built from the interaction primitives are listed under
|
|
169
|
+
[In the examples](./interaction/README.md#in-the-examples).
|
|
170
|
+
|
|
141
171
|
## The map
|
|
142
172
|
|
|
143
173
|
Each subpath is one concern and one import. Its README has the full
|
|
@@ -154,7 +184,7 @@ reference: every Model shape, Message, argument, and failure rule.
|
|
|
154
184
|
| [`device`](./device/README.md) | hardware | `Geolocation`, `mediaDevices`, `mediaStream`, `permissions`, fullscreen |
|
|
155
185
|
| [`events`](./events/README.md) | raw window events | `Visibility`, `WindowSize`, `Idle`, `InputModality`, `keyboardEvents`, `pointerEvents`, `scrollEvents`, `activeElementEvents`, `matchHotkey` |
|
|
156
186
|
| [`observers`](./observers/README.md) | element observers | `Resize`, `Intersection`, `Mutation`, `Bounds` |
|
|
157
|
-
| [`dom`](./dom/README.md) | element Mounts and one-shot Commands | `Autofocus`, `InputMask`, `KeepInView`, `Measure`, `FocusScope`, `FollowTabStop`, `ScrollLock`, `HideOutside`, `copyText`, `readText`, `share`, `loadScript`, `keepScroll` |
|
|
187
|
+
| [`dom`](./dom/README.md) | element Mounts and one-shot Commands | `Autofocus`, `InputMask`, `KeepInView`, `Measure`, `FocusScope`, `FollowTabStop`, `ScrollLock`, `HideOutside`, `copyText`, `readText`, `share`, `loadScript`, `keepScroll`, `pointedLinks` |
|
|
158
188
|
|
|
159
189
|
## Rules every primitive keeps
|
|
160
190
|
|
package/dist/device/index.d.mts
CHANGED
|
@@ -5,12 +5,12 @@ import { Command } from "foldkit/command";
|
|
|
5
5
|
//#region src/device/fullscreen.d.ts
|
|
6
6
|
export declare const FullscreenMessage: import("foldkit/message").MessageUnion<{
|
|
7
7
|
readonly Changed: {
|
|
8
|
-
|
|
8
|
+
active: Schema.Boolean;
|
|
9
9
|
};
|
|
10
10
|
readonly Entered: {};
|
|
11
11
|
readonly Exited: {};
|
|
12
12
|
readonly Failed: {
|
|
13
|
-
|
|
13
|
+
message: Schema.String;
|
|
14
14
|
};
|
|
15
15
|
}>;
|
|
16
16
|
export type FullscreenMessage = typeof FullscreenMessage.Type;
|
|
@@ -48,18 +48,24 @@ export declare const GeolocationModel: Schema.Struct<{
|
|
|
48
48
|
export type GeolocationModel = typeof GeolocationModel.Type;
|
|
49
49
|
export declare const GeolocationMessage: import("foldkit/message").MessageUnion<{
|
|
50
50
|
readonly Located: {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
latitude: Schema.Number;
|
|
52
|
+
longitude: Schema.Number;
|
|
53
|
+
accuracy: Schema.Number;
|
|
54
54
|
};
|
|
55
55
|
readonly Denied: {};
|
|
56
56
|
readonly Failed: {
|
|
57
|
-
|
|
57
|
+
message: Schema.String;
|
|
58
58
|
};
|
|
59
59
|
}>;
|
|
60
60
|
export type GeolocationMessage = typeof GeolocationMessage.Type;
|
|
61
|
+
/**
|
|
62
|
+
* Geolocation as a bundle: the device position in the Model, watched while
|
|
63
|
+
* placed. Permission denial is its own status (actionable UI); transient
|
|
64
|
+
* failures keep the last fix and note the error. No geolocation API yields
|
|
65
|
+
* an empty stream instead of throwing.
|
|
66
|
+
*/
|
|
61
67
|
export declare const Geolocation: Bundle.Bundle<"Geolocation", void, {
|
|
62
|
-
readonly status: "
|
|
68
|
+
readonly status: "denied" | "ready" | "unknown";
|
|
63
69
|
readonly coords: {
|
|
64
70
|
readonly latitude: number;
|
|
65
71
|
readonly longitude: number;
|
|
@@ -67,6 +73,8 @@ export declare const Geolocation: Bundle.Bundle<"Geolocation", void, {
|
|
|
67
73
|
} | null;
|
|
68
74
|
readonly lastError: string | null;
|
|
69
75
|
}, {
|
|
76
|
+
readonly _tag: "Denied";
|
|
77
|
+
} | {
|
|
70
78
|
readonly _tag: "Failed";
|
|
71
79
|
readonly message: string;
|
|
72
80
|
} | {
|
|
@@ -74,8 +82,6 @@ export declare const Geolocation: Bundle.Bundle<"Geolocation", void, {
|
|
|
74
82
|
readonly latitude: number;
|
|
75
83
|
readonly longitude: number;
|
|
76
84
|
readonly accuracy: number;
|
|
77
|
-
} | {
|
|
78
|
-
readonly _tag: "Denied";
|
|
79
85
|
}, never, never, never, void, {}, {}>;
|
|
80
86
|
//#endregion
|
|
81
87
|
//#region src/device/media-devices.d.ts
|
|
@@ -101,7 +107,7 @@ export declare const MediaDevicesMessage: import("foldkit/message").MessageUnion
|
|
|
101
107
|
readonly Scan: {};
|
|
102
108
|
readonly DevicesChanged: {};
|
|
103
109
|
readonly Refreshed: {
|
|
104
|
-
|
|
110
|
+
devices: Schema.$Array<Schema.Struct<{
|
|
105
111
|
readonly deviceId: Schema.String;
|
|
106
112
|
readonly groupId: Schema.String;
|
|
107
113
|
readonly kind: Schema.Literals<readonly ["audioinput", "audiooutput", "videoinput"]>;
|
|
@@ -110,7 +116,7 @@ export declare const MediaDevicesMessage: import("foldkit/message").MessageUnion
|
|
|
110
116
|
};
|
|
111
117
|
readonly Denied: {};
|
|
112
118
|
readonly Failed: {
|
|
113
|
-
|
|
119
|
+
message: Schema.String;
|
|
114
120
|
};
|
|
115
121
|
}>;
|
|
116
122
|
export type MediaDevicesMessage = typeof MediaDevicesMessage.Type;
|
|
@@ -125,12 +131,20 @@ export interface MediaDevicesHandle {
|
|
|
125
131
|
addEventListener(type: string, listener: () => void): void;
|
|
126
132
|
removeEventListener(type: string, listener: () => void): void;
|
|
127
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Media devices as a bundle factory: the enumerated device list in the
|
|
136
|
+
* Model, scanned on placement and re-scanned on every `devicechange`.
|
|
137
|
+
* Denial lands as `denied` while keeping the last list (actionable UI over
|
|
138
|
+
* stale data, like Geolocation's last fix); any other failure keeps the
|
|
139
|
+
* list and notes the error. No media-devices API yields an empty stream
|
|
140
|
+
* and a `Failed` scan instead of throwing.
|
|
141
|
+
*/
|
|
128
142
|
export declare const mediaDevices: <const Name extends string>(config: {
|
|
129
143
|
readonly name: Name;
|
|
130
144
|
/** Read lazily so tests can substitute a double, like the SSE bundle. */
|
|
131
145
|
readonly create?: ((navigator: Navigator) => MediaDevicesHandle | null) | undefined;
|
|
132
146
|
}) => Bundle.Bundle<Name, void, {
|
|
133
|
-
readonly status: "
|
|
147
|
+
readonly status: "denied" | "ready" | "unknown";
|
|
134
148
|
readonly devices: readonly {
|
|
135
149
|
readonly deviceId: string;
|
|
136
150
|
readonly groupId: string;
|
|
@@ -140,13 +154,11 @@ export declare const mediaDevices: <const Name extends string>(config: {
|
|
|
140
154
|
readonly lastError: string | null;
|
|
141
155
|
}, {
|
|
142
156
|
readonly _tag: "Denied";
|
|
157
|
+
} | {
|
|
158
|
+
readonly _tag: "DevicesChanged";
|
|
143
159
|
} | {
|
|
144
160
|
readonly _tag: "Failed";
|
|
145
161
|
readonly message: string;
|
|
146
|
-
} | {
|
|
147
|
-
readonly _tag: "Scan";
|
|
148
|
-
} | {
|
|
149
|
-
readonly _tag: "DevicesChanged";
|
|
150
162
|
} | {
|
|
151
163
|
readonly _tag: "Refreshed";
|
|
152
164
|
readonly devices: readonly {
|
|
@@ -155,6 +167,8 @@ export declare const mediaDevices: <const Name extends string>(config: {
|
|
|
155
167
|
readonly kind: "audioinput" | "audiooutput" | "videoinput";
|
|
156
168
|
readonly label: string;
|
|
157
169
|
}[];
|
|
170
|
+
} | {
|
|
171
|
+
readonly _tag: "Scan";
|
|
158
172
|
}, never, never, never, void, {}, {}>;
|
|
159
173
|
//#endregion
|
|
160
174
|
//#region src/device/media-stream.d.ts
|
|
@@ -170,7 +184,7 @@ export declare const MediaStreamMessage: import("foldkit/message").MessageUnion<
|
|
|
170
184
|
readonly Ended: {};
|
|
171
185
|
readonly Denied: {};
|
|
172
186
|
readonly Failed: {
|
|
173
|
-
|
|
187
|
+
message: Schema.String;
|
|
174
188
|
};
|
|
175
189
|
}>;
|
|
176
190
|
export type MediaStreamMessage = typeof MediaStreamMessage.Type;
|
|
@@ -182,6 +196,7 @@ export interface MediaTrackHandle {
|
|
|
182
196
|
export interface MediaStreamHandle {
|
|
183
197
|
getTracks(): ReadonlyArray<MediaTrackHandle>;
|
|
184
198
|
}
|
|
199
|
+
/** Which tracks `mediaStream` asks `getUserMedia` for. */
|
|
185
200
|
export interface MediaStreamConstraints {
|
|
186
201
|
readonly audio: boolean;
|
|
187
202
|
readonly video: boolean;
|
|
@@ -190,6 +205,15 @@ export interface MediaStreamConstraints {
|
|
|
190
205
|
export declare const LiveStream: ManagedResource.ManagedResource<MediaStreamHandle, ManagedResource.ManagedResourceService<"media-stream">>;
|
|
191
206
|
/** The stream service a placed camera requires. */
|
|
192
207
|
export type LiveStreamService = ManagedResource.ServiceOf<typeof LiveStream>;
|
|
208
|
+
/**
|
|
209
|
+
* A camera/mic stream as a bundle factory: one live MediaStream held in a
|
|
210
|
+
* Managed Resource while the Model asks for it. `Started` requests,
|
|
211
|
+
* `Stopped` releases; denial parks at `denied` (actionable UI), any other
|
|
212
|
+
* acquire failure parks at `idle` with the error noted — both clear
|
|
213
|
+
* requirements, so a failing device never spins an acquire loop. Release
|
|
214
|
+
* stops every track. No media-devices API fails the acquire instead of
|
|
215
|
+
* throwing.
|
|
216
|
+
*/
|
|
193
217
|
export declare const mediaStream: <const Name extends string>(config: {
|
|
194
218
|
readonly name: Name;
|
|
195
219
|
/** Read lazily so tests can substitute a double, like the SSE bundle. */
|
|
@@ -198,7 +222,7 @@ export declare const mediaStream: <const Name extends string>(config: {
|
|
|
198
222
|
readonly audio: boolean;
|
|
199
223
|
readonly video: boolean;
|
|
200
224
|
}, {
|
|
201
|
-
readonly status: "denied" | "idle" | "
|
|
225
|
+
readonly status: "denied" | "idle" | "live" | "requesting";
|
|
202
226
|
readonly lastError: string | null;
|
|
203
227
|
}, {
|
|
204
228
|
readonly _tag: "Started";
|
|
@@ -207,15 +231,15 @@ export declare const mediaStream: <const Name extends string>(config: {
|
|
|
207
231
|
} | {
|
|
208
232
|
readonly _tag: "Denied";
|
|
209
233
|
} | {
|
|
210
|
-
readonly _tag: "
|
|
234
|
+
readonly _tag: "Ended";
|
|
211
235
|
} | {
|
|
212
236
|
readonly _tag: "Failed";
|
|
213
237
|
readonly message: string;
|
|
214
238
|
} | {
|
|
215
|
-
readonly _tag: "
|
|
239
|
+
readonly _tag: "Live";
|
|
216
240
|
}, never, never, never, void, {
|
|
217
241
|
stream: ManagedResource.Entry<{
|
|
218
|
-
readonly status: "denied" | "idle" | "
|
|
242
|
+
readonly status: "denied" | "idle" | "live" | "requesting";
|
|
219
243
|
readonly lastError: string | null;
|
|
220
244
|
}, {
|
|
221
245
|
readonly _tag: "Started";
|
|
@@ -224,12 +248,12 @@ export declare const mediaStream: <const Name extends string>(config: {
|
|
|
224
248
|
} | {
|
|
225
249
|
readonly _tag: "Denied";
|
|
226
250
|
} | {
|
|
227
|
-
readonly _tag: "
|
|
251
|
+
readonly _tag: "Ended";
|
|
228
252
|
} | {
|
|
229
253
|
readonly _tag: "Failed";
|
|
230
254
|
readonly message: string;
|
|
231
255
|
} | {
|
|
232
|
-
readonly _tag: "
|
|
256
|
+
readonly _tag: "Live";
|
|
233
257
|
}, Option.Option<{
|
|
234
258
|
readonly audio: boolean;
|
|
235
259
|
readonly video: boolean;
|
|
@@ -248,15 +272,15 @@ export declare const PermissionsModel: Schema.Struct<{
|
|
|
248
272
|
export type PermissionsModel = typeof PermissionsModel.Type;
|
|
249
273
|
export declare const PermissionsMessage: import("foldkit/message").MessageUnion<{
|
|
250
274
|
readonly Snapshot: {
|
|
251
|
-
|
|
275
|
+
states: Schema.$Record<Schema.String, Schema.Literals<readonly ["granted", "denied", "prompt"]>>;
|
|
252
276
|
};
|
|
253
277
|
readonly Changed: {
|
|
254
|
-
|
|
255
|
-
|
|
278
|
+
name: Schema.String;
|
|
279
|
+
state: Schema.Literals<readonly ["granted", "denied", "prompt"]>;
|
|
256
280
|
};
|
|
257
281
|
readonly Cleared: {};
|
|
258
282
|
readonly Failed: {
|
|
259
|
-
|
|
283
|
+
message: Schema.String;
|
|
260
284
|
};
|
|
261
285
|
}>;
|
|
262
286
|
export type PermissionsMessage = typeof PermissionsMessage.Type;
|
|
@@ -280,6 +304,14 @@ interface Acquired {
|
|
|
280
304
|
export declare const Watch: ManagedResource.ManagedResource<Acquired, ManagedResource.ManagedResourceService<"permissions">>;
|
|
281
305
|
/** The watch service a placed Permissions bundle requires. */
|
|
282
306
|
export type WatchService = ManagedResource.ServiceOf<typeof Watch>;
|
|
307
|
+
/**
|
|
308
|
+
* Permission states as a bundle factory: queried on placement, kept current
|
|
309
|
+
* by each status object's `onchange`. A resource holds the status objects
|
|
310
|
+
* (they cannot live in the Model); its queue feeds the subscription, and
|
|
311
|
+
* release detaches every handler. An unknown permission name fails the
|
|
312
|
+
* acquire; an unknown state string fails it too — both become `Failed`,
|
|
313
|
+
* never Model facts.
|
|
314
|
+
*/
|
|
283
315
|
export declare const permissions: <const Name extends string>(config: {
|
|
284
316
|
readonly name: Name;
|
|
285
317
|
/** Read lazily so tests can substitute a double, like the SSE bundle. */
|
|
@@ -288,7 +320,7 @@ export declare const permissions: <const Name extends string>(config: {
|
|
|
288
320
|
readonly names: ReadonlyArray<string>;
|
|
289
321
|
}, {
|
|
290
322
|
readonly states: {
|
|
291
|
-
readonly [x: string]: "
|
|
323
|
+
readonly [x: string]: "denied" | "granted" | "prompt";
|
|
292
324
|
};
|
|
293
325
|
readonly lastError: string | null;
|
|
294
326
|
}, {
|
|
@@ -296,17 +328,18 @@ export declare const permissions: <const Name extends string>(config: {
|
|
|
296
328
|
} | {
|
|
297
329
|
readonly _tag: "Changed";
|
|
298
330
|
readonly name: string;
|
|
299
|
-
readonly state: "
|
|
331
|
+
readonly state: "denied" | "granted" | "prompt";
|
|
332
|
+
} | {
|
|
333
|
+
readonly _tag: "Failed";
|
|
334
|
+
readonly message: string;
|
|
300
335
|
} | {
|
|
301
336
|
readonly _tag: "Snapshot";
|
|
302
337
|
readonly states: {
|
|
303
|
-
readonly [x: string]: "
|
|
338
|
+
readonly [x: string]: "denied" | "granted" | "prompt";
|
|
304
339
|
};
|
|
305
|
-
} | {
|
|
306
|
-
readonly _tag: "Failed";
|
|
307
|
-
readonly message: string;
|
|
308
340
|
}, never, never, ManagedResource.ManagedResourceService<"permissions">, void, Readonly<{
|
|
309
341
|
watch: ManagedResource.Entry<PermissionsModel, PermissionsMessage, Option.Option<ReadonlyArray<string>>, Acquired, WatchService, (acquired: Acquired) => PermissionsMessage>;
|
|
310
342
|
}>, {}>;
|
|
311
343
|
//#endregion
|
|
344
|
+
export {};
|
|
312
345
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/device/fullscreen.ts","../../src/device/geolocation.ts","../../src/device/media-devices.ts","../../src/device/media-stream.ts","../../src/device/permissions.ts"],"mappings":";;;;;qBAWa,6CAAiB
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/device/fullscreen.ts","../../src/device/geolocation.ts","../../src/device/media-devices.ts","../../src/device/media-stream.ts","../../src/device/permissions.ts"],"mappings":";;;;;qBAWa,6CAAiB;;IACjB,QAAM,OAAA;;;;;IAGP,SAAO,OAAA;;;YAEP,2BAA2B,kBAAkB;;;;;;;qBAW5C,kBAAe,SAAa,YAAU,QAAQ;;qBA0B9C,sBAAqB,QAAQ;;;;;qBAgC7B,yBAAwB,OAAO,OAAO;;;qBC/EtC,aAAW,OAAA;;;;;YAKZ,qBAAqB,YAAY;qBAEhC,kBAAgB,OAAA;;;;;;;;;YAKjB,0BAA0B,iBAAiB;qBAE1C,8CAAkB;;IAE3B,UAAQ,OAAA;IACR,WAAS,OAAA;IACT,UAAQ,OAAA;;;;IAGA,SAAO,OAAA;;;YAEP,4BAA4B,mBAAmB;;;;;;;qBAyD9C,aAAW,OAAA;;;;;;;;;;;;;;;;;;;;;qBChFX,aAAW,OAAA;;;;;;YAMZ,qBAAqB,YAAY;qBAEhC,mBAAiB,OAAA;;;;;;;;;;YAKlB,2BAA2B,kBAAkB;qBAE5C,+CAAmB;;;;IAGjB,SAAO,OAAA,OAAA,OAAA;;;;;;;;;IAEV,SAAO,OAAA;;;YAEP,6BAA6B,oBAAoB;;iBAa5C;EACf,oBAAoB,QAClB;aACW;aACA;aACA;aACA;;EAGb,iBAAiB,cAAc;EAC/B,oBAAoB,cAAc;;;;;;;;;;qBAkBvB,qBAAsB,qBAAmB;WAC3C,MAAM;;WAEN,WAAW,WAAW,cAAc;MAC9C,OAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBCnEY,kBAAgB,OAAA;;;;YAIjB,0BAA0B,iBAAiB;qBAE1C,8CAAkB;;;;;;;IAMnB,SAAO,OAAA;;;YAEP,4BAA4B,mBAAmB;;iBAG1C;EACf;;;iBAIe;EACf,aAAa,cAAc;;;iBAIZ;WACN;WACA;;;qBAIE,YAAU,gBAAA,gBAAA,mBAAA,gBAAA;;YAGX,oBAAoB,gBAAgB,iBAAiB;;;;;;;;;;qBA6BpD,oBAAqB,qBAAmB;WAC1C,MAAM;;WAEN,YACL,aAAa,2BAA2B,QAAQ;MACrD,OAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBCrEY,iBAAe,OAAA;YAChB,yBAAyB,gBAAgB;qBAExC,kBAAgB,OAAA;;;;YAIjB,0BAA0B,iBAAiB;qBAE1C,8CAAkB;;IACjB,QAAM,OAAA,QAAA,OAAA,QAAA,OAAA;;;IACP,MAAI,OAAA;IAAiB,OAAK,OAAA;;;;IAE3B,SAAO,OAAA;;;YAEP,4BAA4B,mBAAmB;;iBAG1C;WACN;EACT;;;iBAIe;EACf,MAAM;aAAuB;MAAiB,QAAQ;;UAG9C;WACC,UAAU,oBAAoB;WAC9B,UAAU,SAAS,eAAe;WAClC,QAAQ,MAAM,MAAM;;;qBAIlB,OAAK,gBAAA,gBAAA,UAAA,gBAAA;;YAGN,eAAe,gBAAgB,iBAAiB;;;;;;;;;qBA+B/C,oBAAqB,qBAAmB;WAC1C,MAAM;;WAEN,WAAW,WAAW,cAAc;MAC9C,OAAA,OAAA;WAYqB,OAAA;;;;;;;;;;;;;;;;;;;;;EA5Cb,OAAA,gBAAgB,MACrB,kBACA,oBACA,OAAO,OAAO,wBACd,UACA,eACC,UAAU,aAAa"}
|
package/dist/device/index.mjs
CHANGED
|
@@ -70,12 +70,6 @@ const fullscreenChanges = () => {
|
|
|
70
70
|
};
|
|
71
71
|
//#endregion
|
|
72
72
|
//#region src/device/geolocation.ts
|
|
73
|
-
/**
|
|
74
|
-
* Geolocation as a bundle: the device position in the Model, watched while
|
|
75
|
-
* placed. Permission denial is its own status (actionable UI); transient
|
|
76
|
-
* failures keep the last fix and note the error. No geolocation API yields
|
|
77
|
-
* an empty stream instead of throwing.
|
|
78
|
-
*/
|
|
79
73
|
const Coordinates = Schema.Struct({
|
|
80
74
|
latitude: Schema.Number,
|
|
81
75
|
longitude: Schema.Number,
|
|
@@ -119,6 +113,12 @@ const watchStream = () => {
|
|
|
119
113
|
return Stream.fromQueue(queue);
|
|
120
114
|
}));
|
|
121
115
|
};
|
|
116
|
+
/**
|
|
117
|
+
* Geolocation as a bundle: the device position in the Model, watched while
|
|
118
|
+
* placed. Permission denial is its own status (actionable UI); transient
|
|
119
|
+
* failures keep the last fix and note the error. No geolocation API yields
|
|
120
|
+
* an empty stream instead of throwing.
|
|
121
|
+
*/
|
|
122
122
|
const Geolocation = Bundle.make("Geolocation", {
|
|
123
123
|
Model: GeolocationModel,
|
|
124
124
|
Message: GeolocationMessage,
|
|
@@ -146,18 +146,10 @@ const Geolocation = Bundle.make("Geolocation", {
|
|
|
146
146
|
lastError: message
|
|
147
147
|
}) })
|
|
148
148
|
}),
|
|
149
|
-
subscriptions: () => Subscription.make()(() => ({ watch: Subscription.
|
|
149
|
+
subscriptions: () => Subscription.make()(() => ({ watch: Subscription.persistentEntry(watchStream()) }))
|
|
150
150
|
});
|
|
151
151
|
//#endregion
|
|
152
152
|
//#region src/device/media-devices.ts
|
|
153
|
-
/**
|
|
154
|
-
* Media devices as a bundle factory: the enumerated device list in the
|
|
155
|
-
* Model, scanned on placement and re-scanned on every `devicechange`.
|
|
156
|
-
* Denial lands as `denied` while keeping the last list (actionable UI over
|
|
157
|
-
* stale data, like Geolocation's last fix); any other failure keeps the
|
|
158
|
-
* list and notes the error. No media-devices API yields an empty stream
|
|
159
|
-
* and a `Failed` scan instead of throwing.
|
|
160
|
-
*/
|
|
161
153
|
const MediaDevice = Schema.Struct({
|
|
162
154
|
deviceId: Schema.String,
|
|
163
155
|
groupId: Schema.String,
|
|
@@ -187,6 +179,14 @@ const MediaDevicesMessage = defineMessageUnion({
|
|
|
187
179
|
const sameDevices = (a, b) => a.length === b.length && a.every((device, index) => device.deviceId === b[index].deviceId && device.groupId === b[index].groupId && device.kind === b[index].kind && device.label === b[index].label);
|
|
188
180
|
const failMessage$2 = (error) => error instanceof Error ? error.message : String(error);
|
|
189
181
|
const isDenied$1 = (error) => typeof error?.name === "string" && ["NotAllowedError", "SecurityError"].includes(error.name);
|
|
182
|
+
/**
|
|
183
|
+
* Media devices as a bundle factory: the enumerated device list in the
|
|
184
|
+
* Model, scanned on placement and re-scanned on every `devicechange`.
|
|
185
|
+
* Denial lands as `denied` while keeping the last list (actionable UI over
|
|
186
|
+
* stale data, like Geolocation's last fix); any other failure keeps the
|
|
187
|
+
* list and notes the error. No media-devices API yields an empty stream
|
|
188
|
+
* and a `Failed` scan instead of throwing.
|
|
189
|
+
*/
|
|
190
190
|
const mediaDevices = (config) => {
|
|
191
191
|
const handle = () => {
|
|
192
192
|
if (typeof navigator === "undefined") return null;
|
|
@@ -255,20 +255,11 @@ const mediaDevices = (config) => {
|
|
|
255
255
|
lastError: message
|
|
256
256
|
}) })
|
|
257
257
|
}),
|
|
258
|
-
subscriptions: () => Subscription.make()(() => ({ changes: Subscription.
|
|
258
|
+
subscriptions: () => Subscription.make()(() => ({ changes: Subscription.persistentEntry(changedStream()) }))
|
|
259
259
|
});
|
|
260
260
|
};
|
|
261
261
|
//#endregion
|
|
262
262
|
//#region src/device/media-stream.ts
|
|
263
|
-
/**
|
|
264
|
-
* A camera/mic stream as a bundle factory: one live MediaStream held in a
|
|
265
|
-
* Managed Resource while the Model asks for it. `Started` requests,
|
|
266
|
-
* `Stopped` releases; denial parks at `denied` (actionable UI), any other
|
|
267
|
-
* acquire failure parks at `idle` with the error noted — both clear
|
|
268
|
-
* requirements, so a failing device never spins an acquire loop. Release
|
|
269
|
-
* stops every track. No media-devices API fails the acquire instead of
|
|
270
|
-
* throwing.
|
|
271
|
-
*/
|
|
272
263
|
const MediaStreamModel = Schema.Struct({
|
|
273
264
|
status: Schema.Literals([
|
|
274
265
|
"idle",
|
|
@@ -290,6 +281,15 @@ const MediaStreamMessage = defineMessageUnion({
|
|
|
290
281
|
const LiveStream = ManagedResource.tag()("media-stream");
|
|
291
282
|
const failMessage$1 = (error) => error instanceof Error ? error.message : String(error);
|
|
292
283
|
const isDenied = (error) => typeof error?.name === "string" && ["NotAllowedError", "SecurityError"].includes(error.name);
|
|
284
|
+
/**
|
|
285
|
+
* A camera/mic stream as a bundle factory: one live MediaStream held in a
|
|
286
|
+
* Managed Resource while the Model asks for it. `Started` requests,
|
|
287
|
+
* `Stopped` releases; denial parks at `denied` (actionable UI), any other
|
|
288
|
+
* acquire failure parks at `idle` with the error noted — both clear
|
|
289
|
+
* requirements, so a failing device never spins an acquire loop. Release
|
|
290
|
+
* stops every track. No media-devices API fails the acquire instead of
|
|
291
|
+
* throwing.
|
|
292
|
+
*/
|
|
293
293
|
const mediaStream = (config) => {
|
|
294
294
|
const request = (constraints) => {
|
|
295
295
|
if (config.request !== void 0) return config.request(constraints);
|
|
@@ -359,14 +359,6 @@ const mediaStream = (config) => {
|
|
|
359
359
|
};
|
|
360
360
|
//#endregion
|
|
361
361
|
//#region src/device/permissions.ts
|
|
362
|
-
/**
|
|
363
|
-
* Permission states as a bundle factory: queried on placement, kept current
|
|
364
|
-
* by each status object's `onchange`. A resource holds the status objects
|
|
365
|
-
* (they cannot live in the Model); its queue feeds the subscription, and
|
|
366
|
-
* release detaches every handler. An unknown permission name fails the
|
|
367
|
-
* acquire; an unknown state string fails it too — both become `Failed`,
|
|
368
|
-
* never Model facts.
|
|
369
|
-
*/
|
|
370
362
|
const PermissionState = Schema.Literals([
|
|
371
363
|
"granted",
|
|
372
364
|
"denied",
|
|
@@ -392,6 +384,14 @@ const readState = (state, name) => {
|
|
|
392
384
|
if (state !== "granted" && state !== "denied" && state !== "prompt") throw new Error(`unknown permission state for ${name}: ${state}`);
|
|
393
385
|
return state;
|
|
394
386
|
};
|
|
387
|
+
/**
|
|
388
|
+
* Permission states as a bundle factory: queried on placement, kept current
|
|
389
|
+
* by each status object's `onchange`. A resource holds the status objects
|
|
390
|
+
* (they cannot live in the Model); its queue feeds the subscription, and
|
|
391
|
+
* release detaches every handler. An unknown permission name fails the
|
|
392
|
+
* acquire; an unknown state string fails it too — both become `Failed`,
|
|
393
|
+
* never Model facts.
|
|
394
|
+
*/
|
|
395
395
|
const permissions = (config) => {
|
|
396
396
|
const handle = () => {
|
|
397
397
|
if (typeof navigator === "undefined") return null;
|
|
@@ -486,7 +486,7 @@ const permissions = (config) => {
|
|
|
486
486
|
}), Queue.shutdown(events)))
|
|
487
487
|
}) }));
|
|
488
488
|
},
|
|
489
|
-
subscriptions: () => Subscription.make()(() => ({ changes: Subscription.
|
|
489
|
+
subscriptions: () => Subscription.make()(() => ({ changes: Subscription.persistentEntry(Stream.unwrap(Effect.matchEffect(Watch.get, {
|
|
490
490
|
onFailure: () => Effect.succeed(Stream.empty),
|
|
491
491
|
onSuccess: ({ events }) => Effect.succeed(Stream.fromQueue(events))
|
|
492
492
|
}))) }))
|