@wcstack/camera 4.7.0 → 4.9.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.ja.md CHANGED
@@ -157,6 +157,7 @@ Core の構造サーフェスは wcstack I/O ノード横断の規範です([asy
157
157
 
158
158
  ## 注意・落とし穴
159
159
 
160
+ - **4.8 から、`<wcs-camera>` は接続の 1 マイクロタスク後に決める**: 開始するか(`autostart`)、どの `device-id` / `facing-mode` / `audio` / `width` / `height` でかを、そのときの属性で([タイミング契約](../../docs/timing-and-firing-contract.ja.md) §23)。`@wcstack/router` はパラメータだけの遷移でルートの要素を入れ直し、`@wcstack/state` が次のルートの入力を書くのはその後 — その場で始めると前のルートのデバイスを取得し、その映像が残っていた。制約の変化は次のマイクロタスクで 1 回にまとめて取得し直す。`start()` は今の属性を使い、同じタスクの `stop()` は自動開始を取り消す。
160
161
  - **セキュアコンテキスト(https)必須。** `getUserMedia` は `file://` / 素の `http://` では使えません。
161
162
  - **Content-Security-Policy。** 4.5 から、プレビュー自身のスタイルは構築したスタイルシートで、`style-src` の対象外です。`'unsafe-inline'` の無いポリシーでもプレビューの配置が保たれます。4.4 までは shadow root の `<style>` で、そうしたポリシーに止められました(プレビューの `<video>` が元の大きさで描かれる)。その版では自分のスタイルシートで `wcs-camera::part(video) { display: block; width: 100%; height: 100% }` と書いてください。
162
163
  - **`error` / `errorInfo` は最後の試みを表します。** 後から(サイトの設定で)許可されても消えません。`permission` はすぐに許可に追従しますが、`errorInfo` は次の `start()` が成功するまで `not-allowed` のままです。「止められている」は `permission` で判断し、最後のエラーが `not-allowed` で `permission` が `granted` なら、もう一度開始を促してください。
@@ -169,7 +170,7 @@ Core の構造サーフェスは wcstack I/O ノード横断の規範です([asy
169
170
  - `not-found`(phase `start`)——`NotFoundError`(要求した種類のカメラ / マイクが存在しない)。
170
171
  - `not-readable`(phase `start`)——`NotReadableError`(デバイス占有・ハードウェア障害)。
171
172
  - `invalid-argument`(phase `start`)——`OverconstrainedError` / `NotSupportedError`(制約・mimeType が満たせない)。
172
- - `invalid-state`(phase `start`)——`NoStreamError`(stream 未 attach で録画開始 — 何も録画されず、要素が `[wcs/command-not-ready]` で警告する。1 要素につき 1 回)。
173
+ - `invalid-state`(phase `start`)——`NoStreamError`(stream 未 attach で録画開始 — 何も録画されず、要素が `[wcs/command-not-ready]` で警告する。stream がまた attach されるまで 1 回 — 4.8 から。それまでは 1 要素につき 1 回)。
173
174
  - `aborted`(phase `execute`・`recoverable: true`)——`AbortError`(実行途中の中断、retry で回復しうる)。
174
175
  - `media-error`(phase `execute`)——その他の実行時失敗(例: `RecorderError` / 想定外の `MediaRecorder` エラー)。
175
176
 
package/README.md CHANGED
@@ -158,6 +158,7 @@ The structural Core surface is normative across wcstack IO nodes ([async-io-node
158
158
 
159
159
  ## Notes & gotchas
160
160
 
161
+ - **Since 4.8, `<wcs-camera>` decides on connect one microtask later**: whether to start (`autostart`) and with which `device-id` / `facing-mode` / `audio` / `width` / `height`, with the attributes as they are then ([timing contract](../../docs/timing-and-firing-contract.md) §23). `@wcstack/router` puts a route's elements back on a parameter-only navigation before `@wcstack/state` writes the next route's inputs: starting at once acquired the previous route's device, and that stream stayed live. A constraint change re-acquires in the next microtask, folded into one. `start()` uses the current attributes; `stop()` in the same task cancels the auto-start.
161
162
  - **Secure context (https) required.** `getUserMedia` is unavailable on `file://` / plain `http://`.
162
163
  - **Content-Security-Policy.** The preview's own styles are a constructed stylesheet since 4.5, which `style-src` does not govern, so a policy without `'unsafe-inline'` keeps the preview's layout. Up to 4.4 they were a `<style>` in the shadow root, which such a policy blocks (the preview `<video>` rendered at its own size): style it from your stylesheet with `wcs-camera::part(video) { display: block; width: 100%; height: 100% }`.
163
164
  - **`error` / `errorInfo` describe the last attempt.** A permission granted later (in the site settings) does not clear them: `permission` follows the grant at once, while `errorInfo` keeps `not-allowed` until the next `start()` succeeds. Decide "blocked" from `permission`, and offer Start again when the last error was `not-allowed` and `permission` is `granted`.
@@ -170,7 +171,7 @@ The structural Core surface is normative across wcstack IO nodes ([async-io-node
170
171
  - `not-found` (phase `start`) — `NotFoundError` (no camera / mic of the requested kind).
171
172
  - `not-readable` (phase `start`) — `NotReadableError` (device busy or hardware fault).
172
173
  - `invalid-argument` (phase `start`) — `OverconstrainedError` / `NotSupportedError` (constraints or mimeType unsatisfiable).
173
- - `invalid-state` (phase `start`) — `NoStreamError` (recorder started with no stream attached — nothing is recorded, and the element warns `[wcs/command-not-ready]`, once per element).
174
+ - `invalid-state` (phase `start`) — `NoStreamError` (recorder started with no stream attached — nothing is recorded, and the element warns `[wcs/command-not-ready]`, once until a stream is attached again — since 4.8; before, once per element).
174
175
  - `aborted` (phase `execute`, `recoverable: true`) — `AbortError` (interrupted mid-flight, may recover on retry).
175
176
  - `media-error` (phase `execute`) — any other runtime failure (e.g. `RecorderError` / unexpected `MediaRecorder` error).
176
177
 
package/dist/auto.min.js CHANGED
@@ -1,2 +1,2 @@
1
- const e={tagNames:{camera:"wcs-camera",recorder:"wcs-recorder"}},t=new Set(["camera","microphone","geolocation","midi","clipboard-read","clipboard-write","screen-wake-lock","idle-detection","accelerometer","gyroscope","magnetometer","ambient-light-sensor","display-capture"]);function i(e){const i=globalThis;if(!1===i.isSecureContext)return"insecure-context";if(!t.has(e))return null;const s=i.document?.permissionsPolicy??i.document?.featurePolicy;if(void 0===s||"function"!=typeof s.allowsFeature)return null;try{return s.allowsFeature(e)?null:"policy-blocked"}catch{return null}}function s(e,t,i){return"insecure-context"===i?`${e} needs a secure context (https:, or http://localhost), and this page is not one.`:`${e} is turned off for this page by a Permissions-Policy: the server's Permissions-Policy header, or an <iframe> without allow="${t}". The browser's site settings cannot change that.`}function r(){return"undefined"!=typeof navigator&&!!navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getUserMedia}function n(e){if(e&&"object"==typeof e&&"name"in e){const t=String(e.name)||"Error";return{name:t,message:"message"in e&&e.message?String(e.message):`Media request failed: ${t}.`}}return{name:"Error",message:"Media request failed."}}function a(){const e=i("camera");return{name:"unsupported",message:"insecure-context"===e?s("getUserMedia","camera",e):"getUserMedia is not available in this browser."}}function o(e){if(e)for(const t of e.getTracks())t.stop()}async function c(){if(!r()||"function"!=typeof navigator.mediaDevices.enumerateDevices)return[];try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>"videoinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label,groupId:e.groupId,kind:e.kind}))}catch{return[]}}class d{_name;_onChange;_status=null;_gen=0;_subscribed=!1;constructor(e,t){this._name=e,this._onChange=t}observe(){if(this._subscribed)return Promise.resolve();if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return this._onChange("unsupported"),Promise.resolve();this._subscribed=!0;const e=++this._gen;return navigator.permissions.query({name:this._name}).then(t=>{e===this._gen&&(this._status=t,this._onChange(t.state),t.addEventListener("change",this._onStatusChange))},()=>{e===this._gen&&this._onChange("unsupported")})}dispose(){this._subscribed=!1,this._gen++,this._status&&(this._status.removeEventListener("change",this._onStatusChange),this._status=null)}_onStatusChange=e=>{const t=e.target;this._onChange(t.state)}}const h=new WeakMap;function u(e){const t=(h.get(e)??0)-1;t>0?h.set(e,t):h.delete(e)}const m="capability-missing",l="not-allowed",_="not-found",g="not-readable",p="invalid-argument",v="invalid-state",b="aborted",f="media-error";function w(e){const{name:t,message:i}=e;return"unsupported"===t?{code:m,phase:"probe",recoverable:!1,message:i}:"NotAllowedError"===t||"SecurityError"===t?{code:l,phase:"start",recoverable:!1,message:i}:"NotFoundError"===t?{code:_,phase:"start",recoverable:!1,message:i}:"NotReadableError"===t?{code:g,phase:"start",recoverable:!1,message:i}:"OverconstrainedError"===t||"NotSupportedError"===t?{code:p,phase:"start",recoverable:!1,message:i}:"NoStreamError"===t?{code:v,phase:"start",recoverable:!1,message:i}:"AbortError"===t?{code:b,phase:"execute",recoverable:!0,message:i}:{code:f,phase:"execute",recoverable:!1,message:i}}class y extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-camera:active-changed",semantics:"state"},{name:"permission",event:"wcs-camera:permission-changed",semantics:"state"},{name:"audioPermission",event:"wcs-camera:audio-permission-changed",semantics:"state"},{name:"deviceId",event:"wcs-camera:device-changed",semantics:"state"},{name:"devices",event:"wcs-camera:devices-changed",semantics:"state"},{name:"error",event:"wcs-camera:error",semantics:"state"},{name:"errorInfo",event:"wcs-camera:error-info-changed",semantics:"state"},{name:"streamReady",event:"wcs-camera:stream-ready",semantics:"handle",getter:e=>e.detail},{name:"ended",event:"wcs-camera:ended",semantics:"event",getter:e=>e.detail}],commands:[{name:"start"},{name:"stop"},{name:"switchCamera"}]};_target;_active=!1;_permission="prompt";_audioPermission=null;_deviceId=null;_devices=[];_error=null;_errorInfo=null;_stream=null;_desired=!1;_constraints={};_gen=0;_subscribed=!1;_camWatcher=null;_micWatcher=null;_ready=Promise.resolve();_toldKeepAlive=!1;constructor(e){super(),this._target=e??this}get active(){return this._active}get permission(){return this._permission}get audioPermission(){return this._audioPermission}get deviceId(){return this._deviceId}get devices(){return this._devices}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setActive(e){this._active!==e&&(this._active=e,this._dispatch("wcs-camera:active-changed",e))}_setPermission(e){this._permission!==e&&(this._permission=e,this._dispatch("wcs-camera:permission-changed",e))}_setAudioPermission(e){this._audioPermission!==e&&(this._audioPermission=e,this._dispatch("wcs-camera:audio-permission-changed",e))}_setDeviceId(e){this._deviceId!==e&&(this._deviceId=e,this._dispatch("wcs-camera:device-changed",e))}_setDevices(e){this._devicesEqual(this._devices,e)||(this._devices=e,this._dispatch("wcs-camera:devices-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:w(e)),this._dispatch("wcs-camera:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-camera:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}_devicesEqual(e,t){if(e.length!==t.length)return!1;for(let i=0;i<e.length;i++)if(e[i].deviceId!==t[i].deviceId||e[i].label!==t[i].label)return!1;return!0}observe(e){return this._constraints={...e},this._subscribed?this._ready=this._ready.then(()=>this._reconcileAudioWatcher()):(this._subscribed=!0,this._ready=this._initPermissions()),this._ready}start(){this._desired=!0,this._restart()}stop(){this._desired=!1,this._release()}switchCamera(){const e="environment"===this._constraints.facingMode?"user":"environment";this._constraints={...this._constraints,facingMode:e,deviceId:void 0},this._desired&&this._restart()}suspend(){var e;this._gen++,this._stream&&(e=this._stream,h.has(e)&&this._warnKeepAlive(),this._release())}_warnKeepAlive(){this._toldKeepAlive||(this._toldKeepAlive=!0,console.warn("[@wcstack/camera] [wcs/camera-keep-alive-missing] The page was hidden while this camera's stream was being recorded, so the camera stopped the stream and the recording ended with it. Keep the camera on while recording: bind keepAlive to the recording flag (keepAlive: <recording path>). (Said once per element.)",this._target))}resume(){this._desired&&!this._active&&this._restart()}dispose(){this._subscribed=!1,this._gen++,this._desired=!1,this._release(),r()&&"function"==typeof navigator.mediaDevices.removeEventListener&&navigator.mediaDevices.removeEventListener("devicechange",this._onDeviceChange),this._camWatcher?.dispose(),this._micWatcher?.dispose(),this._camWatcher=null,this._micWatcher=null}_initPermissions(){if(!r())return this._setPermission("unsupported"),Promise.resolve();const e=[];return null!==i("camera")?this._setPermission("unsupported"):(this._camWatcher=new d("camera",e=>this._setPermission(e)),e.push(this._camWatcher.observe())),this._constraints.audio&&e.push(this._watchMicrophone()),"function"==typeof navigator.mediaDevices.addEventListener&&navigator.mediaDevices.addEventListener("devicechange",this._onDeviceChange),Promise.all(e).then(()=>{})}_onDeviceChange=()=>{c().then(e=>{this._subscribed&&this._setDevices(e)})};_reconcileAudioWatcher(){return this._constraints.audio&&!this._micWatcher&&r()?this._watchMicrophone():(this._constraints.audio||!this._micWatcher&&null===this._audioPermission||(this._micWatcher?.dispose(),this._micWatcher=null,this._setAudioPermission(null)),Promise.resolve())}_watchMicrophone(){return null!==i("microphone")?(this._setAudioPermission("unsupported"),Promise.resolve()):(this._micWatcher=new d("microphone",e=>this._setAudioPermission(e)),this._micWatcher.observe())}_restart(){this._release(),this._acquire()}async _acquire(){const e=++this._gen,t=r()?function(e){const t=i("camera");if(null!==t)return{device:"camera",error:{name:"unsupported",message:s("The camera","camera",t)}};const r=e?i("microphone"):null;return null!==r?{device:"microphone",error:{name:"unsupported",message:s("The microphone","microphone",r)}}:null}(!!this._constraints.audio):null;if(null!==t)return this._setError(t.error),"camera"===t.device?this._setPermission("unsupported"):this._setAudioPermission("unsupported"),this._desired=!1,void this._setActive(!1);const d=function(e){const t={};return e.deviceId?t.deviceId={exact:e.deviceId}:e.facingMode&&(t.facingMode=e.facingMode),"number"==typeof e.width&&(t.width=e.width),"number"==typeof e.height&&(t.height=e.height),{video:!(Object.keys(t).length>0)||t,audio:!0===e.audio}}(this._constraints),{stream:h,error:u}=await async function(e){if(!r())return{error:a()};try{return{stream:await navigator.mediaDevices.getUserMedia(e)}}catch(e){return{error:n(e)}}}(d);if(e!==this._gen)return void o(h??null);if(u)return this._setError(u),"NotAllowedError"===u.name?(this._setPermission("denied"),this._desired=!1):"unsupported"===u.name&&(this._setPermission("unsupported"),this._desired=!1),void this._setActive(!1);const m=h;this._stream=m;for(const e of m.getTracks())e.addEventListener("ended",this._onTrackEnded);this._setError(null),this._setPermission("granted"),this._constraints.audio&&m.getAudioTracks().length>0&&this._setAudioPermission("granted"),this._updateDeviceId(m),this._setActive(!0),this._dispatch("wcs-camera:stream-ready",m);const l=await c();e===this._gen&&this._setDevices(l)}_release(){if(this._stream){for(const e of this._stream.getTracks())e.removeEventListener("ended",this._onTrackEnded);o(this._stream),this._stream=null,this._setActive(!1)}}_onTrackEnded=()=>{this._release(),this._dispatch("wcs-camera:ended",null)};_updateDeviceId(e){const t=e.getVideoTracks()[0];if(t&&"function"==typeof t.getSettings){const e=t.getSettings();this._setDeviceId(e.deviceId??null)}}}function P(e,t){let i=Object.getPrototypeOf(e);for(;null!==i;){const e=Object.getOwnPropertyDescriptor(i,t);if(void 0!==e)return"function"==typeof e.get||"function"==typeof e.set;i=Object.getPrototypeOf(i)}return!1}const S=new WeakSet;function A(e,t,i){if(S.has(e))return;S.add(e);const s=e.localName??"element";console.warn(`[wcs/input-as-attribute] <${s}> has the attribute ${t}="${i}": an object was written as an attribute, so the element did not receive it. A framework does that when the property is set before the element's definition loads (React 19), or when told to (Vue's .attr). Load the definition before rendering, or assign the property through a ref (Vue: .prop). (Said once per element.)`,e)}function E(e){const t=e.constructor?.wcBindable,i=t?.inputs;if(void 0===i)return;const s=e.getAttribute;for(const t of i){const i=t.name,r="function"==typeof s?s.call(e,i.toLowerCase()):null;if(null!==r&&r.startsWith("[object ")&&A(e,i,r),!Object.prototype.hasOwnProperty.call(e,i))continue;if(!P(e,i))continue;const n=e,a=n[i];delete n[i],n[i]=a}}const T=new WeakMap;function k(e,t){let i=T.get(e);return void 0===i&&(i=new Map,T.set(e,i)),i.has(t)||i.set(t,e.getAttribute(t)),i.get(t)}function I(e,t,i){null===i?e.removeAttribute(t):e.setAttribute(t,i)}function C(e,t,i){const s=k(e,t);I(e,t,void 0===i?s:null===i?null:String(i))}function R(e,t,i){const s=k(e,t);I(e,t,void 0===i?s:i?"":null)}const L=":host{display:inline-block}video{display:block;width:100%;height:100%}";let N;class M extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...y.wcBindable,inputs:[{name:"audio",attribute:"audio"},{name:"facingMode",attribute:"facing-mode"},{name:"deviceId",attribute:"device-id"},{name:"width",attribute:"width"},{name:"height",attribute:"height"},{name:"autostart",attribute:"autostart"},{name:"keepAlive",attribute:"keep-alive"}],commands:y.wcBindable.commands};static observedAttributes=["facing-mode","device-id","audio","width","height"];_core;_video;_connectedCallbackPromise=Promise.resolve();_connected=!1;_batchingAttrs=!1;_internals=null;constructor(){super(),this._core=new y(this);const e=this.attachShadow({mode:"open"});!function(e){if(void 0===N)try{N=new CSSStyleSheet,N.replaceSync(L)}catch{N=null}if(null!==N&&Array.isArray(e.adoptedStyleSheets))return void(e.adoptedStyleSheets=[N]);const t=document.createElement("style");t.textContent=L,e.append(t)}(e),this._video=document.createElement("video"),this._video.autoplay=!0,this._video.muted=!0,this._video.setAttribute("playsinline",""),this._video.setAttribute("part","video"),e.append(this._video),this.addEventListener("wcs-camera:stream-ready",this._onStreamReady),this.addEventListener("wcs-camera:active-changed",this._onActiveChanged),this._internals=this._initInternals(),this._wireStates({"wcs-camera:active-changed":e=>({active:!0===e}),"wcs-camera:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get audio(){return this.hasAttribute("audio")}set audio(e){R(this,"audio",e)}get facingMode(){return"environment"===this.getAttribute("facing-mode")?"environment":"user"}set facingMode(e){C(this,"facing-mode",e)}get deviceId(){return this._core.deviceId??this.getAttribute("device-id")??""}set deviceId(e){C(this,"device-id",e)}get width(){return this._numberAttr("width")}set width(e){C(this,"width",e)}get height(){return this._numberAttr("height")}set height(e){C(this,"height",e)}get autostart(){return this.hasAttribute("autostart")}set autostart(e){R(this,"autostart",e)}get keepAlive(){return this.hasAttribute("keep-alive")}set keepAlive(e){R(this,"keep-alive",e)}get videoElement(){return this._video}get active(){return this._core.active}get permission(){return this._core.permission}get audioPermission(){return this._core.audioPermission}get devices(){return this._core.devices}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}get connectedCallbackPromise(){return this._connectedCallbackPromise}start(){this._core.start()}stop(){this._core.stop()}switchCamera(){const e="environment"===this.facingMode?"user":"environment";this._batchingAttrs=!0;try{this.removeAttribute("device-id"),this.setAttribute("facing-mode",e)}finally{this._batchingAttrs=!1}this._core.observe(this._constraints()),this._core.active&&this._core.start()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_constraints(){const e={audio:this.audio,facingMode:this.facingMode},t=this.getAttribute("device-id");return t&&(e.deviceId=t),Number.isFinite(this.width)&&(e.width=this.width),Number.isFinite(this.height)&&(e.height=this.height),e}_onStreamReady=e=>{this._video.srcObject=e.detail};_onActiveChanged=e=>{!1===e.detail&&(this._video.srcObject=null)};connectedCallback(){E(this),this._connected=!0,this._connectedCallbackPromise=this._core.observe(this._constraints()),this.autostart&&this._core.start(),document.addEventListener("visibilitychange",this._onVisibilityChange)}disconnectedCallback(){this._connected=!1,document.removeEventListener("visibilitychange",this._onVisibilityChange),this._core.dispose()}attributeChangedCallback(e,t,i){this._connected&&t!==i&&(this._batchingAttrs||(this._core.observe(this._constraints()),this._core.active&&this._core.start()))}_onVisibilityChange=()=>{this.keepAlive||("hidden"===document.visibilityState?this._core.suspend():this._core.resume())}}class B extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"recording",event:"wcs-recorder:recording-changed",semantics:"state"},{name:"paused",event:"wcs-recorder:paused-changed",semantics:"state"},{name:"duration",event:"wcs-recorder:duration-changed",semantics:"state"},{name:"mimeType",event:"wcs-recorder:mimetype-changed",semantics:"state"},{name:"blob",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.blob??null},{name:"objectURL",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.objectURL??null},{name:"error",event:"wcs-recorder:error",semantics:"state"},{name:"errorInfo",event:"wcs-recorder:error-info-changed",semantics:"state"},{name:"recorded",event:"wcs-recorder:recorded",semantics:"event",getter:e=>e.detail},{name:"dataavailable",event:"wcs-recorder:dataavailable",semantics:"event",getter:e=>e.detail}],commands:[{name:"attachStream"},{name:"start"},{name:"stop"},{name:"pause"},{name:"resume"}]};_target;_recording=!1;_paused=!1;_duration=0;_mimeType="";_blob=null;_objectURL=null;_error=null;_errorInfo=null;_recorder=null;_stream=null;_recordingStream=null;_timeslice=!1;_startTime=0;_gen=0;_closing=null;_ready=Promise.resolve();_toldNotReady=!1;constructor(e){super(),this._target=e??this}get recording(){return this._recording}get paused(){return this._paused}get duration(){return this._duration}get mimeType(){return this._mimeType}get blob(){return this._blob}get objectURL(){return this._objectURL}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setRecording(e){this._recording!==e&&(this._recording=e,this._dispatch("wcs-recorder:recording-changed",e))}_setPaused(e){this._paused!==e&&(this._paused=e,this._dispatch("wcs-recorder:paused-changed",e))}_setDuration(e){this._duration!==e&&(this._duration=e,this._dispatch("wcs-recorder:duration-changed",e))}_setMimeType(e){this._mimeType!==e&&(this._mimeType=e,this._dispatch("wcs-recorder:mimetype-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:w(e)),this._dispatch("wcs-recorder:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-recorder:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}attachStream(e){this._stream=e}start(e={}){if("undefined"==typeof globalThis||"function"!=typeof globalThis.MediaRecorder)return void this._setError({name:"unsupported",message:"MediaRecorder is not available in this environment."});if(!this._stream)return this._setError({name:"NoStreamError",message:"No stream attached. Wire a camera's stream-ready to attachStream first."}),void this._warnNotReady();if(this._recording)return;const t={};let i;e.mimeType&&this._isTypeSupported(e.mimeType)&&(t.mimeType=e.mimeType),"number"==typeof e.audioBitsPerSecond&&(t.audioBitsPerSecond=e.audioBitsPerSecond),"number"==typeof e.videoBitsPerSecond&&(t.videoBitsPerSecond=e.videoBitsPerSecond);try{i=new MediaRecorder(this._stream,t)}catch(e){return void this._setError(n(e))}const s=++this._gen,r=[],a=this._stream;this._timeslice="number"==typeof e.timeslice&&e.timeslice>0;const o=()=>null!==this._closing&&this._closing.gen===s;i.ondataavailable=e=>{if((s===this._gen||o())&&e.data&&e.data.size>0){if(r.push(e.data),s!==this._gen)return;!this._mimeType&&e.data.type&&this._setMimeType(e.data.type),this._timeslice&&this._dispatch("wcs-recorder:dataavailable",e.data)}},i.onstart=()=>{s===this._gen&&i.mimeType&&this._setMimeType(i.mimeType)},i.onstop=()=>{if(u(a),this._recordingStream===a&&(this._recordingStream=null),o()){const{mimeType:e,duration:t}=this._closing;return this._closing=null,void this._assembleBlob(r,e,t)}s===this._gen&&(this._paused||this._setDuration(this._elapsed()),this._assembleBlob(r,this._mimeType,this._duration),this._setPaused(!1),this._setRecording(!1))},i.onerror=e=>{if(s!==this._gen)return;const t=e.error;this._setError(n(t??{name:"RecorderError"}))},i.onpause=()=>{s===this._gen&&(this._setDuration(this._elapsed()),this._setPaused(!0))},i.onresume=()=>{s===this._gen&&(this._startTime=this._now()-this._duration,this._setPaused(!1))},this._recorder=i,this._setError(null),this._setMimeType(i.mimeType||t.mimeType||""),this._startTime=this._now(),this._setDuration(0),i.start(this._timeslice?e.timeslice:void 0),function(e){h.set(e,(h.get(e)??0)+1)}(a),this._recordingStream=a,this._setRecording(!0)}_warnNotReady(){this._toldNotReady||(this._toldNotReady=!0,console.warn("[@wcstack/camera] [wcs/command-not-ready] start() was refused: no stream is attached, so nothing is recorded. It goes to `error` too. Wire a camera's stream to the recorder first. (Said once per element.)",this._target))}stop(){this._recorder&&"inactive"!==this._recorder.state&&this._recorder.stop()}pause(){this._recorder&&"recording"===this._recorder.state&&this._recorder.pause()}resume(){this._recorder&&"paused"===this._recorder.state&&this._recorder.resume()}observe(){return this._ready}dispose(){const e=this._recorder,t=null!==e&&"inactive"!==e.state;if(t&&(this._paused||this._setDuration(this._elapsed()),this._closing={gen:this._gen,mimeType:this._mimeType,duration:this._duration}),this._gen++,this._recorder=null,this._revokeUrl(),this._stream=null,this._setPaused(!1),this._setRecording(!1),t)try{e.stop()}catch{this._closing=null,this._recordingStream&&u(this._recordingStream),this._recordingStream=null}}_assembleBlob(e,t,i){const s=t||e[0]?.type||"",r=new Blob(e,s?{type:s}:void 0);this._revokeUrl();const n=this._createUrl(r);this._blob=r,this._objectURL=n;const a={blob:r,objectURL:n,mimeType:s,duration:i};this._dispatch("wcs-recorder:recorded",a)}_revokeUrl(){this._objectURL&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(this._objectURL),this._objectURL=null}_createUrl(e){return"undefined"!=typeof URL&&"function"==typeof URL.createObjectURL?URL.createObjectURL(e):""}_isTypeSupported(e){return globalThis.MediaRecorder.isTypeSupported(e)}_now(){return performance.now()}_elapsed(){return Math.max(0,Math.round(this._now()-this._startTime))}}class U extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...B.wcBindable,inputs:[{name:"mimeType",attribute:"mime-type"},{name:"timeslice",attribute:"timeslice"},{name:"audioBitsPerSecond",attribute:"audio-bits"},{name:"videoBitsPerSecond",attribute:"video-bits"}],commands:B.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new B(this),this._internals=this._initInternals(),this._wireStates({"wcs-recorder:recording-changed":e=>({recording:!0===e}),"wcs-recorder:paused-changed":e=>({paused:!0===e}),"wcs-recorder:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get connectedCallbackPromise(){return this._connectedCallbackPromise}get mimeType(){return this._core.mimeType}set mimeType(e){C(this,"mime-type",e)}get timeslice(){return this._numberAttr("timeslice")}set timeslice(e){C(this,"timeslice",e)}get audioBitsPerSecond(){return this._numberAttr("audio-bits")}set audioBitsPerSecond(e){C(this,"audio-bits",e)}get videoBitsPerSecond(){return this._numberAttr("video-bits")}set videoBitsPerSecond(e){C(this,"video-bits",e)}get recording(){return this._core.recording}get paused(){return this._core.paused}get duration(){return this._core.duration}get blob(){return this._core.blob}get objectURL(){return this._core.objectURL}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}attachStream(e){this._core.attachStream(e)}start(){this._core.start(this._options())}stop(){this._core.stop()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_options(){const e={},t=this.getAttribute("mime-type")??"";return t&&(e.mimeType=t),Number.isFinite(this.timeslice)&&(e.timeslice=this.timeslice),Number.isFinite(this.audioBitsPerSecond)&&(e.audioBitsPerSecond=this.audioBitsPerSecond),Number.isFinite(this.videoBitsPerSecond)&&(e.videoBitsPerSecond=this.videoBitsPerSecond),e}connectedCallback(){E(this),this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}var j;!function(t=customElements){t.get(e.tagNames.camera)||t.define(e.tagNames.camera,M),t.get(e.tagNames.recorder)||t.define(e.tagNames.recorder,U)}(j);
1
+ const e={tagNames:{camera:"wcs-camera",recorder:"wcs-recorder"}},t=new Set(["camera","microphone","geolocation","midi","clipboard-read","clipboard-write","screen-wake-lock","idle-detection","accelerometer","gyroscope","magnetometer","ambient-light-sensor","display-capture"]);function i(e){const i=globalThis;if(!1===i.isSecureContext)return"insecure-context";if(!t.has(e))return null;const s=i.document?.permissionsPolicy??i.document?.featurePolicy;if(void 0===s||"function"!=typeof s.allowsFeature)return null;try{return s.allowsFeature(e)?null:"policy-blocked"}catch{return null}}function s(e,t,i){return"insecure-context"===i?`${e} needs a secure context (https:, or http://localhost), and this page is not one.`:`${e} is turned off for this page by a Permissions-Policy: the server's Permissions-Policy header, or an <iframe> without allow="${t}". The browser's site settings cannot change that.`}function r(){return"undefined"!=typeof navigator&&!!navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getUserMedia}function n(e){if(e&&"object"==typeof e&&"name"in e){const t=String(e.name)||"Error";return{name:t,message:"message"in e&&e.message?String(e.message):`Media request failed: ${t}.`}}return{name:"Error",message:"Media request failed."}}function a(){const e=i("camera");return{name:"unsupported",message:"insecure-context"===e?s("getUserMedia","camera",e):"getUserMedia is not available in this browser."}}function o(e){if(e)for(const t of e.getTracks())t.stop()}async function c(){if(!r()||"function"!=typeof navigator.mediaDevices.enumerateDevices)return[];try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>"videoinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label,groupId:e.groupId,kind:e.kind}))}catch{return[]}}class d{_name;_onChange;_status=null;_gen=0;_subscribed=!1;constructor(e,t){this._name=e,this._onChange=t}observe(){if(this._subscribed)return Promise.resolve();if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return this._onChange("unsupported"),Promise.resolve();this._subscribed=!0;const e=++this._gen;return navigator.permissions.query({name:this._name}).then(t=>{e===this._gen&&(this._status=t,this._onChange(t.state),t.addEventListener("change",this._onStatusChange))},()=>{e===this._gen&&this._onChange("unsupported")})}dispose(){this._subscribed=!1,this._gen++,this._status&&(this._status.removeEventListener("change",this._onStatusChange),this._status=null)}_onStatusChange=e=>{const t=e.target;this._onChange(t.state)}}const h=new WeakMap;function u(e){const t=(h.get(e)??0)-1;t>0?h.set(e,t):h.delete(e)}const l="capability-missing",m="not-allowed",_="not-found",g="not-readable",p="invalid-argument",v="invalid-state",b="aborted",f="media-error";function w(e){const{name:t,message:i}=e;return"unsupported"===t?{code:l,phase:"probe",recoverable:!1,message:i}:"NotAllowedError"===t||"SecurityError"===t?{code:m,phase:"start",recoverable:!1,message:i}:"NotFoundError"===t?{code:_,phase:"start",recoverable:!1,message:i}:"NotReadableError"===t?{code:g,phase:"start",recoverable:!1,message:i}:"OverconstrainedError"===t||"NotSupportedError"===t?{code:p,phase:"start",recoverable:!1,message:i}:"NoStreamError"===t?{code:v,phase:"start",recoverable:!1,message:i}:"AbortError"===t?{code:b,phase:"execute",recoverable:!0,message:i}:{code:f,phase:"execute",recoverable:!1,message:i}}class y extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-camera:active-changed",semantics:"state"},{name:"permission",event:"wcs-camera:permission-changed",semantics:"state"},{name:"audioPermission",event:"wcs-camera:audio-permission-changed",semantics:"state"},{name:"deviceId",event:"wcs-camera:device-changed",semantics:"state"},{name:"devices",event:"wcs-camera:devices-changed",semantics:"state"},{name:"error",event:"wcs-camera:error",semantics:"state"},{name:"errorInfo",event:"wcs-camera:error-info-changed",semantics:"state"},{name:"streamReady",event:"wcs-camera:stream-ready",semantics:"handle",getter:e=>e.detail},{name:"ended",event:"wcs-camera:ended",semantics:"event",getter:e=>e.detail}],commands:[{name:"start"},{name:"stop"},{name:"switchCamera"}]};_target;_active=!1;_permission="prompt";_audioPermission=null;_deviceId=null;_devices=[];_error=null;_errorInfo=null;_stream=null;_desired=!1;_constraints={};_gen=0;_subscribed=!1;_camWatcher=null;_micWatcher=null;_ready=Promise.resolve();_toldKeepAlive=!1;constructor(e){super(),this._target=e??this}get active(){return this._active}get permission(){return this._permission}get audioPermission(){return this._audioPermission}get deviceId(){return this._deviceId}get devices(){return this._devices}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setActive(e){this._active!==e&&(this._active=e,this._dispatch("wcs-camera:active-changed",e))}_setPermission(e){this._permission!==e&&(this._permission=e,this._dispatch("wcs-camera:permission-changed",e))}_setAudioPermission(e){this._audioPermission!==e&&(this._audioPermission=e,this._dispatch("wcs-camera:audio-permission-changed",e))}_setDeviceId(e){this._deviceId!==e&&(this._deviceId=e,this._dispatch("wcs-camera:device-changed",e))}_setDevices(e){this._devicesEqual(this._devices,e)||(this._devices=e,this._dispatch("wcs-camera:devices-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:w(e)),this._dispatch("wcs-camera:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-camera:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}_devicesEqual(e,t){if(e.length!==t.length)return!1;for(let i=0;i<e.length;i++)if(e[i].deviceId!==t[i].deviceId||e[i].label!==t[i].label)return!1;return!0}observe(e){return this._constraints={...e},this._subscribed?this._ready=this._ready.then(()=>this._reconcileAudioWatcher()):(this._subscribed=!0,this._ready=this._initPermissions()),this._ready}start(){this._desired=!0,this._restart()}stop(){this._desired=!1,this._release()}switchCamera(){const e="environment"===this._constraints.facingMode?"user":"environment";this._constraints={...this._constraints,facingMode:e,deviceId:void 0},this._desired&&this._restart()}suspend(){var e;this._gen++,this._stream&&(e=this._stream,h.has(e)&&this._warnKeepAlive(),this._release())}_warnKeepAlive(){this._toldKeepAlive||(this._toldKeepAlive=!0,console.warn("[@wcstack/camera] [wcs/camera-keep-alive-missing] The page was hidden while this camera's stream was being recorded, so the camera stopped the stream and the recording ended with it. Keep the camera on while recording: bind keepAlive to the recording flag (keepAlive: <recording path>). (Said once per element.)",this._target))}resume(){this._desired&&!this._active&&this._restart()}dispose(){this._subscribed=!1,this._gen++,this._desired=!1,this._release(),r()&&"function"==typeof navigator.mediaDevices.removeEventListener&&navigator.mediaDevices.removeEventListener("devicechange",this._onDeviceChange),this._camWatcher?.dispose(),this._micWatcher?.dispose(),this._camWatcher=null,this._micWatcher=null}_initPermissions(){if(!r())return this._setPermission("unsupported"),Promise.resolve();const e=[];return null!==i("camera")?this._setPermission("unsupported"):(this._camWatcher=new d("camera",e=>this._setPermission(e)),e.push(this._camWatcher.observe())),this._constraints.audio?e.push(this._watchMicrophone()):this._setAudioPermission(null),"function"==typeof navigator.mediaDevices.addEventListener&&navigator.mediaDevices.addEventListener("devicechange",this._onDeviceChange),Promise.all(e).then(()=>{})}_onDeviceChange=()=>{c().then(e=>{this._subscribed&&this._setDevices(e)})};_reconcileAudioWatcher(){return this._constraints.audio&&!this._micWatcher&&r()?this._watchMicrophone():(this._constraints.audio||!this._micWatcher&&null===this._audioPermission||(this._micWatcher?.dispose(),this._micWatcher=null,this._setAudioPermission(null)),Promise.resolve())}_watchMicrophone(){return null!==i("microphone")?(this._setAudioPermission("unsupported"),Promise.resolve()):(this._micWatcher=new d("microphone",e=>this._setAudioPermission(e)),this._micWatcher.observe())}_restart(){this._release(),this._acquire()}async _acquire(){const e=++this._gen,t=r()?function(e){const t=i("camera");if(null!==t)return{device:"camera",error:{name:"unsupported",message:s("The camera","camera",t)}};const r=e?i("microphone"):null;return null!==r?{device:"microphone",error:{name:"unsupported",message:s("The microphone","microphone",r)}}:null}(!!this._constraints.audio):null;if(null!==t)return this._setError(t.error),"camera"===t.device?this._setPermission("unsupported"):this._setAudioPermission("unsupported"),this._desired=!1,void this._setActive(!1);const d=function(e){const t={};return e.deviceId?t.deviceId={exact:e.deviceId}:e.facingMode&&(t.facingMode=e.facingMode),"number"==typeof e.width&&(t.width=e.width),"number"==typeof e.height&&(t.height=e.height),{video:!(Object.keys(t).length>0)||t,audio:!0===e.audio}}(this._constraints),{stream:h,error:u}=await async function(e){if(!r())return{error:a()};try{return{stream:await navigator.mediaDevices.getUserMedia(e)}}catch(e){return{error:n(e)}}}(d);if(e!==this._gen)return void o(h??null);if(u)return this._setError(u),"NotAllowedError"===u.name?(this._setPermission("denied"),this._desired=!1):"unsupported"===u.name&&(this._setPermission("unsupported"),this._desired=!1),void this._setActive(!1);const l=h;this._stream=l;for(const e of l.getTracks())e.addEventListener("ended",this._onTrackEnded);this._setError(null),this._setPermission("granted"),this._constraints.audio&&l.getAudioTracks().length>0&&this._setAudioPermission("granted"),this._updateDeviceId(l),this._setActive(!0),this._dispatch("wcs-camera:stream-ready",l);const m=await c();e===this._gen&&this._setDevices(m)}_release(){if(this._stream){for(const e of this._stream.getTracks())e.removeEventListener("ended",this._onTrackEnded);o(this._stream),this._stream=null,this._setActive(!1)}}_onTrackEnded=()=>{this._release(),this._dispatch("wcs-camera:ended",null)};_updateDeviceId(e){const t=e.getVideoTracks()[0];if(t&&"function"==typeof t.getSettings){const e=t.getSettings();this._setDeviceId(e.deviceId??null)}}}function P(e,t){let i=Object.getPrototypeOf(e);for(;null!==i;){const e=Object.getOwnPropertyDescriptor(i,t);if(void 0!==e)return"function"==typeof e.get||"function"==typeof e.set;i=Object.getPrototypeOf(i)}return!1}const A=new WeakSet;function S(e,t,i){if(A.has(e))return;A.add(e);const s=e.localName??"element";console.warn(`[wcs/input-as-attribute] <${s}> has the attribute ${t}="${i}": an object was written as an attribute, so the element did not receive it. A framework does that when the property is set before the element's definition loads (React 19), or when told to (Vue's .attr). Load the definition before rendering, or assign the property through a ref (Vue: .prop). (Said once per element.)`,e)}function E(e){const t=e.constructor?.wcBindable,i=t?.inputs;if(void 0===i)return;const s=e.getAttribute;for(const t of i){const i=t.name,r="function"==typeof s?s.call(e,i.toLowerCase()):null;if(null!==r&&r.startsWith("[object ")&&S(e,i,r),!Object.prototype.hasOwnProperty.call(e,i))continue;if(!P(e,i))continue;const n=e,a=n[i];delete n[i],n[i]=a}}const T=new WeakMap;function k(e,t){let i=T.get(e);return void 0===i&&(i=new Map,T.set(e,i)),i.has(t)||i.set(t,e.getAttribute(t)),i.get(t)}function I(e,t,i){null===i?e.removeAttribute(t):e.setAttribute(t,i)}function C(e,t,i){const s=k(e,t);I(e,t,void 0===i?s:null===i?null:String(i))}function R(e,t,i){const s=k(e,t);I(e,t,void 0===i?s:i?"":null)}const L=":host{display:inline-block}video{display:block;width:100%;height:100%}";let N;class M extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...y.wcBindable,inputs:[{name:"audio",attribute:"audio"},{name:"facingMode",attribute:"facing-mode"},{name:"deviceId",attribute:"device-id"},{name:"width",attribute:"width"},{name:"height",attribute:"height"},{name:"autostart",attribute:"autostart"},{name:"keepAlive",attribute:"keep-alive"}],commands:y.wcBindable.commands};static observedAttributes=["facing-mode","device-id","audio","width","height"];_core;_video;_connectedCallbackPromise=Promise.resolve();_connectResolve=null;_acquirePending=!1;_autostartCancelled=!1;_connected=!1;_batchingAttrs=!1;_internals=null;constructor(){super(),this._core=new y(this);const e=this.attachShadow({mode:"open"});!function(e){if(void 0===N)try{N=new CSSStyleSheet,N.replaceSync(L)}catch{N=null}if(null!==N&&Array.isArray(e.adoptedStyleSheets))return void(e.adoptedStyleSheets=[N]);const t=document.createElement("style");t.textContent=L,e.append(t)}(e),this._video=document.createElement("video"),this._video.autoplay=!0,this._video.muted=!0,this._video.setAttribute("playsinline",""),this._video.setAttribute("part","video"),e.append(this._video),this.addEventListener("wcs-camera:stream-ready",this._onStreamReady),this.addEventListener("wcs-camera:active-changed",this._onActiveChanged),this._internals=this._initInternals(),this._wireStates({"wcs-camera:active-changed":e=>({active:!0===e}),"wcs-camera:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get audio(){return this.hasAttribute("audio")}set audio(e){R(this,"audio",e)}get facingMode(){return"environment"===this.getAttribute("facing-mode")?"environment":"user"}set facingMode(e){C(this,"facing-mode",e)}get deviceId(){return this._core.deviceId??this.getAttribute("device-id")??""}set deviceId(e){C(this,"device-id",e)}get width(){return this._numberAttr("width")}set width(e){C(this,"width",e)}get height(){return this._numberAttr("height")}set height(e){C(this,"height",e)}get autostart(){return this.hasAttribute("autostart")}set autostart(e){R(this,"autostart",e)}get keepAlive(){return this.hasAttribute("keep-alive")}set keepAlive(e){R(this,"keep-alive",e)}get videoElement(){return this._video}get active(){return this._core.active}get permission(){return this._core.permission}get audioPermission(){return this._core.audioPermission}get devices(){return this._core.devices}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}get connectedCallbackPromise(){return this._connectedCallbackPromise}start(){this._connected&&this._core.observe(this._constraints()),this._core.start()}stop(){null!==this._connectResolve&&(this._autostartCancelled=!0),this._core.stop()}switchCamera(){const e="environment"===this.facingMode?"user":"environment";this._batchingAttrs=!0;try{this.removeAttribute("device-id"),this.setAttribute("facing-mode",e)}finally{this._batchingAttrs=!1}this._core.observe(this._constraints()),this._core.active&&this._core.start()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_constraints(){const e={audio:this.audio,facingMode:this.facingMode},t=this.getAttribute("device-id");return t&&(e.deviceId=t),Number.isFinite(this.width)&&(e.width=this.width),Number.isFinite(this.height)&&(e.height=this.height),e}_onStreamReady=e=>{this._video.srcObject=e.detail};_onActiveChanged=e=>{!1===e.detail&&(this._video.srcObject=null)};_scheduleAcquire(){this._acquirePending||(this._acquirePending=!0,queueMicrotask(()=>{this._acquirePending=!1;const e=this._connectResolve;if(this._connectResolve=null,!this._connected)return;const t=this._core.observe(this._constraints());null!==e?(this.autostart&&!this._autostartCancelled&&this._core.start(),this._autostartCancelled=!1,e(t)):this._core.active&&this._core.start()}))}connectedCallback(){E(this),this._connected=!0,this._connectedCallbackPromise=new Promise(e=>{this._connectResolve=e}),this._scheduleAcquire(),document.addEventListener("visibilitychange",this._onVisibilityChange)}disconnectedCallback(){this._connected=!1,this._connectResolve?.(),this._connectResolve=null,this._autostartCancelled=!1,document.removeEventListener("visibilitychange",this._onVisibilityChange),this._core.dispose()}attributeChangedCallback(e,t,i){this._connected&&t!==i&&(this._batchingAttrs||this._scheduleAcquire())}_onVisibilityChange=()=>{this.keepAlive||("hidden"===document.visibilityState?this._core.suspend():this._core.resume())}}class B extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"recording",event:"wcs-recorder:recording-changed",semantics:"state"},{name:"paused",event:"wcs-recorder:paused-changed",semantics:"state"},{name:"duration",event:"wcs-recorder:duration-changed",semantics:"state"},{name:"mimeType",event:"wcs-recorder:mimetype-changed",semantics:"state"},{name:"blob",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.blob??null},{name:"objectURL",event:"wcs-recorder:recorded",semantics:"state",getter:e=>e.detail?.objectURL??null},{name:"error",event:"wcs-recorder:error",semantics:"state"},{name:"errorInfo",event:"wcs-recorder:error-info-changed",semantics:"state"},{name:"recorded",event:"wcs-recorder:recorded",semantics:"event",getter:e=>e.detail},{name:"dataavailable",event:"wcs-recorder:dataavailable",semantics:"event",getter:e=>e.detail}],commands:[{name:"attachStream"},{name:"start"},{name:"stop"},{name:"pause"},{name:"resume"}]};_target;_recording=!1;_paused=!1;_duration=0;_mimeType="";_blob=null;_objectURL=null;_error=null;_errorInfo=null;_recorder=null;_stream=null;_recordingStream=null;_timeslice=!1;_startTime=0;_gen=0;_closing=null;_ready=Promise.resolve();_toldNotReady=!1;constructor(e){super(),this._target=e??this}get recording(){return this._recording}get paused(){return this._paused}get duration(){return this._duration}get mimeType(){return this._mimeType}get blob(){return this._blob}get objectURL(){return this._objectURL}get error(){return this._error}get errorInfo(){return this._errorInfo}get ready(){return this._ready}_setRecording(e){this._recording!==e&&(this._recording=e,this._dispatch("wcs-recorder:recording-changed",e))}_setPaused(e){this._paused!==e&&(this._paused=e,this._dispatch("wcs-recorder:paused-changed",e))}_setDuration(e){this._duration!==e&&(this._duration=e,this._dispatch("wcs-recorder:duration-changed",e))}_setMimeType(e){this._mimeType!==e&&(this._mimeType=e,this._dispatch("wcs-recorder:mimetype-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._commitErrorInfo(null===e?null:w(e)),this._dispatch("wcs-recorder:error",e))}_commitErrorInfo(e){this._errorInfo=e,this._dispatch("wcs-recorder:error-info-changed",e)}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}attachStream(e){this._stream=e,e&&(this._toldNotReady=!1)}start(e={}){if("undefined"==typeof globalThis||"function"!=typeof globalThis.MediaRecorder)return void this._setError({name:"unsupported",message:"MediaRecorder is not available in this environment."});if(!this._stream)return this._setError({name:"NoStreamError",message:"No stream attached. Wire a camera's stream-ready to attachStream first."}),void this._warnNotReady();if(this._recording)return;const t={};let i;e.mimeType&&this._isTypeSupported(e.mimeType)&&(t.mimeType=e.mimeType),"number"==typeof e.audioBitsPerSecond&&(t.audioBitsPerSecond=e.audioBitsPerSecond),"number"==typeof e.videoBitsPerSecond&&(t.videoBitsPerSecond=e.videoBitsPerSecond);try{i=new MediaRecorder(this._stream,t)}catch(e){return void this._setError(n(e))}const s=++this._gen,r=[],a=this._stream;this._timeslice="number"==typeof e.timeslice&&e.timeslice>0;const o=()=>null!==this._closing&&this._closing.gen===s;i.ondataavailable=e=>{if((s===this._gen||o())&&e.data&&e.data.size>0){if(r.push(e.data),s!==this._gen)return;!this._mimeType&&e.data.type&&this._setMimeType(e.data.type),this._timeslice&&this._dispatch("wcs-recorder:dataavailable",e.data)}},i.onstart=()=>{s===this._gen&&i.mimeType&&this._setMimeType(i.mimeType)},i.onstop=()=>{if(u(a),this._recordingStream===a&&(this._recordingStream=null),o()){const{mimeType:e,duration:t}=this._closing;return this._closing=null,void this._assembleBlob(r,e,t)}s===this._gen&&(this._paused||this._setDuration(this._elapsed()),this._assembleBlob(r,this._mimeType,this._duration),this._setPaused(!1),this._setRecording(!1))},i.onerror=e=>{if(s!==this._gen)return;const t=e.error;this._setError(n(t??{name:"RecorderError"}))},i.onpause=()=>{s===this._gen&&(this._setDuration(this._elapsed()),this._setPaused(!0))},i.onresume=()=>{s===this._gen&&(this._startTime=this._now()-this._duration,this._setPaused(!1))},this._recorder=i,this._setError(null),this._setMimeType(i.mimeType||t.mimeType||""),this._startTime=this._now(),this._setDuration(0),i.start(this._timeslice?e.timeslice:void 0),function(e){h.set(e,(h.get(e)??0)+1)}(a),this._recordingStream=a,this._setRecording(!0)}_warnNotReady(){this._toldNotReady||(this._toldNotReady=!0,console.warn("[@wcstack/camera] [wcs/command-not-ready] start() was refused: no stream is attached, so nothing is recorded. It goes to `error` too. Wire a camera's stream to the recorder first. (Said once until a stream is attached again.)",this._target))}stop(){this._recorder&&"inactive"!==this._recorder.state&&this._recorder.stop()}pause(){this._recorder&&"recording"===this._recorder.state&&this._recorder.pause()}resume(){this._recorder&&"paused"===this._recorder.state&&this._recorder.resume()}observe(){return this._ready}dispose(){const e=this._recorder,t=null!==e&&"inactive"!==e.state;if(t&&(this._paused||this._setDuration(this._elapsed()),this._closing={gen:this._gen,mimeType:this._mimeType,duration:this._duration}),this._gen++,this._recorder=null,this._revokeUrl(),this._stream=null,this._setPaused(!1),this._setRecording(!1),t)try{e.stop()}catch{this._closing=null,this._recordingStream&&u(this._recordingStream),this._recordingStream=null}}_assembleBlob(e,t,i){const s=t||e[0]?.type||"",r=new Blob(e,s?{type:s}:void 0);this._revokeUrl();const n=this._createUrl(r);this._blob=r,this._objectURL=n;const a={blob:r,objectURL:n,mimeType:s,duration:i};this._dispatch("wcs-recorder:recorded",a)}_revokeUrl(){this._objectURL&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(this._objectURL),this._objectURL=null}_createUrl(e){return"undefined"!=typeof URL&&"function"==typeof URL.createObjectURL?URL.createObjectURL(e):""}_isTypeSupported(e){return globalThis.MediaRecorder.isTypeSupported(e)}_now(){return performance.now()}_elapsed(){return Math.max(0,Math.round(this._now()-this._startTime))}}class U extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...B.wcBindable,inputs:[{name:"mimeType",attribute:"mime-type"},{name:"timeslice",attribute:"timeslice"},{name:"audioBitsPerSecond",attribute:"audio-bits"},{name:"videoBitsPerSecond",attribute:"video-bits"}],commands:B.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new B(this),this._internals=this._initInternals(),this._wireStates({"wcs-recorder:recording-changed":e=>({recording:!0===e}),"wcs-recorder:paused-changed":e=>({paused:!0===e}),"wcs-recorder:error":e=>({error:null!=e})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const e=this.attachInternals();return e.states.add("wcs-probe"),e.states.delete("wcs-probe"),e}catch{return null}}_wireStates(e){if(null===this._internals)return;const t=this._internals.states;for(const[i,s]of Object.entries(e))this.addEventListener(i,e=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(e.detail))){try{n?t.add(r):t.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get connectedCallbackPromise(){return this._connectedCallbackPromise}get mimeType(){return this._core.mimeType}set mimeType(e){C(this,"mime-type",e)}get timeslice(){return this._numberAttr("timeslice")}set timeslice(e){C(this,"timeslice",e)}get audioBitsPerSecond(){return this._numberAttr("audio-bits")}set audioBitsPerSecond(e){C(this,"audio-bits",e)}get videoBitsPerSecond(){return this._numberAttr("video-bits")}set videoBitsPerSecond(e){C(this,"video-bits",e)}get recording(){return this._core.recording}get paused(){return this._core.paused}get duration(){return this._core.duration}get blob(){return this._core.blob}get objectURL(){return this._core.objectURL}get error(){return this._core.error}get errorInfo(){return this._core.errorInfo}attachStream(e){this._core.attachStream(e)}start(){this._core.start(this._options())}stop(){this._core.stop()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_options(){const e={},t=this.getAttribute("mime-type")??"";return t&&(e.mimeType=t),Number.isFinite(this.timeslice)&&(e.timeslice=this.timeslice),Number.isFinite(this.audioBitsPerSecond)&&(e.audioBitsPerSecond=this.audioBitsPerSecond),Number.isFinite(this.videoBitsPerSecond)&&(e.videoBitsPerSecond=this.videoBitsPerSecond),e}connectedCallback(){E(this),this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}var j;!function(t=customElements){t.get(e.tagNames.camera)||t.define(e.tagNames.camera,M),t.get(e.tagNames.recorder)||t.define(e.tagNames.recorder,U)}(j);
2
2
  //# sourceMappingURL=auto.min.js.map