foldkit-primitives 0.15.4 → 0.16.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/dist/device/fullscreen.d.ts +28 -0
- package/dist/device/fullscreen.d.ts.map +1 -0
- package/dist/device/fullscreen.js +69 -0
- package/dist/device/fullscreen.js.map +1 -0
- package/dist/device/geolocation.d.ts +56 -0
- package/dist/device/geolocation.d.ts.map +1 -0
- package/dist/device/geolocation.js +87 -0
- package/dist/device/geolocation.js.map +1 -0
- package/dist/device/index.d.ts +5 -0
- package/dist/device/index.js +6 -0
- package/dist/device/media-devices.d.ts +88 -0
- package/dist/device/media-devices.d.ts.map +1 -0
- package/dist/device/media-devices.js +117 -0
- package/dist/device/media-devices.js.map +1 -0
- package/dist/device/media-stream.d.ts +93 -0
- package/dist/device/media-stream.d.ts.map +1 -0
- package/dist/device/media-stream.js +106 -0
- package/dist/device/media-stream.js.map +1 -0
- package/dist/device/permissions.d.ts +106 -0
- package/dist/device/permissions.d.ts.map +1 -0
- package/dist/device/permissions.js +143 -0
- package/dist/device/permissions.js.map +1 -0
- package/dist/dom/autofocus.d.ts +15 -0
- package/dist/dom/autofocus.d.ts.map +1 -0
- package/dist/dom/autofocus.js +22 -0
- package/dist/dom/autofocus.js.map +1 -0
- package/dist/dom/clipboard.d.ts +30 -0
- package/dist/dom/clipboard.d.ts.map +1 -0
- package/dist/dom/clipboard.js +50 -0
- package/dist/dom/clipboard.js.map +1 -0
- package/dist/dom/editable-text.d.ts +72 -0
- package/dist/dom/editable-text.d.ts.map +1 -0
- package/dist/dom/editable-text.js +232 -0
- package/dist/dom/editable-text.js.map +1 -0
- package/dist/dom/fluid-hover.d.ts +93 -0
- package/dist/dom/fluid-hover.d.ts.map +1 -0
- package/dist/dom/fluid-hover.js +294 -0
- package/dist/dom/fluid-hover.js.map +1 -0
- package/dist/dom/focus-scope.d.ts +21 -0
- package/dist/dom/focus-scope.d.ts.map +1 -0
- package/dist/dom/focus-scope.js +93 -0
- package/dist/dom/focus-scope.js.map +1 -0
- package/dist/dom/follow-tab-stop.d.ts +11 -0
- package/dist/dom/follow-tab-stop.d.ts.map +1 -0
- package/dist/dom/follow-tab-stop.js +54 -0
- package/dist/dom/follow-tab-stop.js.map +1 -0
- package/dist/dom/image-decode.d.ts +27 -0
- package/dist/dom/image-decode.d.ts.map +1 -0
- package/dist/dom/image-decode.js +50 -0
- package/dist/dom/image-decode.js.map +1 -0
- package/dist/dom/index.d.ts +24 -0
- package/dist/dom/index.js +25 -0
- package/dist/dom/input-mask.d.ts +35 -0
- package/dist/dom/input-mask.d.ts.map +1 -0
- package/dist/dom/input-mask.js +90 -0
- package/dist/dom/input-mask.js.map +1 -0
- package/dist/dom/keep-in-view.d.ts +16 -0
- package/dist/dom/keep-in-view.d.ts.map +1 -0
- package/dist/dom/keep-in-view.js +38 -0
- package/dist/dom/keep-in-view.js.map +1 -0
- package/dist/dom/keyboard-inset.d.ts +29 -0
- package/dist/dom/keyboard-inset.d.ts.map +1 -0
- package/dist/dom/keyboard-inset.js +45 -0
- package/dist/dom/keyboard-inset.js.map +1 -0
- package/dist/dom/layers.d.ts +6 -0
- package/dist/dom/layers.d.ts.map +1 -0
- package/dist/dom/layers.js +35 -0
- package/dist/dom/layers.js.map +1 -0
- package/dist/dom/measure.d.ts +42 -0
- package/dist/dom/measure.d.ts.map +1 -0
- package/dist/dom/measure.js +123 -0
- package/dist/dom/measure.js.map +1 -0
- package/dist/dom/move.d.ts +45 -0
- package/dist/dom/move.d.ts.map +1 -0
- package/dist/dom/move.js +96 -0
- package/dist/dom/move.js.map +1 -0
- package/dist/dom/pointed-links.d.ts +13 -0
- package/dist/dom/pointed-links.d.ts.map +1 -0
- package/dist/dom/pointed-links.js +59 -0
- package/dist/dom/pointed-links.js.map +1 -0
- package/dist/dom/pointer-drag.d.ts +132 -0
- package/dist/dom/pointer-drag.d.ts.map +1 -0
- package/dist/dom/pointer-drag.js +307 -0
- package/dist/dom/pointer-drag.js.map +1 -0
- package/dist/dom/pointer-position.d.ts +133 -0
- package/dist/dom/pointer-position.d.ts.map +1 -0
- package/dist/dom/pointer-position.js +173 -0
- package/dist/dom/pointer-position.js.map +1 -0
- package/dist/dom/script-loader.d.ts +12 -0
- package/dist/dom/script-loader.d.ts.map +1 -0
- package/dist/dom/script-loader.js +54 -0
- package/dist/dom/script-loader.js.map +1 -0
- package/dist/dom/scroll-bars.d.ts +52 -0
- package/dist/dom/scroll-bars.d.ts.map +1 -0
- package/dist/dom/scroll-bars.js +259 -0
- package/dist/dom/scroll-bars.js.map +1 -0
- package/dist/dom/scroll-settle.d.ts +33 -0
- package/dist/dom/scroll-settle.d.ts.map +1 -0
- package/dist/dom/scroll-settle.js +84 -0
- package/dist/dom/scroll-settle.js.map +1 -0
- package/dist/dom/scroll.d.ts +21 -0
- package/dist/dom/scroll.d.ts.map +1 -0
- package/dist/dom/scroll.js +141 -0
- package/dist/dom/scroll.js.map +1 -0
- package/dist/dom/share.d.ts +19 -0
- package/dist/dom/share.d.ts.map +1 -0
- package/dist/dom/share.js +41 -0
- package/dist/dom/share.js.map +1 -0
- package/dist/dom/swipe.d.ts +121 -0
- package/dist/dom/swipe.d.ts.map +1 -0
- package/dist/dom/swipe.js +291 -0
- package/dist/dom/swipe.js.map +1 -0
- package/dist/dom/targets.d.ts +45 -0
- package/dist/dom/targets.d.ts.map +1 -0
- package/dist/dom/targets.js +70 -0
- package/dist/dom/targets.js.map +1 -0
- package/dist/dom/track-box.d.ts +61 -0
- package/dist/dom/track-box.d.ts.map +1 -0
- package/dist/dom/track-box.js +131 -0
- package/dist/dom/track-box.js.map +1 -0
- package/dist/events/active-element.d.ts +15 -0
- package/dist/events/active-element.d.ts.map +1 -0
- package/dist/events/active-element.js +33 -0
- package/dist/events/active-element.js.map +1 -0
- package/dist/events/hotkeys.d.ts +22 -0
- package/dist/events/hotkeys.d.ts.map +1 -0
- package/dist/events/hotkeys.js +56 -0
- package/dist/events/hotkeys.js.map +1 -0
- package/dist/events/idle.d.ts +29 -0
- package/dist/events/idle.d.ts.map +1 -0
- package/dist/events/idle.js +51 -0
- package/dist/events/idle.js.map +1 -0
- package/dist/events/index.d.ts +9 -0
- package/dist/events/index.js +10 -0
- package/dist/{input-modality-l7InESYh.d.mts → events/input-modality.d.ts} +9 -10
- package/dist/events/input-modality.d.ts.map +1 -0
- package/dist/{input-modality-XwQOFLip.mjs → events/input-modality.js} +2 -2
- package/dist/events/input-modality.js.map +1 -0
- package/dist/events/keyboard.d.ts +30 -0
- package/dist/events/keyboard.d.ts.map +1 -0
- package/dist/events/keyboard.js +55 -0
- package/dist/events/keyboard.js.map +1 -0
- package/dist/events/pointer.d.ts +15 -0
- package/dist/events/pointer.d.ts.map +1 -0
- package/dist/events/pointer.js +28 -0
- package/dist/events/pointer.js.map +1 -0
- package/dist/events/scroll.d.ts +16 -0
- package/dist/events/scroll.d.ts.map +1 -0
- package/dist/events/scroll.js +29 -0
- package/dist/events/scroll.js.map +1 -0
- package/dist/events/visibility.d.ts +25 -0
- package/dist/events/visibility.d.ts.map +1 -0
- package/dist/events/visibility.js +28 -0
- package/dist/events/visibility.js.map +1 -0
- package/dist/events/window-size.d.ts +30 -0
- package/dist/events/window-size.d.ts.map +1 -0
- package/dist/events/window-size.js +63 -0
- package/dist/events/window-size.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +10 -0
- package/dist/interaction/bundle-behavior.d.ts +37 -0
- package/dist/interaction/bundle-behavior.d.ts.map +1 -0
- package/dist/interaction/bundle-behavior.js +34 -0
- package/dist/interaction/bundle-behavior.js.map +1 -0
- package/dist/interaction/dismiss-layer.d.ts +100 -0
- package/dist/interaction/dismiss-layer.d.ts.map +1 -0
- package/dist/interaction/dismiss-layer.js +140 -0
- package/dist/interaction/dismiss-layer.js.map +1 -0
- package/dist/interaction/editable-text.d.ts +13 -0
- package/dist/interaction/editable-text.d.ts.map +1 -0
- package/dist/interaction/editable-text.js +17 -0
- package/dist/interaction/editable-text.js.map +1 -0
- package/dist/interaction/focus-scope.d.ts +21 -0
- package/dist/interaction/focus-scope.d.ts.map +1 -0
- package/dist/interaction/focus-scope.js +25 -0
- package/dist/interaction/focus-scope.js.map +1 -0
- package/dist/interaction/focus-visible.d.ts +9 -0
- package/dist/interaction/focus-visible.d.ts.map +1 -0
- package/dist/interaction/focus-visible.js +10 -0
- package/dist/interaction/focus-visible.js.map +1 -0
- package/dist/interaction/grid-navigation.d.ts +73 -0
- package/dist/interaction/grid-navigation.d.ts.map +1 -0
- package/dist/interaction/grid-navigation.js +137 -0
- package/dist/interaction/grid-navigation.js.map +1 -0
- package/dist/interaction/index.d.ts +21 -0
- package/dist/interaction/index.js +21 -0
- package/dist/interaction/layers.d.ts +12 -0
- package/dist/interaction/layers.d.ts.map +1 -0
- package/dist/interaction/layers.js +14 -0
- package/dist/interaction/layers.js.map +1 -0
- package/dist/interaction/list-navigation.d.ts +105 -0
- package/dist/interaction/list-navigation.d.ts.map +1 -0
- package/dist/interaction/list-navigation.js +168 -0
- package/dist/interaction/list-navigation.js.map +1 -0
- package/dist/interaction/live-announce.d.ts +70 -0
- package/dist/interaction/live-announce.d.ts.map +1 -0
- package/dist/interaction/live-announce.js +115 -0
- package/dist/interaction/live-announce.js.map +1 -0
- package/dist/interaction/long-press.d.ts +102 -0
- package/dist/interaction/long-press.d.ts.map +1 -0
- package/dist/interaction/long-press.js +115 -0
- package/dist/interaction/long-press.js.map +1 -0
- package/dist/interaction/move.d.ts +10 -0
- package/dist/interaction/move.d.ts.map +1 -0
- package/dist/interaction/move.js +17 -0
- package/dist/interaction/move.js.map +1 -0
- package/dist/interaction/overlay.d.ts +45 -0
- package/dist/interaction/overlay.d.ts.map +1 -0
- package/dist/interaction/overlay.js +57 -0
- package/dist/interaction/overlay.js.map +1 -0
- package/dist/interaction/placing.d.ts +86 -0
- package/dist/interaction/placing.d.ts.map +1 -0
- package/dist/interaction/placing.js +239 -0
- package/dist/interaction/placing.js.map +1 -0
- package/dist/interaction/pointer-drag.d.ts +23 -0
- package/dist/interaction/pointer-drag.d.ts.map +1 -0
- package/dist/interaction/pointer-drag.js +21 -0
- package/dist/interaction/pointer-drag.js.map +1 -0
- package/dist/interaction/press.d.ts +152 -0
- package/dist/interaction/press.d.ts.map +1 -0
- package/dist/interaction/press.js +229 -0
- package/dist/interaction/press.js.map +1 -0
- package/dist/interaction/roving-tabindex.d.ts +85 -0
- package/dist/interaction/roving-tabindex.d.ts.map +1 -0
- package/dist/interaction/roving-tabindex.js +142 -0
- package/dist/interaction/roving-tabindex.js.map +1 -0
- package/dist/interaction/selection.d.ts +72 -0
- package/dist/interaction/selection.d.ts.map +1 -0
- package/dist/interaction/selection.js +129 -0
- package/dist/interaction/selection.js.map +1 -0
- package/dist/interaction/targets.d.ts +15 -0
- package/dist/interaction/targets.d.ts.map +1 -0
- package/dist/interaction/targets.js +20 -0
- package/dist/interaction/targets.js.map +1 -0
- package/dist/interaction/tree-navigation.d.ts +111 -0
- package/dist/interaction/tree-navigation.d.ts.map +1 -0
- package/dist/interaction/tree-navigation.js +207 -0
- package/dist/interaction/tree-navigation.js.map +1 -0
- package/dist/interaction/typeahead.d.ts +73 -0
- package/dist/interaction/typeahead.d.ts.map +1 -0
- package/dist/interaction/typeahead.js +113 -0
- package/dist/interaction/typeahead.js.map +1 -0
- package/dist/internal.d.ts +16 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/{internal-C4gHodLc.mjs → internal.js} +7 -5
- package/dist/internal.js.map +1 -0
- package/dist/media/breakpoints.d.ts +37 -0
- package/dist/media/breakpoints.d.ts.map +1 -0
- package/dist/media/breakpoints.js +64 -0
- package/dist/media/breakpoints.js.map +1 -0
- package/dist/media/index.d.ts +3 -0
- package/dist/media/index.js +4 -0
- package/dist/media/mediaquery.d.ts +40 -0
- package/dist/media/mediaquery.d.ts.map +1 -0
- package/dist/media/mediaquery.js +42 -0
- package/dist/media/mediaquery.js.map +1 -0
- package/dist/media/platform.d.ts +30 -0
- package/dist/media/platform.d.ts.map +1 -0
- package/dist/media/platform.js +50 -0
- package/dist/media/platform.js.map +1 -0
- package/dist/motion/index.d.ts +5 -0
- package/dist/motion/index.js +5 -0
- package/dist/motion/motion.d.ts +16 -0
- package/dist/motion/motion.d.ts.map +1 -0
- package/dist/motion/motion.js +25 -0
- package/dist/motion/motion.js.map +1 -0
- package/dist/motion/presence.d.ts +37 -0
- package/dist/motion/presence.d.ts.map +1 -0
- package/dist/motion/presence.js +65 -0
- package/dist/motion/presence.js.map +1 -0
- package/dist/motion/spring.d.ts +50 -0
- package/dist/motion/spring.d.ts.map +1 -0
- package/dist/motion/spring.js +92 -0
- package/dist/motion/spring.js.map +1 -0
- package/dist/motion/tween.d.ts +44 -0
- package/dist/motion/tween.d.ts.map +1 -0
- package/dist/motion/tween.js +65 -0
- package/dist/motion/tween.js.map +1 -0
- package/dist/net/broadcast.d.ts +39 -0
- package/dist/net/broadcast.d.ts.map +1 -0
- package/dist/net/broadcast.js +73 -0
- package/dist/net/broadcast.js.map +1 -0
- package/dist/net/index.d.ts +5 -0
- package/dist/net/index.js +6 -0
- package/dist/net/online.d.ts +24 -0
- package/dist/net/online.d.ts.map +1 -0
- package/dist/net/online.js +26 -0
- package/dist/net/online.js.map +1 -0
- package/dist/net/sharedHost.d.ts +64 -0
- package/dist/net/sharedHost.d.ts.map +1 -0
- package/dist/net/sharedHost.js +124 -0
- package/dist/net/sharedHost.js.map +1 -0
- package/dist/net/sse.d.ts +115 -0
- package/dist/net/sse.d.ts.map +1 -0
- package/dist/net/sse.js +138 -0
- package/dist/net/sse.js.map +1 -0
- package/dist/net/websocket.d.ts +172 -0
- package/dist/net/websocket.d.ts.map +1 -0
- package/dist/net/websocket.js +199 -0
- package/dist/net/websocket.js.map +1 -0
- package/dist/observers/bounds.d.ts +24 -0
- package/dist/observers/bounds.d.ts.map +1 -0
- package/dist/observers/bounds.js +51 -0
- package/dist/observers/bounds.js.map +1 -0
- package/dist/observers/index.d.ts +4 -0
- package/dist/observers/index.js +5 -0
- package/dist/observers/intersection.d.ts +18 -0
- package/dist/observers/intersection.d.ts.map +1 -0
- package/dist/observers/intersection.js +33 -0
- package/dist/observers/intersection.js.map +1 -0
- package/dist/observers/mutation.d.ts +25 -0
- package/dist/observers/mutation.d.ts.map +1 -0
- package/dist/observers/mutation.js +53 -0
- package/dist/observers/mutation.js.map +1 -0
- package/dist/observers/resize.d.ts +18 -0
- package/dist/observers/resize.d.ts.map +1 -0
- package/dist/observers/resize.js +33 -0
- package/dist/observers/resize.js.map +1 -0
- package/dist/state/history.d.ts +115 -0
- package/dist/state/history.d.ts.map +1 -0
- package/dist/state/history.js +167 -0
- package/dist/state/history.js.map +1 -0
- package/dist/state/index.d.ts +6 -0
- package/dist/state/index.js +7 -0
- package/dist/state/locale.d.ts +27 -0
- package/dist/state/locale.d.ts.map +1 -0
- package/dist/state/locale.js +25 -0
- package/dist/state/locale.js.map +1 -0
- package/dist/state/pagination.d.ts +53 -0
- package/dist/state/pagination.d.ts.map +1 -0
- package/dist/state/pagination.js +72 -0
- package/dist/state/pagination.js.map +1 -0
- package/dist/state/range.d.ts +9 -0
- package/dist/state/range.d.ts.map +1 -0
- package/dist/state/range.js +18 -0
- package/dist/state/range.js.map +1 -0
- package/dist/state/selection.d.ts +47 -0
- package/dist/state/selection.d.ts.map +1 -0
- package/dist/state/selection.js +39 -0
- package/dist/state/selection.js.map +1 -0
- package/dist/state/virtual.d.ts +201 -0
- package/dist/state/virtual.d.ts.map +1 -0
- package/dist/state/{index.mjs → virtual.js} +10 -306
- package/dist/state/virtual.js.map +1 -0
- package/dist/time/debounce.d.ts +43 -0
- package/dist/time/debounce.d.ts.map +1 -0
- package/dist/time/debounce.js +65 -0
- package/dist/time/debounce.js.map +1 -0
- package/dist/time/index.d.ts +6 -0
- package/dist/time/index.js +7 -0
- package/dist/time/interval.d.ts +37 -0
- package/dist/time/interval.d.ts.map +1 -0
- package/dist/time/interval.js +52 -0
- package/dist/time/interval.js.map +1 -0
- package/dist/time/relative.d.ts +8 -0
- package/dist/time/relative.d.ts.map +1 -0
- package/dist/time/relative.js +27 -0
- package/dist/time/relative.js.map +1 -0
- package/dist/time/throttle.d.ts +38 -0
- package/dist/time/throttle.d.ts.map +1 -0
- package/dist/time/throttle.js +39 -0
- package/dist/time/throttle.js.map +1 -0
- package/dist/time/ticks.d.ts +21 -0
- package/dist/time/ticks.d.ts.map +1 -0
- package/dist/time/ticks.js +30 -0
- package/dist/time/ticks.js.map +1 -0
- package/dist/time/timer.d.ts +32 -0
- package/dist/time/timer.d.ts.map +1 -0
- package/dist/time/timer.js +50 -0
- package/dist/time/timer.js.map +1 -0
- package/package.json +31 -31
- package/dist/device/index.d.mts +0 -335
- package/dist/device/index.d.mts.map +0 -1
- package/dist/device/index.mjs +0 -493
- package/dist/device/index.mjs.map +0 -1
- package/dist/dom/index.d.mts +0 -714
- package/dist/dom/index.d.mts.map +0 -1
- package/dist/dom/index.mjs +0 -1840
- package/dist/dom/index.mjs.map +0 -1
- package/dist/editable-text-Bg7vil7h.d.mts +0 -307
- package/dist/editable-text-Bg7vil7h.d.mts.map +0 -1
- package/dist/events/index.d.mts +0 -168
- package/dist/events/index.d.mts.map +0 -1
- package/dist/events/index.mjs +0 -307
- package/dist/events/index.mjs.map +0 -1
- package/dist/follow-tab-stop-DM5EI4Ci.mjs +0 -856
- package/dist/follow-tab-stop-DM5EI4Ci.mjs.map +0 -1
- package/dist/index-Bomdwb7e.d.mts +0 -142
- package/dist/index-Bomdwb7e.d.mts.map +0 -1
- package/dist/index.d.mts +0 -12
- package/dist/index.mjs +0 -12
- package/dist/input-modality-XwQOFLip.mjs.map +0 -1
- package/dist/input-modality-l7InESYh.d.mts.map +0 -1
- package/dist/interaction/index.d.mts +0 -1193
- package/dist/interaction/index.d.mts.map +0 -1
- package/dist/interaction/index.mjs +0 -1992
- package/dist/interaction/index.mjs.map +0 -1
- package/dist/internal-C4gHodLc.mjs.map +0 -1
- package/dist/media/index.d.mts +0 -103
- package/dist/media/index.d.mts.map +0 -1
- package/dist/media/index.mjs +0 -146
- package/dist/media/index.mjs.map +0 -1
- package/dist/motion/index.d.mts +0 -2
- package/dist/motion/index.mjs +0 -1
- package/dist/motion-BmlJCjn6.mjs +0 -236
- package/dist/motion-BmlJCjn6.mjs.map +0 -1
- package/dist/net/index.d.mts +0 -338
- package/dist/net/index.d.mts.map +0 -1
- package/dist/net/index.mjs +0 -530
- package/dist/net/index.mjs.map +0 -1
- package/dist/observers/index.d.mts +0 -76
- package/dist/observers/index.d.mts.map +0 -1
- package/dist/observers/index.mjs +0 -155
- package/dist/observers/index.mjs.map +0 -1
- package/dist/rolldown-runtime-D8jQpDG7.mjs +0 -12
- package/dist/state/index.d.mts +0 -432
- package/dist/state/index.d.mts.map +0 -1
- package/dist/state/index.mjs.map +0 -1
- package/dist/time/index.d.mts +0 -167
- package/dist/time/index.d.mts.map +0 -1
- package/dist/time/index.mjs +0 -236
- package/dist/time/index.mjs.map +0 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Schema, Stream } from "effect";
|
|
2
|
+
import { Command } from "foldkit/command";
|
|
3
|
+
export declare const FullscreenMessage: import("foldkit/message").MessageUnion<{
|
|
4
|
+
readonly Changed: {
|
|
5
|
+
active: Schema.Boolean;
|
|
6
|
+
};
|
|
7
|
+
readonly Entered: {};
|
|
8
|
+
readonly Exited: {};
|
|
9
|
+
readonly Failed: {
|
|
10
|
+
message: Schema.String;
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
export type FullscreenMessage = typeof FullscreenMessage.Type;
|
|
14
|
+
/**
|
|
15
|
+
* Requests fullscreen for an element. The capability is feature-checked on
|
|
16
|
+
* the element itself (SVG and HTML elements both qualify), falling back to
|
|
17
|
+
* the legacy `webkit` prefix; a platform with neither yields `Failed`
|
|
18
|
+
* instead of throwing.
|
|
19
|
+
*/
|
|
20
|
+
export declare const enterFullscreen: (element: Element) => Command<FullscreenMessage, never, never>;
|
|
21
|
+
/** Leaves fullscreen, with the same legacy fallback. Without a document API it yields `Failed`, not a throw. */
|
|
22
|
+
export declare const exitFullscreen: () => Command<FullscreenMessage, never, never>;
|
|
23
|
+
/**
|
|
24
|
+
* `Changed` whenever fullscreen state flips, starting with the current
|
|
25
|
+
* answer. Without a document the stream is empty instead of throwing.
|
|
26
|
+
*/
|
|
27
|
+
export declare const fullscreenChanges: () => Stream.Stream<FullscreenMessage>;
|
|
28
|
+
//# sourceMappingURL=fullscreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullscreen.d.ts","names":[],"sources":["../../src/device/fullscreen.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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import { defineMessageUnion } from "foldkit/message";
|
|
3
|
+
// src/device/fullscreen.ts
|
|
4
|
+
/**
|
|
5
|
+
* Fullscreen as Commands plus an entry — not a bundle, because the document
|
|
6
|
+
* owns the state: `document.fullscreenElement` is the fact, and the entry
|
|
7
|
+
* reports it as `Changed`. Commands yield `Entered`/`Exited` on success and
|
|
8
|
+
* `Failed` otherwise (rejected request, missing API), so `update` handles
|
|
9
|
+
* both outcomes as Messages.
|
|
10
|
+
*/
|
|
11
|
+
const FullscreenMessage = defineMessageUnion({
|
|
12
|
+
Changed: { active: Schema.Boolean },
|
|
13
|
+
Entered: {},
|
|
14
|
+
Exited: {},
|
|
15
|
+
Failed: { message: Schema.String }
|
|
16
|
+
});
|
|
17
|
+
const failMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
18
|
+
/**
|
|
19
|
+
* Requests fullscreen for an element. The capability is feature-checked on
|
|
20
|
+
* the element itself (SVG and HTML elements both qualify), falling back to
|
|
21
|
+
* the legacy `webkit` prefix; a platform with neither yields `Failed`
|
|
22
|
+
* instead of throwing.
|
|
23
|
+
*/
|
|
24
|
+
const enterFullscreen = (element) => ({
|
|
25
|
+
name: "Fullscreen.enter",
|
|
26
|
+
args: {},
|
|
27
|
+
effect: Effect.matchEffect(Effect.tryPromise({
|
|
28
|
+
try: () => {
|
|
29
|
+
const requestable = element;
|
|
30
|
+
const request = requestable.requestFullscreen ?? requestable.webkitRequestFullscreen;
|
|
31
|
+
if (typeof request !== "function") throw new Error("fullscreen is unavailable");
|
|
32
|
+
return request.call(element);
|
|
33
|
+
},
|
|
34
|
+
catch: (error) => error
|
|
35
|
+
}), {
|
|
36
|
+
onFailure: (error) => Effect.succeed(FullscreenMessage.Failed({ message: failMessage(error) })),
|
|
37
|
+
onSuccess: () => Effect.succeed(FullscreenMessage.Entered())
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
/** Leaves fullscreen, with the same legacy fallback. Without a document API it yields `Failed`, not a throw. */
|
|
41
|
+
const exitFullscreen = () => ({
|
|
42
|
+
name: "Fullscreen.exit",
|
|
43
|
+
args: {},
|
|
44
|
+
effect: Effect.matchEffect(Effect.tryPromise({
|
|
45
|
+
try: () => {
|
|
46
|
+
if (typeof document === "undefined") throw new Error("fullscreen is unavailable");
|
|
47
|
+
const api = document;
|
|
48
|
+
const exit = api.exitFullscreen ?? api.webkitExitFullscreen;
|
|
49
|
+
if (typeof exit !== "function") throw new Error("fullscreen is unavailable");
|
|
50
|
+
return exit.call(document);
|
|
51
|
+
},
|
|
52
|
+
catch: (error) => error
|
|
53
|
+
}), {
|
|
54
|
+
onFailure: (error) => Effect.succeed(FullscreenMessage.Failed({ message: failMessage(error) })),
|
|
55
|
+
onSuccess: () => Effect.succeed(FullscreenMessage.Exited())
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* `Changed` whenever fullscreen state flips, starting with the current
|
|
60
|
+
* answer. Without a document the stream is empty instead of throwing.
|
|
61
|
+
*/
|
|
62
|
+
const fullscreenChanges = () => {
|
|
63
|
+
if (typeof document === "undefined" || typeof document.addEventListener !== "function") return Stream.empty;
|
|
64
|
+
const isActive = () => document.fullscreenElement !== null && document.fullscreenElement !== void 0;
|
|
65
|
+
return Stream.concat(Stream.make(FullscreenMessage.Changed({ active: isActive() })), Stream.fromEventListener(document, "fullscreenchange").pipe(Stream.map(() => FullscreenMessage.Changed({ active: isActive() }))));
|
|
66
|
+
};
|
|
67
|
+
export { FullscreenMessage, enterFullscreen, exitFullscreen, fullscreenChanges };
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=fullscreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fullscreen.js","names":[],"sources":["../../src/device/fullscreen.ts"],"sourcesContent":["/**\n * Fullscreen as Commands plus an entry — not a bundle, because the document\n * owns the state: `document.fullscreenElement` is the fact, and the entry\n * reports it as `Changed`. Commands yield `Entered`/`Exited` on success and\n * `Failed` otherwise (rejected request, missing API), so `update` handles\n * both outcomes as Messages.\n */\nimport { Effect, Schema, Stream } from 'effect'\nimport type { Command } from 'foldkit/command'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const FullscreenMessage = defineMessageUnion({\n Changed: { active: Schema.Boolean },\n Entered: {},\n Exited: {},\n Failed: { message: Schema.String },\n})\nexport type FullscreenMessage = typeof FullscreenMessage.Type\n\nconst failMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error)\n\n/**\n * Requests fullscreen for an element. The capability is feature-checked on\n * the element itself (SVG and HTML elements both qualify), falling back to\n * the legacy `webkit` prefix; a platform with neither yields `Failed`\n * instead of throwing.\n */\nexport const enterFullscreen = (element: Element): Command<FullscreenMessage, never, never> => ({\n name: 'Fullscreen.enter',\n args: {},\n effect: Effect.matchEffect(\n Effect.tryPromise({\n try: () => {\n const requestable = element as unknown as {\n readonly requestFullscreen?: unknown\n readonly webkitRequestFullscreen?: unknown\n }\n const request = requestable.requestFullscreen ?? requestable.webkitRequestFullscreen\n if (typeof request !== 'function') {\n throw new Error('fullscreen is unavailable')\n }\n return (request as () => Promise<void>).call(element)\n },\n catch: (error: unknown) => error,\n }),\n {\n onFailure: error => Effect.succeed(FullscreenMessage.Failed({ message: failMessage(error) })),\n onSuccess: () => Effect.succeed(FullscreenMessage.Entered()),\n },\n ),\n})\n\n/** Leaves fullscreen, with the same legacy fallback. Without a document API it yields `Failed`, not a throw. */\nexport const exitFullscreen = (): Command<FullscreenMessage, never, never> => ({\n name: 'Fullscreen.exit',\n args: {},\n effect: Effect.matchEffect(\n Effect.tryPromise({\n try: () => {\n if (typeof document === 'undefined') {\n throw new Error('fullscreen is unavailable')\n }\n const api = document as unknown as {\n readonly exitFullscreen?: unknown\n readonly webkitExitFullscreen?: unknown\n }\n const exit = api.exitFullscreen ?? api.webkitExitFullscreen\n if (typeof exit !== 'function') {\n throw new Error('fullscreen is unavailable')\n }\n return (exit as () => Promise<void>).call(document)\n },\n catch: (error: unknown) => error,\n }),\n {\n onFailure: error => Effect.succeed(FullscreenMessage.Failed({ message: failMessage(error) })),\n onSuccess: () => Effect.succeed(FullscreenMessage.Exited()),\n },\n ),\n})\n\n/**\n * `Changed` whenever fullscreen state flips, starting with the current\n * answer. Without a document the stream is empty instead of throwing.\n */\nexport const fullscreenChanges = (): Stream.Stream<FullscreenMessage> => {\n if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') {\n return Stream.empty\n }\n // Incomplete DOMs leave fullscreenElement undefined instead of null.\n const isActive = (): boolean =>\n document.fullscreenElement !== null && document.fullscreenElement !== undefined\n return Stream.concat(\n Stream.make(FullscreenMessage.Changed({ active: isActive() })),\n Stream.fromEventListener(document, 'fullscreenchange').pipe(\n Stream.map(() => FullscreenMessage.Changed({ active: isActive() })),\n ),\n )\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAa,oBAAoB,mBAAmB;CAClD,SAAS,EAAE,QAAQ,OAAO,QAAQ;CAClC,SAAS,CAAC;CACV,QAAQ,CAAC;CACT,QAAQ,EAAE,SAAS,OAAO,OAAO;AACnC,CAAC;AAGD,MAAM,eAAe,UACnB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;;;;;;;AAQvD,MAAa,mBAAmB,aAAgE;CAC9F,MAAM;CACN,MAAM,CAAC;CACP,QAAQ,OAAO,YACb,OAAO,WAAW;EAChB,WAAW;GACT,MAAM,cAAc;GAIpB,MAAM,UAAU,YAAY,qBAAqB,YAAY;GAC7D,IAAI,OAAO,YAAY,YACrB,MAAM,IAAI,MAAM,2BAA2B;GAE7C,OAAQ,QAAgC,KAAK,OAAO;EACtD;EACA,QAAQ,UAAmB;CAC7B,CAAC,GACD;EACE,YAAW,UAAS,OAAO,QAAQ,kBAAkB,OAAO,EAAE,SAAS,YAAY,KAAK,EAAE,CAAC,CAAC;EAC5F,iBAAiB,OAAO,QAAQ,kBAAkB,QAAQ,CAAC;CAC7D,CACF;AACF;;AAGA,MAAa,wBAAkE;CAC7E,MAAM;CACN,MAAM,CAAC;CACP,QAAQ,OAAO,YACb,OAAO,WAAW;EAChB,WAAW;GACT,IAAI,OAAO,aAAa,aACtB,MAAM,IAAI,MAAM,2BAA2B;GAE7C,MAAM,MAAM;GAIZ,MAAM,OAAO,IAAI,kBAAkB,IAAI;GACvC,IAAI,OAAO,SAAS,YAClB,MAAM,IAAI,MAAM,2BAA2B;GAE7C,OAAQ,KAA6B,KAAK,QAAQ;EACpD;EACA,QAAQ,UAAmB;CAC7B,CAAC,GACD;EACE,YAAW,UAAS,OAAO,QAAQ,kBAAkB,OAAO,EAAE,SAAS,YAAY,KAAK,EAAE,CAAC,CAAC;EAC5F,iBAAiB,OAAO,QAAQ,kBAAkB,OAAO,CAAC;CAC5D,CACF;AACF;;;;;AAMA,MAAa,0BAA4D;CACvE,IAAI,OAAO,aAAa,eAAe,OAAO,SAAS,qBAAqB,YAC1E,OAAO,OAAO;CAGhB,MAAM,iBACJ,SAAS,sBAAsB,QAAQ,SAAS,sBAAsB,KAAA;CACxE,OAAO,OAAO,OACZ,OAAO,KAAK,kBAAkB,QAAQ,EAAE,QAAQ,SAAS,EAAE,CAAC,CAAC,GAC7D,OAAO,kBAAkB,UAAU,kBAAkB,CAAC,CAAC,KACrD,OAAO,UAAU,kBAAkB,QAAQ,EAAE,QAAQ,SAAS,EAAE,CAAC,CAAC,CACpE,CACF;AACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import { Bundle } from "foldkit-bundle";
|
|
3
|
+
export declare const Coordinates: Schema.Struct<{
|
|
4
|
+
readonly latitude: Schema.Number;
|
|
5
|
+
readonly longitude: Schema.Number;
|
|
6
|
+
readonly accuracy: Schema.Number;
|
|
7
|
+
}>;
|
|
8
|
+
export type Coordinates = typeof Coordinates.Type;
|
|
9
|
+
export declare const GeolocationModel: Schema.Struct<{
|
|
10
|
+
readonly status: Schema.Literals<readonly ["unknown", "ready", "denied"]>;
|
|
11
|
+
readonly coords: Schema.OptionFromNullOr<Schema.Struct<{
|
|
12
|
+
readonly latitude: Schema.Number;
|
|
13
|
+
readonly longitude: Schema.Number;
|
|
14
|
+
readonly accuracy: Schema.Number;
|
|
15
|
+
}>>;
|
|
16
|
+
readonly lastError: Schema.OptionFromNullOr<Schema.String>;
|
|
17
|
+
}>;
|
|
18
|
+
export type GeolocationModel = typeof GeolocationModel.Type;
|
|
19
|
+
export declare const GeolocationMessage: import("foldkit/message").MessageUnion<{
|
|
20
|
+
readonly Located: {
|
|
21
|
+
latitude: Schema.Number;
|
|
22
|
+
longitude: Schema.Number;
|
|
23
|
+
accuracy: Schema.Number;
|
|
24
|
+
};
|
|
25
|
+
readonly Denied: {};
|
|
26
|
+
readonly Failed: {
|
|
27
|
+
message: Schema.String;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
export type GeolocationMessage = typeof GeolocationMessage.Type;
|
|
31
|
+
/**
|
|
32
|
+
* Geolocation as a bundle: the device position in the Model, watched while
|
|
33
|
+
* placed. Permission denial is its own status (actionable UI); transient
|
|
34
|
+
* failures keep the last fix and note the error. No geolocation API yields
|
|
35
|
+
* an empty stream instead of throwing.
|
|
36
|
+
*/
|
|
37
|
+
export declare const Geolocation: Bundle.Bundle<"Geolocation", void, {
|
|
38
|
+
readonly status: "denied" | "ready" | "unknown";
|
|
39
|
+
readonly coords: Option.Option<{
|
|
40
|
+
readonly latitude: number;
|
|
41
|
+
readonly longitude: number;
|
|
42
|
+
readonly accuracy: number;
|
|
43
|
+
}>;
|
|
44
|
+
readonly lastError: Option.Option<string>;
|
|
45
|
+
}, {
|
|
46
|
+
readonly _tag: "Denied";
|
|
47
|
+
} | {
|
|
48
|
+
readonly _tag: "Failed";
|
|
49
|
+
readonly message: string;
|
|
50
|
+
} | {
|
|
51
|
+
readonly _tag: "Located";
|
|
52
|
+
readonly latitude: number;
|
|
53
|
+
readonly longitude: number;
|
|
54
|
+
readonly accuracy: number;
|
|
55
|
+
}, never, never, never, void, {}, {}>;
|
|
56
|
+
//# sourceMappingURL=geolocation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocation.d.ts","names":[],"sources":["../../src/device/geolocation.ts"],"mappings":";;qBAOa,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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Effect, Option, Queue, Schema, Stream } from "effect";
|
|
2
|
+
import { defineMessageUnion } from "foldkit/message";
|
|
3
|
+
import * as Subscription from "foldkit/subscription";
|
|
4
|
+
import { Bundle } from "foldkit-bundle";
|
|
5
|
+
import { unlessSame } from "../internal.js";
|
|
6
|
+
// src/device/geolocation.ts
|
|
7
|
+
const Coordinates = Schema.Struct({
|
|
8
|
+
latitude: Schema.Number,
|
|
9
|
+
longitude: Schema.Number,
|
|
10
|
+
accuracy: Schema.Number
|
|
11
|
+
});
|
|
12
|
+
const GeolocationModel = Schema.Struct({
|
|
13
|
+
status: Schema.Literals([
|
|
14
|
+
"unknown",
|
|
15
|
+
"ready",
|
|
16
|
+
"denied"
|
|
17
|
+
]),
|
|
18
|
+
coords: Schema.OptionFromNullOr(Coordinates),
|
|
19
|
+
lastError: Schema.OptionFromNullOr(Schema.String)
|
|
20
|
+
});
|
|
21
|
+
const GeolocationMessage = defineMessageUnion({
|
|
22
|
+
Located: {
|
|
23
|
+
latitude: Schema.Number,
|
|
24
|
+
longitude: Schema.Number,
|
|
25
|
+
accuracy: Schema.Number
|
|
26
|
+
},
|
|
27
|
+
Denied: {},
|
|
28
|
+
Failed: { message: Schema.String }
|
|
29
|
+
});
|
|
30
|
+
const watchStream = () => {
|
|
31
|
+
if (typeof navigator === "undefined" || typeof navigator.geolocation?.watchPosition !== "function") return Stream.empty;
|
|
32
|
+
const geolocation = navigator.geolocation;
|
|
33
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
34
|
+
const queue = yield* Queue.unbounded();
|
|
35
|
+
const onLocated = (position) => {
|
|
36
|
+
Effect.runFork(Queue.offer(queue, GeolocationMessage.Located({
|
|
37
|
+
latitude: position.coords.latitude,
|
|
38
|
+
longitude: position.coords.longitude,
|
|
39
|
+
accuracy: position.coords.accuracy
|
|
40
|
+
})));
|
|
41
|
+
};
|
|
42
|
+
const onError = (error) => {
|
|
43
|
+
Effect.runFork(Queue.offer(queue, error.code === 1 ? GeolocationMessage.Denied() : GeolocationMessage.Failed({ message: error.message })));
|
|
44
|
+
};
|
|
45
|
+
const watchId = geolocation.watchPosition(onLocated, onError);
|
|
46
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => geolocation.clearWatch(watchId)));
|
|
47
|
+
return Stream.fromQueue(queue);
|
|
48
|
+
}));
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Geolocation as a bundle: the device position in the Model, watched while
|
|
52
|
+
* placed. Permission denial is its own status (actionable UI); transient
|
|
53
|
+
* failures keep the last fix and note the error. No geolocation API yields
|
|
54
|
+
* an empty stream instead of throwing.
|
|
55
|
+
*/
|
|
56
|
+
const Geolocation = Bundle.make("Geolocation", {
|
|
57
|
+
Model: GeolocationModel,
|
|
58
|
+
Message: GeolocationMessage,
|
|
59
|
+
init: () => ({ model: {
|
|
60
|
+
status: "unknown",
|
|
61
|
+
coords: Option.none(),
|
|
62
|
+
lastError: Option.none()
|
|
63
|
+
} }),
|
|
64
|
+
update: (model, message) => GeolocationMessage.match(message, {
|
|
65
|
+
Located: ({ latitude, longitude, accuracy }) => model.status === "ready" && Option.exists(model.coords, (coords) => coords.latitude === latitude && coords.longitude === longitude && coords.accuracy === accuracy) ? { model } : { model: {
|
|
66
|
+
...model,
|
|
67
|
+
status: "ready",
|
|
68
|
+
coords: Option.some({
|
|
69
|
+
latitude,
|
|
70
|
+
longitude,
|
|
71
|
+
accuracy
|
|
72
|
+
})
|
|
73
|
+
} },
|
|
74
|
+
Denied: () => ({ model: unlessSame(model, {
|
|
75
|
+
...model,
|
|
76
|
+
status: "denied"
|
|
77
|
+
}) }),
|
|
78
|
+
Failed: ({ message }) => ({ model: unlessSame(model, {
|
|
79
|
+
...model,
|
|
80
|
+
lastError: Option.some(message)
|
|
81
|
+
}) })
|
|
82
|
+
}),
|
|
83
|
+
subscriptions: () => Subscription.make()(() => ({ watch: Subscription.persistentEntry(watchStream()) }))
|
|
84
|
+
});
|
|
85
|
+
export { Coordinates, Geolocation, GeolocationMessage, GeolocationModel };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=geolocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocation.js","names":[],"sources":["../../src/device/geolocation.ts"],"sourcesContent":["import { Effect, Option, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport type * as Update from 'foldkit/update'\nimport { Bundle } from 'foldkit-bundle'\nimport { unlessSame } from '../internal.js'\n\nexport const Coordinates = Schema.Struct({\n latitude: Schema.Number,\n longitude: Schema.Number,\n accuracy: Schema.Number,\n})\nexport type Coordinates = typeof Coordinates.Type\n\nexport const GeolocationModel = Schema.Struct({\n status: Schema.Literals(['unknown', 'ready', 'denied']),\n coords: Schema.OptionFromNullOr(Coordinates),\n lastError: Schema.OptionFromNullOr(Schema.String),\n})\nexport type GeolocationModel = typeof GeolocationModel.Type\n\nexport const GeolocationMessage = defineMessageUnion({\n Located: {\n latitude: Schema.Number,\n longitude: Schema.Number,\n accuracy: Schema.Number,\n },\n Denied: {},\n Failed: { message: Schema.String },\n})\nexport type GeolocationMessage = typeof GeolocationMessage.Type\n\ntype PositionCallback = (position: {\n readonly coords: {\n readonly latitude: number\n readonly longitude: number\n readonly accuracy: number\n }\n}) => void\ntype ErrorCallback = (error: { readonly code: number; readonly message: string }) => void\n\nconst watchStream = (): Stream.Stream<GeolocationMessage> => {\n if (\n typeof navigator === 'undefined' ||\n typeof navigator.geolocation?.watchPosition !== 'function'\n ) {\n return Stream.empty\n }\n const geolocation = navigator.geolocation\n return Stream.unwrap(\n Effect.gen(function* () {\n const queue = yield* Queue.unbounded<GeolocationMessage>()\n const onLocated: PositionCallback = position => {\n Effect.runFork(\n Queue.offer(\n queue,\n GeolocationMessage.Located({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracy: position.coords.accuracy,\n }),\n ),\n )\n }\n const onError: ErrorCallback = error => {\n Effect.runFork(\n Queue.offer(\n queue,\n error.code === 1\n ? GeolocationMessage.Denied()\n : GeolocationMessage.Failed({ message: error.message }),\n ),\n )\n }\n const watchId = geolocation.watchPosition(onLocated, onError)\n yield* Effect.addFinalizer(() => Effect.sync(() => geolocation.clearWatch(watchId)))\n return Stream.fromQueue(queue)\n }),\n )\n}\n\n/**\n * Geolocation as a bundle: the device position in the Model, watched while\n * placed. Permission denial is its own status (actionable UI); transient\n * failures keep the last fix and note the error. No geolocation API yields\n * an empty stream instead of throwing.\n */\nexport const Geolocation = Bundle.make('Geolocation', {\n Model: GeolocationModel,\n Message: GeolocationMessage,\n init: () => ({ model: { status: 'unknown', coords: Option.none(), lastError: Option.none() } }),\n update: (model, message) =>\n GeolocationMessage.match<\n Update.ReturnWithOutMessage<GeolocationModel, GeolocationMessage, never>\n >(message, {\n // A watch repeats a fix that has not moved.\n Located: ({ latitude, longitude, accuracy }) =>\n model.status === 'ready' &&\n Option.exists(\n model.coords,\n coords =>\n coords.latitude === latitude &&\n coords.longitude === longitude &&\n coords.accuracy === accuracy,\n )\n ? { model }\n : {\n model: {\n ...model,\n status: 'ready' as const,\n coords: Option.some({ latitude, longitude, accuracy }),\n },\n },\n Denied: () => ({ model: unlessSame(model, { ...model, status: 'denied' }) }),\n Failed: ({ message }) => ({\n model: unlessSame(model, { ...model, lastError: Option.some(message) }),\n }),\n }),\n subscriptions: (): Subscription.Subscriptions<GeolocationModel, GeolocationMessage> =>\n Subscription.make<GeolocationModel, GeolocationMessage>()(() => ({\n watch: Subscription.persistentEntry(watchStream()),\n })),\n})\n"],"mappings":";;;;;;AAOA,MAAa,cAAc,OAAO,OAAO;CACvC,UAAU,OAAO;CACjB,WAAW,OAAO;CAClB,UAAU,OAAO;AACnB,CAAC;AAGD,MAAa,mBAAmB,OAAO,OAAO;CAC5C,QAAQ,OAAO,SAAS;EAAC;EAAW;EAAS;CAAQ,CAAC;CACtD,QAAQ,OAAO,iBAAiB,WAAW;CAC3C,WAAW,OAAO,iBAAiB,OAAO,MAAM;AAClD,CAAC;AAGD,MAAa,qBAAqB,mBAAmB;CACnD,SAAS;EACP,UAAU,OAAO;EACjB,WAAW,OAAO;EAClB,UAAU,OAAO;CACnB;CACA,QAAQ,CAAC;CACT,QAAQ,EAAE,SAAS,OAAO,OAAO;AACnC,CAAC;AAYD,MAAM,oBAAuD;CAC3D,IACE,OAAO,cAAc,eACrB,OAAO,UAAU,aAAa,kBAAkB,YAEhD,OAAO,OAAO;CAEhB,MAAM,cAAc,UAAU;CAC9B,OAAO,OAAO,OACZ,OAAO,IAAI,aAAa;EACtB,MAAM,QAAQ,OAAO,MAAM,UAA8B;EACzD,MAAM,aAA8B,aAAY;GAC9C,OAAO,QACL,MAAM,MACJ,OACA,mBAAmB,QAAQ;IACzB,UAAU,SAAS,OAAO;IAC1B,WAAW,SAAS,OAAO;IAC3B,UAAU,SAAS,OAAO;GAC5B,CAAC,CACH,CACF;EACF;EACA,MAAM,WAAyB,UAAS;GACtC,OAAO,QACL,MAAM,MACJ,OACA,MAAM,SAAS,IACX,mBAAmB,OAAO,IAC1B,mBAAmB,OAAO,EAAE,SAAS,MAAM,QAAQ,CAAC,CAC1D,CACF;EACF;EACA,MAAM,UAAU,YAAY,cAAc,WAAW,OAAO;EAC5D,OAAO,OAAO,mBAAmB,OAAO,WAAW,YAAY,WAAW,OAAO,CAAC,CAAC;EACnF,OAAO,OAAO,UAAU,KAAK;CAC/B,CAAC,CACH;AACF;;;;;;;AAQA,MAAa,cAAc,OAAO,KAAK,eAAe;CACpD,OAAO;CACP,SAAS;CACT,aAAa,EAAE,OAAO;EAAE,QAAQ;EAAW,QAAQ,OAAO,KAAK;EAAG,WAAW,OAAO,KAAK;CAAE,EAAE;CAC7F,SAAS,OAAO,YACd,mBAAmB,MAEjB,SAAS;EAET,UAAU,EAAE,UAAU,WAAW,eAC/B,MAAM,WAAW,WACjB,OAAO,OACL,MAAM,SACN,WACE,OAAO,aAAa,YACpB,OAAO,cAAc,aACrB,OAAO,aAAa,QACxB,IACI,EAAE,MAAM,IACR,EACE,OAAO;GACL,GAAG;GACH,QAAQ;GACR,QAAQ,OAAO,KAAK;IAAE;IAAU;IAAW;GAAS,CAAC;EACvD,EACF;EACN,eAAe,EAAE,OAAO,WAAW,OAAO;GAAE,GAAG;GAAO,QAAQ;EAAS,CAAC,EAAE;EAC1E,SAAS,EAAE,eAAe,EACxB,OAAO,WAAW,OAAO;GAAE,GAAG;GAAO,WAAW,OAAO,KAAK,OAAO;EAAE,CAAC,EACxE;CACF,CAAC;CACH,qBACE,aAAa,KAA2C,CAAC,QAAQ,EAC/D,OAAO,aAAa,gBAAgB,YAAY,CAAC,EACnD,EAAE;AACN,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import { Bundle } from "foldkit-bundle";
|
|
3
|
+
export declare const MediaDevice: Schema.Struct<{
|
|
4
|
+
readonly deviceId: Schema.String;
|
|
5
|
+
readonly groupId: Schema.String;
|
|
6
|
+
readonly kind: Schema.Literals<readonly ["audioinput", "audiooutput", "videoinput"]>;
|
|
7
|
+
readonly label: Schema.String;
|
|
8
|
+
}>;
|
|
9
|
+
export type MediaDevice = typeof MediaDevice.Type;
|
|
10
|
+
export declare const MediaDevicesModel: Schema.Struct<{
|
|
11
|
+
readonly status: Schema.Literals<readonly ["unknown", "ready", "denied"]>;
|
|
12
|
+
readonly devices: Schema.$Array<Schema.Struct<{
|
|
13
|
+
readonly deviceId: Schema.String;
|
|
14
|
+
readonly groupId: Schema.String;
|
|
15
|
+
readonly kind: Schema.Literals<readonly ["audioinput", "audiooutput", "videoinput"]>;
|
|
16
|
+
readonly label: Schema.String;
|
|
17
|
+
}>>;
|
|
18
|
+
readonly lastError: Schema.OptionFromNullOr<Schema.String>;
|
|
19
|
+
}>;
|
|
20
|
+
export type MediaDevicesModel = typeof MediaDevicesModel.Type;
|
|
21
|
+
export declare const MediaDevicesMessage: import("foldkit/message").MessageUnion<{
|
|
22
|
+
readonly Scan: {};
|
|
23
|
+
readonly DevicesChanged: {};
|
|
24
|
+
readonly Refreshed: {
|
|
25
|
+
devices: Schema.$Array<Schema.Struct<{
|
|
26
|
+
readonly deviceId: Schema.String;
|
|
27
|
+
readonly groupId: Schema.String;
|
|
28
|
+
readonly kind: Schema.Literals<readonly ["audioinput", "audiooutput", "videoinput"]>;
|
|
29
|
+
readonly label: Schema.String;
|
|
30
|
+
}>>;
|
|
31
|
+
};
|
|
32
|
+
readonly Denied: {};
|
|
33
|
+
readonly Failed: {
|
|
34
|
+
message: Schema.String;
|
|
35
|
+
};
|
|
36
|
+
}>;
|
|
37
|
+
export type MediaDevicesMessage = typeof MediaDevicesMessage.Type;
|
|
38
|
+
/** The slice of MediaDevices the bundle needs; satisfied by the platform object. */
|
|
39
|
+
export interface MediaDevicesHandle {
|
|
40
|
+
enumerateDevices(): Promise<ReadonlyArray<{
|
|
41
|
+
readonly deviceId: string;
|
|
42
|
+
readonly groupId: string;
|
|
43
|
+
readonly kind: string;
|
|
44
|
+
readonly label: string;
|
|
45
|
+
}>>;
|
|
46
|
+
addEventListener(type: string, listener: () => void): void;
|
|
47
|
+
removeEventListener(type: string, listener: () => void): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Media devices as a bundle factory: the enumerated device list in the
|
|
51
|
+
* Model, scanned on placement and re-scanned on every `devicechange`.
|
|
52
|
+
* Denial lands as `denied` while keeping the last list (actionable UI over
|
|
53
|
+
* stale data, like Geolocation's last fix); any other failure keeps the
|
|
54
|
+
* list and notes the error. No media-devices API yields an empty stream
|
|
55
|
+
* and a `Failed` scan instead of throwing.
|
|
56
|
+
*/
|
|
57
|
+
export declare const mediaDevices: <const Name extends string>(config: {
|
|
58
|
+
readonly name: Name;
|
|
59
|
+
/** Read lazily so tests can substitute a double, like the SSE bundle. */
|
|
60
|
+
readonly create?: ((navigator: Navigator) => MediaDevicesHandle | null) | undefined;
|
|
61
|
+
}) => Bundle.Bundle<Name, void, {
|
|
62
|
+
readonly status: "denied" | "ready" | "unknown";
|
|
63
|
+
readonly devices: readonly {
|
|
64
|
+
readonly deviceId: string;
|
|
65
|
+
readonly groupId: string;
|
|
66
|
+
readonly kind: "audioinput" | "audiooutput" | "videoinput";
|
|
67
|
+
readonly label: string;
|
|
68
|
+
}[];
|
|
69
|
+
readonly lastError: Option.Option<string>;
|
|
70
|
+
}, {
|
|
71
|
+
readonly _tag: "Denied";
|
|
72
|
+
} | {
|
|
73
|
+
readonly _tag: "DevicesChanged";
|
|
74
|
+
} | {
|
|
75
|
+
readonly _tag: "Failed";
|
|
76
|
+
readonly message: string;
|
|
77
|
+
} | {
|
|
78
|
+
readonly _tag: "Refreshed";
|
|
79
|
+
readonly devices: readonly {
|
|
80
|
+
readonly deviceId: string;
|
|
81
|
+
readonly groupId: string;
|
|
82
|
+
readonly kind: "audioinput" | "audiooutput" | "videoinput";
|
|
83
|
+
readonly label: string;
|
|
84
|
+
}[];
|
|
85
|
+
} | {
|
|
86
|
+
readonly _tag: "Scan";
|
|
87
|
+
}, never, never, never, void, {}, {}>;
|
|
88
|
+
//# sourceMappingURL=media-devices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-devices.d.ts","names":[],"sources":["../../src/device/media-devices.ts"],"mappings":";;qBAOa,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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Effect, Option, Schema, Stream } from "effect";
|
|
2
|
+
import { defineMessageUnion } from "foldkit/message";
|
|
3
|
+
import * as Subscription from "foldkit/subscription";
|
|
4
|
+
import { Bundle } from "foldkit-bundle";
|
|
5
|
+
import { unlessSame } from "../internal.js";
|
|
6
|
+
// src/device/media-devices.ts
|
|
7
|
+
const MediaDevice = Schema.Struct({
|
|
8
|
+
deviceId: Schema.String,
|
|
9
|
+
groupId: Schema.String,
|
|
10
|
+
kind: Schema.Literals([
|
|
11
|
+
"audioinput",
|
|
12
|
+
"audiooutput",
|
|
13
|
+
"videoinput"
|
|
14
|
+
]),
|
|
15
|
+
label: Schema.String
|
|
16
|
+
});
|
|
17
|
+
const MediaDevicesModel = Schema.Struct({
|
|
18
|
+
status: Schema.Literals([
|
|
19
|
+
"unknown",
|
|
20
|
+
"ready",
|
|
21
|
+
"denied"
|
|
22
|
+
]),
|
|
23
|
+
devices: Schema.Array(MediaDevice),
|
|
24
|
+
lastError: Schema.OptionFromNullOr(Schema.String)
|
|
25
|
+
});
|
|
26
|
+
const MediaDevicesMessage = defineMessageUnion({
|
|
27
|
+
Scan: {},
|
|
28
|
+
DevicesChanged: {},
|
|
29
|
+
Refreshed: { devices: Schema.Array(MediaDevice) },
|
|
30
|
+
Denied: {},
|
|
31
|
+
Failed: { message: Schema.String }
|
|
32
|
+
});
|
|
33
|
+
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);
|
|
34
|
+
const failMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
35
|
+
const isDenied = (error) => typeof error?.name === "string" && ["NotAllowedError", "SecurityError"].includes(error.name);
|
|
36
|
+
/**
|
|
37
|
+
* Media devices as a bundle factory: the enumerated device list in the
|
|
38
|
+
* Model, scanned on placement and re-scanned on every `devicechange`.
|
|
39
|
+
* Denial lands as `denied` while keeping the last list (actionable UI over
|
|
40
|
+
* stale data, like Geolocation's last fix); any other failure keeps the
|
|
41
|
+
* list and notes the error. No media-devices API yields an empty stream
|
|
42
|
+
* and a `Failed` scan instead of throwing.
|
|
43
|
+
*/
|
|
44
|
+
const mediaDevices = (config) => {
|
|
45
|
+
const handle = () => {
|
|
46
|
+
if (typeof navigator === "undefined") return null;
|
|
47
|
+
if (config.create !== void 0) return config.create(navigator);
|
|
48
|
+
return navigator.mediaDevices ?? null;
|
|
49
|
+
};
|
|
50
|
+
const scanEffect = Effect.matchEffect(Effect.tryPromise({
|
|
51
|
+
try: async () => {
|
|
52
|
+
const devices = handle();
|
|
53
|
+
if (devices === null) throw new Error("media devices are unavailable");
|
|
54
|
+
return (await devices.enumerateDevices()).map((item) => {
|
|
55
|
+
if (item.kind !== "audioinput" && item.kind !== "audiooutput" && item.kind !== "videoinput") throw new Error(`unknown device kind: ${item.kind}`);
|
|
56
|
+
return {
|
|
57
|
+
deviceId: item.deviceId,
|
|
58
|
+
groupId: item.groupId,
|
|
59
|
+
kind: item.kind,
|
|
60
|
+
label: item.label
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
catch: (error) => error
|
|
65
|
+
}), {
|
|
66
|
+
onFailure: (error) => Effect.succeed(isDenied(error) ? MediaDevicesMessage.Denied() : MediaDevicesMessage.Failed({ message: failMessage(error) })),
|
|
67
|
+
onSuccess: (devices) => Effect.succeed(MediaDevicesMessage.Refreshed({ devices: [...devices] }))
|
|
68
|
+
});
|
|
69
|
+
const scan = () => ({
|
|
70
|
+
name: `${config.name}.scan`,
|
|
71
|
+
effect: scanEffect
|
|
72
|
+
});
|
|
73
|
+
const changedStream = () => {
|
|
74
|
+
const devices = handle();
|
|
75
|
+
if (devices === null) return Stream.empty;
|
|
76
|
+
return Stream.fromEventListener(devices, "devicechange").pipe(Stream.map(() => MediaDevicesMessage.DevicesChanged()));
|
|
77
|
+
};
|
|
78
|
+
return Bundle.make(config.name, {
|
|
79
|
+
Model: MediaDevicesModel,
|
|
80
|
+
Message: MediaDevicesMessage,
|
|
81
|
+
init: () => ({
|
|
82
|
+
model: {
|
|
83
|
+
status: "unknown",
|
|
84
|
+
devices: [],
|
|
85
|
+
lastError: Option.none()
|
|
86
|
+
},
|
|
87
|
+
commands: [scan()]
|
|
88
|
+
}),
|
|
89
|
+
update: (model, message) => MediaDevicesMessage.match(message, {
|
|
90
|
+
Scan: () => ({
|
|
91
|
+
model,
|
|
92
|
+
commands: [scan()]
|
|
93
|
+
}),
|
|
94
|
+
DevicesChanged: () => ({
|
|
95
|
+
model,
|
|
96
|
+
commands: [scan()]
|
|
97
|
+
}),
|
|
98
|
+
Refreshed: ({ devices }) => model.status === "ready" && sameDevices(model.devices, devices) ? { model } : { model: {
|
|
99
|
+
...model,
|
|
100
|
+
status: "ready",
|
|
101
|
+
devices
|
|
102
|
+
} },
|
|
103
|
+
Denied: () => ({ model: unlessSame(model, {
|
|
104
|
+
...model,
|
|
105
|
+
status: "denied"
|
|
106
|
+
}) }),
|
|
107
|
+
Failed: ({ message }) => ({ model: unlessSame(model, {
|
|
108
|
+
...model,
|
|
109
|
+
lastError: Option.some(message)
|
|
110
|
+
}) })
|
|
111
|
+
}),
|
|
112
|
+
subscriptions: () => Subscription.make()(() => ({ changes: Subscription.persistentEntry(changedStream()) }))
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
export { MediaDevice, MediaDevicesMessage, MediaDevicesModel, mediaDevices };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=media-devices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-devices.js","names":[],"sources":["../../src/device/media-devices.ts"],"sourcesContent":["import { Effect, Option, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport type * as Update from 'foldkit/update'\nimport { Bundle } from 'foldkit-bundle'\nimport { unlessSame } from '../internal.js'\n\nexport const MediaDevice = Schema.Struct({\n deviceId: Schema.String,\n groupId: Schema.String,\n kind: Schema.Literals(['audioinput', 'audiooutput', 'videoinput']),\n label: Schema.String,\n})\nexport type MediaDevice = typeof MediaDevice.Type\n\nexport const MediaDevicesModel = Schema.Struct({\n status: Schema.Literals(['unknown', 'ready', 'denied']),\n devices: Schema.Array(MediaDevice),\n lastError: Schema.OptionFromNullOr(Schema.String),\n})\nexport type MediaDevicesModel = typeof MediaDevicesModel.Type\n\nexport const MediaDevicesMessage = defineMessageUnion({\n Scan: {},\n DevicesChanged: {},\n Refreshed: { devices: Schema.Array(MediaDevice) },\n Denied: {},\n Failed: { message: Schema.String },\n})\nexport type MediaDevicesMessage = typeof MediaDevicesMessage.Type\n\nconst sameDevices = (a: ReadonlyArray<MediaDevice>, b: ReadonlyArray<MediaDevice>): boolean =>\n a.length === b.length &&\n a.every(\n (device, index) =>\n device.deviceId === b[index]!.deviceId &&\n device.groupId === b[index]!.groupId &&\n device.kind === b[index]!.kind &&\n device.label === b[index]!.label,\n )\n\n/** The slice of MediaDevices the bundle needs; satisfied by the platform object. */\nexport interface MediaDevicesHandle {\n enumerateDevices(): Promise<\n ReadonlyArray<{\n readonly deviceId: string\n readonly groupId: string\n readonly kind: string\n readonly label: string\n }>\n >\n addEventListener(type: string, listener: () => void): void\n removeEventListener(type: string, listener: () => void): void\n}\n\nconst failMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error)\n\nconst isDenied = (error: unknown): boolean =>\n typeof (error as { readonly name?: unknown } | null)?.name === 'string' &&\n ['NotAllowedError', 'SecurityError'].includes((error as { readonly name: string }).name)\n\n/**\n * Media devices as a bundle factory: the enumerated device list in the\n * Model, scanned on placement and re-scanned on every `devicechange`.\n * Denial lands as `denied` while keeping the last list (actionable UI over\n * stale data, like Geolocation's last fix); any other failure keeps the\n * list and notes the error. No media-devices API yields an empty stream\n * and a `Failed` scan instead of throwing.\n */\nexport const mediaDevices = <const Name extends string>(config: {\n readonly name: Name\n /** Read lazily so tests can substitute a double, like the SSE bundle. */\n readonly create?: ((navigator: Navigator) => MediaDevicesHandle | null) | undefined\n}) => {\n const handle = (): MediaDevicesHandle | null => {\n if (typeof navigator === 'undefined') return null\n if (config.create !== undefined) return config.create(navigator)\n const devices = navigator.mediaDevices as unknown as MediaDevicesHandle | undefined\n return devices ?? null\n }\n\n const scanEffect = Effect.matchEffect(\n Effect.tryPromise({\n try: async () => {\n const devices = handle()\n if (devices === null) throw new Error('media devices are unavailable')\n const listed = await devices.enumerateDevices()\n // The platform constrains kind to three values; anything else is a\n // boundary violation, so it fails the scan instead of entering the Model.\n return listed.map(item => {\n if (\n item.kind !== 'audioinput' &&\n item.kind !== 'audiooutput' &&\n item.kind !== 'videoinput'\n ) {\n throw new Error(`unknown device kind: ${item.kind}`)\n }\n // Guarded by the check above: only the three platform kinds reach here.\n return {\n deviceId: item.deviceId,\n groupId: item.groupId,\n kind: item.kind as MediaDevice['kind'],\n label: item.label,\n }\n })\n },\n catch: (error: unknown) => error,\n }),\n {\n onFailure: error =>\n Effect.succeed(\n isDenied(error)\n ? MediaDevicesMessage.Denied()\n : MediaDevicesMessage.Failed({ message: failMessage(error) }),\n ),\n onSuccess: devices =>\n Effect.succeed(MediaDevicesMessage.Refreshed({ devices: [...devices] })),\n },\n )\n\n const scan = () => ({ name: `${config.name}.scan`, effect: scanEffect })\n\n const changedStream = (): Stream.Stream<MediaDevicesMessage> => {\n const devices = handle()\n if (devices === null) return Stream.empty\n // The handle carries only the slice the bundle needs, not the full\n // EventTarget surface, so the listener attaches through a cast.\n return Stream.fromEventListener(devices as unknown as EventTarget, 'devicechange').pipe(\n Stream.map(() => MediaDevicesMessage.DevicesChanged()),\n )\n }\n\n return Bundle.make(config.name, {\n Model: MediaDevicesModel,\n Message: MediaDevicesMessage,\n init: () => ({\n model: { status: 'unknown', devices: [], lastError: Option.none() },\n commands: [scan()],\n }),\n update: (model, message) =>\n MediaDevicesMessage.match<\n Update.ReturnWithOutMessage<MediaDevicesModel, MediaDevicesMessage, never>\n >(message, {\n Scan: () => ({ model, commands: [scan()] }),\n DevicesChanged: () => ({ model, commands: [scan()] }),\n // One plug-in fires several `devicechange`s, and each rescan finds the same list.\n Refreshed: ({ devices }) =>\n model.status === 'ready' && sameDevices(model.devices, devices)\n ? { model }\n : { model: { ...model, status: 'ready' as const, devices } },\n Denied: () => ({ model: unlessSame(model, { ...model, status: 'denied' }) }),\n Failed: ({ message }) => ({\n model: unlessSame(model, { ...model, lastError: Option.some(message) }),\n }),\n }),\n subscriptions: (): Subscription.Subscriptions<MediaDevicesModel, MediaDevicesMessage> =>\n Subscription.make<MediaDevicesModel, MediaDevicesMessage>()(() => ({\n changes: Subscription.persistentEntry(changedStream()),\n })),\n })\n}\n"],"mappings":";;;;;;AAOA,MAAa,cAAc,OAAO,OAAO;CACvC,UAAU,OAAO;CACjB,SAAS,OAAO;CAChB,MAAM,OAAO,SAAS;EAAC;EAAc;EAAe;CAAY,CAAC;CACjE,OAAO,OAAO;AAChB,CAAC;AAGD,MAAa,oBAAoB,OAAO,OAAO;CAC7C,QAAQ,OAAO,SAAS;EAAC;EAAW;EAAS;CAAQ,CAAC;CACtD,SAAS,OAAO,MAAM,WAAW;CACjC,WAAW,OAAO,iBAAiB,OAAO,MAAM;AAClD,CAAC;AAGD,MAAa,sBAAsB,mBAAmB;CACpD,MAAM,CAAC;CACP,gBAAgB,CAAC;CACjB,WAAW,EAAE,SAAS,OAAO,MAAM,WAAW,EAAE;CAChD,QAAQ,CAAC;CACT,QAAQ,EAAE,SAAS,OAAO,OAAO;AACnC,CAAC;AAGD,MAAM,eAAe,GAA+B,MAClD,EAAE,WAAW,EAAE,UACf,EAAE,OACC,QAAQ,UACP,OAAO,aAAa,EAAE,MAAM,CAAE,YAC9B,OAAO,YAAY,EAAE,MAAM,CAAE,WAC7B,OAAO,SAAS,EAAE,MAAM,CAAE,QAC1B,OAAO,UAAU,EAAE,MAAM,CAAE,KAC/B;AAgBF,MAAM,eAAe,UACnB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAEvD,MAAM,YAAY,UAChB,OAAQ,OAA8C,SAAS,YAC/D,CAAC,mBAAmB,eAAe,CAAC,CAAC,SAAU,MAAoC,IAAI;;;;;;;;;AAUzF,MAAa,gBAA2C,WAIlD;CACJ,MAAM,eAA0C;EAC9C,IAAI,OAAO,cAAc,aAAa,OAAO;EAC7C,IAAI,OAAO,WAAW,KAAA,GAAW,OAAO,OAAO,OAAO,SAAS;EAE/D,OADgB,UAAU,gBACR;CACpB;CAEA,MAAM,aAAa,OAAO,YACxB,OAAO,WAAW;EAChB,KAAK,YAAY;GACf,MAAM,UAAU,OAAO;GACvB,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,+BAA+B;GAIrE,QAAO,MAHc,QAAQ,iBAAiB,EAAA,CAGhC,KAAI,SAAQ;IACxB,IACE,KAAK,SAAS,gBACd,KAAK,SAAS,iBACd,KAAK,SAAS,cAEd,MAAM,IAAI,MAAM,wBAAwB,KAAK,MAAM;IAGrD,OAAO;KACL,UAAU,KAAK;KACf,SAAS,KAAK;KACd,MAAM,KAAK;KACX,OAAO,KAAK;IACd;GACF,CAAC;EACH;EACA,QAAQ,UAAmB;CAC7B,CAAC,GACD;EACE,YAAW,UACT,OAAO,QACL,SAAS,KAAK,IACV,oBAAoB,OAAO,IAC3B,oBAAoB,OAAO,EAAE,SAAS,YAAY,KAAK,EAAE,CAAC,CAChE;EACF,YAAW,YACT,OAAO,QAAQ,oBAAoB,UAAU,EAAE,SAAS,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC;CAC3E,CACF;CAEA,MAAM,cAAc;EAAE,MAAM,GAAG,OAAO,KAAK;EAAQ,QAAQ;CAAW;CAEtE,MAAM,sBAA0D;EAC9D,MAAM,UAAU,OAAO;EACvB,IAAI,YAAY,MAAM,OAAO,OAAO;EAGpC,OAAO,OAAO,kBAAkB,SAAmC,cAAc,CAAC,CAAC,KACjF,OAAO,UAAU,oBAAoB,eAAe,CAAC,CACvD;CACF;CAEA,OAAO,OAAO,KAAK,OAAO,MAAM;EAC9B,OAAO;EACP,SAAS;EACT,aAAa;GACX,OAAO;IAAE,QAAQ;IAAW,SAAS,CAAC;IAAG,WAAW,OAAO,KAAK;GAAE;GAClE,UAAU,CAAC,KAAK,CAAC;EACnB;EACA,SAAS,OAAO,YACd,oBAAoB,MAElB,SAAS;GACT,aAAa;IAAE;IAAO,UAAU,CAAC,KAAK,CAAC;GAAE;GACzC,uBAAuB;IAAE;IAAO,UAAU,CAAC,KAAK,CAAC;GAAE;GAEnD,YAAY,EAAE,cACZ,MAAM,WAAW,WAAW,YAAY,MAAM,SAAS,OAAO,IAC1D,EAAE,MAAM,IACR,EAAE,OAAO;IAAE,GAAG;IAAO,QAAQ;IAAkB;GAAQ,EAAE;GAC/D,eAAe,EAAE,OAAO,WAAW,OAAO;IAAE,GAAG;IAAO,QAAQ;GAAS,CAAC,EAAE;GAC1E,SAAS,EAAE,eAAe,EACxB,OAAO,WAAW,OAAO;IAAE,GAAG;IAAO,WAAW,OAAO,KAAK,OAAO;GAAE,CAAC,EACxE;EACF,CAAC;EACH,qBACE,aAAa,KAA6C,CAAC,QAAQ,EACjE,SAAS,aAAa,gBAAgB,cAAc,CAAC,EACvD,EAAE;CACN,CAAC;AACH"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import { Bundle } from "foldkit-bundle";
|
|
3
|
+
import * as ManagedResource from "foldkit/managedResource";
|
|
4
|
+
export declare const MediaStreamModel: Schema.Struct<{
|
|
5
|
+
readonly status: Schema.Literals<readonly ["idle", "requesting", "live", "denied"]>;
|
|
6
|
+
readonly lastError: Schema.OptionFromNullOr<Schema.String>;
|
|
7
|
+
}>;
|
|
8
|
+
export type MediaStreamModel = typeof MediaStreamModel.Type;
|
|
9
|
+
export declare const MediaStreamMessage: import("foldkit/message").MessageUnion<{
|
|
10
|
+
readonly Started: {};
|
|
11
|
+
readonly Stopped: {};
|
|
12
|
+
readonly Live: {};
|
|
13
|
+
readonly Ended: {};
|
|
14
|
+
readonly Denied: {};
|
|
15
|
+
readonly Failed: {
|
|
16
|
+
message: Schema.String;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
19
|
+
export type MediaStreamMessage = typeof MediaStreamMessage.Type;
|
|
20
|
+
/** A single media track: stopped on release. */
|
|
21
|
+
export interface MediaTrackHandle {
|
|
22
|
+
stop(): void;
|
|
23
|
+
}
|
|
24
|
+
/** The live stream: its tracks stop on release. */
|
|
25
|
+
export interface MediaStreamHandle {
|
|
26
|
+
getTracks(): ReadonlyArray<MediaTrackHandle>;
|
|
27
|
+
}
|
|
28
|
+
/** Which tracks `mediaStream` asks `getUserMedia` for. */
|
|
29
|
+
export interface MediaStreamConstraints {
|
|
30
|
+
readonly audio: boolean;
|
|
31
|
+
readonly video: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** The live stream, for commands that attach it to an element. One assembly holds one. */
|
|
34
|
+
export declare const LiveStream: ManagedResource.ManagedResource<MediaStreamHandle, ManagedResource.ManagedResourceService<"media-stream">>;
|
|
35
|
+
/** The stream service a placed camera requires. */
|
|
36
|
+
export type LiveStreamService = ManagedResource.ServiceOf<typeof LiveStream>;
|
|
37
|
+
/**
|
|
38
|
+
* A camera/mic stream as a bundle factory: one live MediaStream held in a
|
|
39
|
+
* Managed Resource while the Model asks for it. `Started` requests,
|
|
40
|
+
* `Stopped` releases; denial parks at `denied` (actionable UI), any other
|
|
41
|
+
* acquire failure parks at `idle` with the error noted — both clear
|
|
42
|
+
* requirements, so a failing device never spins an acquire loop. Release
|
|
43
|
+
* stops every track. No media-devices API fails the acquire instead of
|
|
44
|
+
* throwing.
|
|
45
|
+
*/
|
|
46
|
+
export declare const mediaStream: <const Name extends string>(config: {
|
|
47
|
+
readonly name: Name;
|
|
48
|
+
/** Read lazily so tests can substitute a double, like the SSE bundle. */
|
|
49
|
+
readonly request?: ((constraints: MediaStreamConstraints) => Promise<MediaStreamHandle>) | undefined;
|
|
50
|
+
}) => Bundle.Bundle<Name, {
|
|
51
|
+
readonly audio: boolean;
|
|
52
|
+
readonly video: boolean;
|
|
53
|
+
}, {
|
|
54
|
+
readonly status: "denied" | "idle" | "live" | "requesting";
|
|
55
|
+
readonly lastError: Option.Option<string>;
|
|
56
|
+
}, {
|
|
57
|
+
readonly _tag: "Denied";
|
|
58
|
+
} | {
|
|
59
|
+
readonly _tag: "Ended";
|
|
60
|
+
} | {
|
|
61
|
+
readonly _tag: "Failed";
|
|
62
|
+
readonly message: string;
|
|
63
|
+
} | {
|
|
64
|
+
readonly _tag: "Live";
|
|
65
|
+
} | {
|
|
66
|
+
readonly _tag: "Started";
|
|
67
|
+
} | {
|
|
68
|
+
readonly _tag: "Stopped";
|
|
69
|
+
}, never, never, never, void, {
|
|
70
|
+
stream: ManagedResource.Entry<{
|
|
71
|
+
readonly status: "denied" | "idle" | "live" | "requesting";
|
|
72
|
+
readonly lastError: Option.Option<string>;
|
|
73
|
+
}, {
|
|
74
|
+
readonly _tag: "Denied";
|
|
75
|
+
} | {
|
|
76
|
+
readonly _tag: "Ended";
|
|
77
|
+
} | {
|
|
78
|
+
readonly _tag: "Failed";
|
|
79
|
+
readonly message: string;
|
|
80
|
+
} | {
|
|
81
|
+
readonly _tag: "Live";
|
|
82
|
+
} | {
|
|
83
|
+
readonly _tag: "Started";
|
|
84
|
+
} | {
|
|
85
|
+
readonly _tag: "Stopped";
|
|
86
|
+
}, Option.Option<{
|
|
87
|
+
readonly audio: boolean;
|
|
88
|
+
readonly video: boolean;
|
|
89
|
+
}>, MediaStreamHandle, ManagedResource.ManagedResourceService<"media-stream">, () => {
|
|
90
|
+
readonly _tag: "Live";
|
|
91
|
+
}>;
|
|
92
|
+
}, {}>;
|
|
93
|
+
//# sourceMappingURL=media-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-stream.d.ts","names":[],"sources":["../../src/device/media-stream.ts"],"mappings":";;;qBAOa,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,YACH,aAAa,2BAA2B,QAAQ;MAEvD,OAAA,OAAA"}
|